Provide a separate class for representing metadata version in kotlinx-metadata

Separate class allows encapsulating comparison, equality, and structure of version parts.
It also makes more sense from API standpoint than plain `IntArray`.
This commit is contained in:
Leonid Startsev
2023-11-08 12:56:05 +01:00
committed by Space Team
parent f101e5a336
commit 721af75f40
16 changed files with 323 additions and 89 deletions
@@ -1310,6 +1310,24 @@ public final class kotlinx/metadata/jvm/JvmMetadataUtil {
public static final fun toJvmInternalName (Ljava/lang/String;)Ljava/lang/String; public static final fun toJvmInternalName (Ljava/lang/String;)Ljava/lang/String;
} }
public final class kotlinx/metadata/jvm/JvmMetadataVersion : java/lang/Comparable {
public static final field CURRENT Lkotlinx/metadata/jvm/JvmMetadataVersion;
public static final field Companion Lkotlinx/metadata/jvm/JvmMetadataVersion$Companion;
public fun <init> (II)V
public fun <init> (III)V
public synthetic fun compareTo (Ljava/lang/Object;)I
public fun compareTo (Lkotlinx/metadata/jvm/JvmMetadataVersion;)I
public fun equals (Ljava/lang/Object;)Z
public final fun getMajor ()I
public final fun getMinor ()I
public final fun getPatch ()I
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
public final class kotlinx/metadata/jvm/JvmMetadataVersion$Companion {
}
public final class kotlinx/metadata/jvm/JvmMethodSignature : kotlinx/metadata/jvm/JvmMemberSignature { public final class kotlinx/metadata/jvm/JvmMethodSignature : kotlinx/metadata/jvm/JvmMemberSignature {
public fun <init> (Ljava/lang/String;Ljava/lang/String;)V public fun <init> (Ljava/lang/String;Ljava/lang/String;)V
public final fun component1 ()Ljava/lang/String; public final fun component1 ()Ljava/lang/String;
@@ -1441,12 +1459,12 @@ 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 abstract fun getFlags ()I public abstract fun getFlags ()I
public abstract fun getVersion ()[I public abstract fun getVersion ()Lkotlinx/metadata/jvm/JvmMetadataVersion;
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 readLenient (Lkotlin/Metadata;)Lkotlinx/metadata/jvm/KotlinClassMetadata; public static final fun readLenient (Lkotlin/Metadata;)Lkotlinx/metadata/jvm/KotlinClassMetadata;
public static final fun readStrict (Lkotlin/Metadata;)Lkotlinx/metadata/jvm/KotlinClassMetadata; public static final fun readStrict (Lkotlin/Metadata;)Lkotlinx/metadata/jvm/KotlinClassMetadata;
public abstract fun setFlags (I)V public abstract fun setFlags (I)V
public abstract fun setVersion ([I)V public abstract fun setVersion (Lkotlinx/metadata/jvm/JvmMetadataVersion;)V
public abstract fun write ()Lkotlin/Metadata; public abstract fun write ()Lkotlin/Metadata;
public static final fun writeClass (Lkotlinx/metadata/KmClass;)Lkotlin/Metadata; public static final fun writeClass (Lkotlinx/metadata/KmClass;)Lkotlin/Metadata;
public static final fun writeClass (Lkotlinx/metadata/KmClass;[I)Lkotlin/Metadata; public static final fun writeClass (Lkotlinx/metadata/KmClass;[I)Lkotlin/Metadata;
@@ -1469,14 +1487,14 @@ public abstract class kotlinx/metadata/jvm/KotlinClassMetadata {
} }
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 fun <init> (Lkotlinx/metadata/KmClass;[II)V public fun <init> (Lkotlinx/metadata/KmClass;Lkotlinx/metadata/jvm/JvmMetadataVersion;I)V
public final fun accept (Lkotlinx/metadata/KmClassVisitor;)V public final fun accept (Lkotlinx/metadata/KmClassVisitor;)V
public fun getFlags ()I public fun getFlags ()I
public final fun getKmClass ()Lkotlinx/metadata/KmClass; public final fun getKmClass ()Lkotlinx/metadata/KmClass;
public fun getVersion ()[I public fun getVersion ()Lkotlinx/metadata/jvm/JvmMetadataVersion;
public fun setFlags (I)V public fun setFlags (I)V
public final fun setKmClass (Lkotlinx/metadata/KmClass;)V public final fun setKmClass (Lkotlinx/metadata/KmClass;)V
public fun setVersion ([I)V public fun setVersion (Lkotlinx/metadata/jvm/JvmMetadataVersion;)V
public final fun toKmClass ()Lkotlinx/metadata/KmClass; public final fun toKmClass ()Lkotlinx/metadata/KmClass;
public fun write ()Lkotlin/Metadata; public fun write ()Lkotlin/Metadata;
} }
@@ -1521,14 +1539,14 @@ public final class kotlinx/metadata/jvm/KotlinClassMetadata$Companion {
} }
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 fun <init> (Lkotlinx/metadata/KmPackage;[II)V public fun <init> (Lkotlinx/metadata/KmPackage;Lkotlinx/metadata/jvm/JvmMetadataVersion;I)V
public final fun accept (Lkotlinx/metadata/KmPackageVisitor;)V public final fun accept (Lkotlinx/metadata/KmPackageVisitor;)V
public fun getFlags ()I public fun getFlags ()I
public final fun getKmPackage ()Lkotlinx/metadata/KmPackage; public final fun getKmPackage ()Lkotlinx/metadata/KmPackage;
public fun getVersion ()[I public fun getVersion ()Lkotlinx/metadata/jvm/JvmMetadataVersion;
public fun setFlags (I)V public fun setFlags (I)V
public final fun setKmPackage (Lkotlinx/metadata/KmPackage;)V public final fun setKmPackage (Lkotlinx/metadata/KmPackage;)V
public fun setVersion ([I)V public fun setVersion (Lkotlinx/metadata/jvm/JvmMetadataVersion;)V
public final fun toKmPackage ()Lkotlinx/metadata/KmPackage; public final fun toKmPackage ()Lkotlinx/metadata/KmPackage;
public fun write ()Lkotlin/Metadata; public fun write ()Lkotlin/Metadata;
} }
@@ -1542,13 +1560,13 @@ public final class kotlinx/metadata/jvm/KotlinClassMetadata$FileFacade$Writer :
} }
public final class kotlinx/metadata/jvm/KotlinClassMetadata$MultiFileClassFacade : kotlinx/metadata/jvm/KotlinClassMetadata { public final class kotlinx/metadata/jvm/KotlinClassMetadata$MultiFileClassFacade : kotlinx/metadata/jvm/KotlinClassMetadata {
public fun <init> (Ljava/util/List;[II)V public fun <init> (Ljava/util/List;Lkotlinx/metadata/jvm/JvmMetadataVersion;I)V
public fun getFlags ()I public fun getFlags ()I
public final fun getPartClassNames ()Ljava/util/List; public final fun getPartClassNames ()Ljava/util/List;
public fun getVersion ()[I public fun getVersion ()Lkotlinx/metadata/jvm/JvmMetadataVersion;
public fun setFlags (I)V public fun setFlags (I)V
public final fun setPartClassNames (Ljava/util/List;)V public final fun setPartClassNames (Ljava/util/List;)V
public fun setVersion ([I)V public fun setVersion (Lkotlinx/metadata/jvm/JvmMetadataVersion;)V
public fun write ()Lkotlin/Metadata; public fun write ()Lkotlin/Metadata;
} }
@@ -1561,16 +1579,16 @@ 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 fun <init> (Lkotlinx/metadata/KmPackage;Ljava/lang/String;[II)V public fun <init> (Lkotlinx/metadata/KmPackage;Ljava/lang/String;Lkotlinx/metadata/jvm/JvmMetadataVersion;I)V
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 fun getFlags ()I public fun getFlags ()I
public final fun getKmPackage ()Lkotlinx/metadata/KmPackage; public final fun getKmPackage ()Lkotlinx/metadata/KmPackage;
public fun getVersion ()[I public fun getVersion ()Lkotlinx/metadata/jvm/JvmMetadataVersion;
public final fun setFacadeClassName (Ljava/lang/String;)V public final fun setFacadeClassName (Ljava/lang/String;)V
public fun setFlags (I)V public fun setFlags (I)V
public final fun setKmPackage (Lkotlinx/metadata/KmPackage;)V public final fun setKmPackage (Lkotlinx/metadata/KmPackage;)V
public fun setVersion ([I)V public fun setVersion (Lkotlinx/metadata/jvm/JvmMetadataVersion;)V
public final fun toKmPackage ()Lkotlinx/metadata/KmPackage; public final fun toKmPackage ()Lkotlinx/metadata/KmPackage;
public fun write ()Lkotlin/Metadata; public fun write ()Lkotlin/Metadata;
} }
@@ -1584,15 +1602,15 @@ 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 fun <init> (Lkotlinx/metadata/KmLambda;[II)V public fun <init> (Lkotlinx/metadata/KmLambda;Lkotlinx/metadata/jvm/JvmMetadataVersion;I)V
public final fun accept (Lkotlinx/metadata/KmLambdaVisitor;)V public final fun accept (Lkotlinx/metadata/KmLambdaVisitor;)V
public fun getFlags ()I public fun getFlags ()I
public final fun getKmLambda ()Lkotlinx/metadata/KmLambda; public final fun getKmLambda ()Lkotlinx/metadata/KmLambda;
public fun getVersion ()[I public fun getVersion ()Lkotlinx/metadata/jvm/JvmMetadataVersion;
public final fun isLambda ()Z public final fun isLambda ()Z
public fun setFlags (I)V public fun setFlags (I)V
public final fun setKmLambda (Lkotlinx/metadata/KmLambda;)V public final fun setKmLambda (Lkotlinx/metadata/KmLambda;)V
public fun setVersion ([I)V public fun setVersion (Lkotlinx/metadata/jvm/JvmMetadataVersion;)V
public final fun toKmLambda ()Lkotlinx/metadata/KmLambda; public final fun toKmLambda ()Lkotlinx/metadata/KmLambda;
public fun write ()Lkotlin/Metadata; public fun write ()Lkotlin/Metadata;
} }
@@ -1609,9 +1627,9 @@ public final class kotlinx/metadata/jvm/KotlinClassMetadata$Unknown : kotlinx/me
public static final field Companion Lkotlinx/metadata/jvm/KotlinClassMetadata$Unknown$Companion; public static final field Companion Lkotlinx/metadata/jvm/KotlinClassMetadata$Unknown$Companion;
public static final field INSTANCE Lkotlinx/metadata/jvm/KotlinClassMetadata$Unknown; public static final field INSTANCE Lkotlinx/metadata/jvm/KotlinClassMetadata$Unknown;
public fun getFlags ()I public fun getFlags ()I
public fun getVersion ()[I public fun getVersion ()Lkotlinx/metadata/jvm/JvmMetadataVersion;
public fun setFlags (I)V public fun setFlags (I)V
public fun setVersion ([I)V public fun setVersion (Lkotlinx/metadata/jvm/JvmMetadataVersion;)V
public fun write ()Lkotlin/Metadata; public fun write ()Lkotlin/Metadata;
} }
@@ -1626,14 +1644,14 @@ public final class kotlinx/metadata/jvm/KotlinModuleMetadata {
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;)[B public static final fun write (Lkotlinx/metadata/jvm/KmModule;)[B
public static final fun write (Lkotlinx/metadata/jvm/KmModule;[I)[B public static final fun write (Lkotlinx/metadata/jvm/KmModule;Lkotlinx/metadata/jvm/JvmMetadataVersion;)[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;)[B public final fun write (Lkotlinx/metadata/jvm/KmModule;)[B
public final fun write (Lkotlinx/metadata/jvm/KmModule;[I)[B public final fun write (Lkotlinx/metadata/jvm/KmModule;Lkotlinx/metadata/jvm/JvmMetadataVersion;)[B
public static synthetic fun write$default (Lkotlinx/metadata/jvm/KotlinModuleMetadata$Companion;Lkotlinx/metadata/jvm/KmModule;[IILjava/lang/Object;)[B public static synthetic fun write$default (Lkotlinx/metadata/jvm/KotlinModuleMetadata$Companion;Lkotlinx/metadata/jvm/KmModule;Lkotlinx/metadata/jvm/JvmMetadataVersion;ILjava/lang/Object;)[B
} }
public final class kotlinx/metadata/jvm/KotlinModuleMetadata$Writer { public final class kotlinx/metadata/jvm/KotlinModuleMetadata$Writer {
@@ -0,0 +1,116 @@
/*
* Copyright 2010-2023 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 org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion as CompilerMetadataVersion
/**
* Version of the metadata inside JVM classfile.
*
* Starting from Kotlin 1.4, the metadata version is equal to the language version.
* It consists of major and minor versions. Patch version usually does not affect the metadata format, but it is present for completeness because
* compiler writes its full version to the `@Metadata` annotation; therefore, the patch version is accounted for in comparisons and equality.
* All components (major, minor, and patch) should be non-negative.
*
* While the metadata version is currently equal to the language version, this may be changed in the future, including adding or removing components.
* Therefore, it is recommended to rely on the [compareTo] and [equals] method instead of accessing components directly.
*
* Note that the metadata version is 1.1 for Kotlin compilers from 1.0 until 1.4, and is 1.0 or less for pre-1.0 compilers.
* Metadata with versions less than 1.1 is considered incompatible and cannot be read or written.
*
* The library can read in strict mode only compatible versions of metadata. For definition of compatible version, see documentation for [CURRENT] property.
*
* @property major Major component of version
* @property minor Minor component of version
* @property patch Patch component of version
*/
public class JvmMetadataVersion(public val major: Int, public val minor: Int, public val patch: Int) : Comparable<JvmMetadataVersion> {
/**
* Creates a new [JvmMetadataVersion] instance with specified [major] and [minor] versions and a patch version equal to 0.
*/
public constructor(major: Int, minor: Int) : this(major, minor, 0)
internal constructor(intArray: IntArray) : this(intArray[0], intArray[1], intArray[2])
@JvmName("toIntArray")
internal fun toIntArray(): IntArray = intArrayOf(major, minor, patch)
init {
require(major >= 0) { "Major version should be not less than 0" }
require(minor >= 0) { "Minor version should be not less than 0" }
require(patch >= 0) { "Patch version should be not less than 0" }
}
/**
* Compares this JvmMetadataVersion object with another JvmMetadataVersion object.
*
* Comparison is based on integer values of version parts with [major] being most significant one, then [minor], and finally [patch].
*
* @return a negative integer, zero, or a positive integer if this JvmMetadataVersion object is less than, equal to, or greater than [other].
*/
override fun compareTo(other: JvmMetadataVersion): Int {
val majors = major.compareTo(other.major)
if (majors != 0) return majors
val minors = minor.compareTo(other.minor)
return if (minors != 0) minors else patch.compareTo(other.patch)
}
/**
* Returns a string representation of the version number.
* The string representation is in the format "major.minor.patch".
*/
override fun toString(): String = "$major.$minor.$patch"
/**
* Calculates the hash code value for the object based on major, minor, and patch components.
*/
override fun hashCode(): Int {
var result = major
result = 31 * result + minor
result = 31 * result + patch
return result
}
/**
* Checks if the current JvmMetadataVersion object is equal to another object.
*
* Instances of JvmMetadataVersion are equal if they have the same major, minor, and patch components.
*/
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as JvmMetadataVersion
if (major != other.major) return false
if (minor != other.minor) return false
if (patch != other.patch) return false
return true
}
/**
* Companion object to hold pre-defined JvmMetadataVersion instances.
*/
public companion object {
/**
* The latest stable metadata version supported by this version of the library.
* The library can read in strict mode Kotlin metadata produced by Kotlin compilers from 1.0 up to and including this version + 1 minor.
*
* In other words, the metadata version is supported if it is greater or equal than 1.1, and less or equal than [CURRENT] + 1 minor version.
* Note that the metadata version is 1.1 for Kotlin from 1.0 until 1.4, and is equal to the language version starting from Kotlin 1.4.
*
* For example, if the latest supported stable Kotlin version is 1.7.0, kotlinx-metadata-jvm can read in strict mode binaries produced by Kotlin compilers from 1.0
* to 1.8.* inclusively. In this case, this property will have the value `1.7.0`.
*
* @see Metadata.metadataVersion
* @see KotlinClassMetadata.readStrict
*/
@JvmField
public val CURRENT: JvmMetadataVersion = JvmMetadataVersion(CompilerMetadataVersion.INSTANCE.toArray())
}
}
@@ -178,7 +178,7 @@ public final class KotlinClassHeader implements Metadata {
public static final int MULTI_FILE_CLASS_PART_KIND = 5; public static final int MULTI_FILE_CLASS_PART_KIND = 5;
/** /**
* Use {@link KotlinClassMetadata#COMPATIBLE_METADATA_VERSION} instead * Use {@link kotlinx.metadata.jvm.JvmMetadataVersion#CURRENT} instead
*/ */
@Deprecated @Deprecated
public static final int[] COMPATIBLE_METADATA_VERSION = Arrays.copyOf(JvmMetadataVersion.INSTANCE.toArray(), 3); public static final int[] COMPATIBLE_METADATA_VERSION = Arrays.copyOf(JvmMetadataVersion.INSTANCE.toArray(), 3);
@@ -4,6 +4,7 @@
*/ */
@file:Suppress( @file:Suppress(
"DEPRECATION_ERROR", // Deprecated .accept implementation "DEPRECATION_ERROR", // Deprecated .accept implementation
"DEPRECATION", // COMPATIBLE_METADATA_VERSION as default param
"UNUSED_PARAMETER" // For deprecated Writer.write "UNUSED_PARAMETER" // For deprecated Writer.write
) )
@@ -12,9 +13,8 @@ package kotlinx.metadata.jvm
import kotlinx.metadata.* import kotlinx.metadata.*
import kotlinx.metadata.internal.* import kotlinx.metadata.internal.*
import kotlinx.metadata.jvm.internal.* import kotlinx.metadata.jvm.internal.*
import kotlinx.metadata.jvm.internal.JvmReadUtils.checkMetadataVersion
import kotlinx.metadata.jvm.internal.JvmReadUtils.readMetadataImpl import kotlinx.metadata.jvm.internal.JvmReadUtils.readMetadataImpl
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion as CompilerMetadataVersion
import org.jetbrains.kotlin.metadata.jvm.serialization.JvmStringTable import org.jetbrains.kotlin.metadata.jvm.serialization.JvmStringTable
import java.util.* import java.util.*
@@ -61,16 +61,16 @@ public sealed class KotlinClassMetadata {
* Encodes and writes this metadata to the new instance of [Metadata]. * Encodes and writes this metadata to the new instance of [Metadata].
* *
* This method encodes all available data, including [version] and [flags]. * This method encodes all available data, including [version] and [flags].
* Due to technical limitations, it is not possible to write the metadata when the specified version is less (lexicographically) than `[1, 4]` * Due to technical limitations, it is not possible to write the metadata when the specified version is less than 1.4.
* *
* @throws IllegalArgumentException if metadata is malformed or [version] of this instance is less than 1.4 and cannot be written. * @throws IllegalArgumentException if metadata is malformed or metadata was read in lenient mode and cannot be written back or [version] of this instance is less than 1.4.
*/ */
public abstract fun write(): Metadata public abstract fun write(): Metadata
/** /**
* Version of this metadata. * Version of this metadata.
*/ */
public abstract var version: IntArray public abstract var version: JvmMetadataVersion
/** /**
* Additional classfile-level flags of this metadata. See [Metadata.extraInt] for possible values. * Additional classfile-level flags of this metadata. See [Metadata.extraInt] for possible values.
@@ -92,13 +92,13 @@ public sealed class KotlinClassMetadata {
* Returns the same (mutable) [KmClass] instance every time. * Returns the same (mutable) [KmClass] instance every time.
*/ */
public var kmClass: KmClass, public var kmClass: KmClass,
public override var version: IntArray, public override var version: JvmMetadataVersion,
public override var flags: Int, public override var flags: Int,
) : KotlinClassMetadata() { ) : KotlinClassMetadata() {
internal constructor(annotationData: Metadata, lenient: Boolean) : this( internal constructor(annotationData: Metadata, lenient: Boolean) : this(
JvmReadUtils.readKmClass(annotationData), JvmReadUtils.readKmClass(annotationData),
annotationData.metadataVersion, JvmMetadataVersion(annotationData.metadataVersion),
annotationData.extraInt annotationData.extraInt
) { ) {
isAllowedToWrite = !lenient isAllowedToWrite = !lenient
@@ -111,7 +111,7 @@ public sealed class KotlinClassMetadata {
val writer = ClassWriter(JvmStringTable()) val writer = ClassWriter(JvmStringTable())
writer.writeClass(kmClass) writer.writeClass(kmClass)
val (d1, d2) = writeProtoBufData(writer.t.build(), writer.c) val (d1, d2) = writeProtoBufData(writer.t.build(), writer.c)
Metadata(CLASS_KIND, version, d1, d2, extraInt = flags) Metadata(CLASS_KIND, version.toIntArray(), d1, d2, extraInt = flags)
} }
} }
@@ -181,7 +181,7 @@ public sealed class KotlinClassMetadata {
/** /**
* Version of this metadata. * Version of this metadata.
*/ */
public override var version: IntArray, public override var version: JvmMetadataVersion,
/** /**
* Additional classfile-level flags of this metadata. See [Metadata.extraInt] for possible values. * Additional classfile-level flags of this metadata. See [Metadata.extraInt] for possible values.
*/ */
@@ -190,7 +190,7 @@ public sealed class KotlinClassMetadata {
internal constructor(annotationData: Metadata, lenient: Boolean) : this( internal constructor(annotationData: Metadata, lenient: Boolean) : this(
JvmReadUtils.readKmPackage(annotationData), JvmReadUtils.readKmPackage(annotationData),
annotationData.metadataVersion, JvmMetadataVersion(annotationData.metadataVersion),
annotationData.extraInt annotationData.extraInt
) { ) {
isAllowedToWrite = !lenient isAllowedToWrite = !lenient
@@ -203,7 +203,7 @@ public sealed class KotlinClassMetadata {
val writer = PackageWriter(JvmStringTable()) val writer = PackageWriter(JvmStringTable())
writer.writePackage(kmPackage) writer.writePackage(kmPackage)
val (d1, d2) = writeProtoBufData(writer.t.build(), writer.c) val (d1, d2) = writeProtoBufData(writer.t.build(), writer.c)
Metadata(FILE_FACADE_KIND, version, d1, d2, extraInt = flags) Metadata(FILE_FACADE_KIND, version.toIntArray(), d1, d2, extraInt = flags)
} }
} }
@@ -269,7 +269,7 @@ public sealed class KotlinClassMetadata {
/** /**
* Version of this metadata. * Version of this metadata.
*/ */
public override var version: IntArray, public override var version: JvmMetadataVersion,
/** /**
* Additional classfile-level flags of this metadata. See [Metadata.extraInt] for possible values. * Additional classfile-level flags of this metadata. See [Metadata.extraInt] for possible values.
*/ */
@@ -278,7 +278,7 @@ public sealed class KotlinClassMetadata {
internal constructor(annotationData: Metadata, lenient: Boolean) : this( internal constructor(annotationData: Metadata, lenient: Boolean) : this(
JvmReadUtils.readKmLambda(annotationData), JvmReadUtils.readKmLambda(annotationData),
annotationData.metadataVersion, JvmMetadataVersion(annotationData.metadataVersion),
annotationData.extraInt annotationData.extraInt
) { ) {
isAllowedToWrite = !lenient isAllowedToWrite = !lenient
@@ -294,9 +294,9 @@ public sealed class KotlinClassMetadata {
val (d1, d2) = val (d1, d2) =
if (proto != null) writeProtoBufData(proto, writer.c) if (proto != null) writeProtoBufData(proto, writer.c)
else Pair(emptyArray<String>(), emptyArray<String>()) else Pair(emptyArray<String>(), emptyArray<String>())
Metadata(SYNTHETIC_CLASS_KIND, version, d1, d2, extraInt = flags) Metadata(SYNTHETIC_CLASS_KIND, version.toIntArray(), d1, d2, extraInt = flags)
} else { } else {
Metadata(SYNTHETIC_CLASS_KIND, version, emptyArray<String>(), emptyArray<String>(), extraInt = flags) Metadata(SYNTHETIC_CLASS_KIND, version.toIntArray(), emptyArray<String>(), emptyArray<String>(), extraInt = flags)
} }
} }
@@ -407,7 +407,7 @@ public sealed class KotlinClassMetadata {
/** /**
* Version of this metadata. * Version of this metadata.
*/ */
public override var version: IntArray, public override var version: JvmMetadataVersion,
/** /**
* Additional classfile-level flags of this metadata. See [Metadata.extraInt] for possible values. * Additional classfile-level flags of this metadata. See [Metadata.extraInt] for possible values.
*/ */
@@ -416,7 +416,7 @@ public sealed class KotlinClassMetadata {
internal constructor(annotationData: Metadata, lenient: Boolean) : this( internal constructor(annotationData: Metadata, lenient: Boolean) : this(
annotationData.data1.asList(), annotationData.data1.asList(),
annotationData.metadataVersion, JvmMetadataVersion(annotationData.metadataVersion),
annotationData.extraInt annotationData.extraInt
) { ) {
isAllowedToWrite = !lenient isAllowedToWrite = !lenient
@@ -426,7 +426,7 @@ public sealed class KotlinClassMetadata {
throwIfNotWriteable(isAllowedToWrite, "multi-file class facade") throwIfNotWriteable(isAllowedToWrite, "multi-file class facade")
checkMetadataVersion(version) checkMetadataVersion(version)
return Metadata( return Metadata(
MULTI_FILE_CLASS_FACADE_KIND, version, MULTI_FILE_CLASS_FACADE_KIND, version.toIntArray(),
partClassNames.toTypedArray<String>(), extraInt = flags partClassNames.toTypedArray<String>(), extraInt = flags
) )
} }
@@ -488,7 +488,7 @@ public sealed class KotlinClassMetadata {
/** /**
* Version of this metadata. * Version of this metadata.
*/ */
public override var version: IntArray, public override var version: JvmMetadataVersion,
/** /**
* Additional classfile-level flags of this metadata. See [Metadata.extraInt] for possible values. * Additional classfile-level flags of this metadata. See [Metadata.extraInt] for possible values.
*/ */
@@ -498,7 +498,7 @@ public sealed class KotlinClassMetadata {
internal constructor(annotationData: Metadata, lenient: Boolean) : this( internal constructor(annotationData: Metadata, lenient: Boolean) : this(
JvmReadUtils.readKmPackage(annotationData), JvmReadUtils.readKmPackage(annotationData),
annotationData.extraString, annotationData.extraString,
annotationData.metadataVersion, JvmMetadataVersion(annotationData.metadataVersion),
annotationData.extraInt annotationData.extraInt
) { ) {
isAllowedToWrite = !lenient isAllowedToWrite = !lenient
@@ -512,7 +512,7 @@ public sealed class KotlinClassMetadata {
writer.writePackage(kmPackage) writer.writePackage(kmPackage)
val (d1, d2) = writeProtoBufData(writer.t.build(), writer.c) val (d1, d2) = writeProtoBufData(writer.t.build(), writer.c)
Metadata( Metadata(
MULTI_FILE_CLASS_PART_KIND, version, d1, d2, facadeClassName, extraInt = flags MULTI_FILE_CLASS_PART_KIND, version.toIntArray(), d1, d2, facadeClassName, extraInt = flags
) )
} }
} }
@@ -577,7 +577,7 @@ public sealed class KotlinClassMetadata {
/** /**
* Version of this metadata. * Version of this metadata.
*/ */
public override var version: IntArray = original.metadataVersion public override var version: JvmMetadataVersion = JvmMetadataVersion(original.metadataVersion)
/** /**
* Additional classfile-level flags of this metadata. See [Metadata.extraInt] for possible values. * Additional classfile-level flags of this metadata. See [Metadata.extraInt] for possible values.
@@ -586,14 +586,23 @@ public sealed class KotlinClassMetadata {
override fun write(): Metadata { override fun write(): Metadata {
throwIfNotWriteable(!lenient, "unknown kind") throwIfNotWriteable(!lenient, "unknown kind")
return Metadata(original.kind, version, original.data1, original.data2, original.extraString, original.packageName, flags) checkMetadataVersion(version)
return Metadata(
original.kind,
version.toIntArray(),
original.data1,
original.data2,
original.extraString,
original.packageName,
flags
)
} }
@PublishedApi @PublishedApi
@Deprecated("This declaration is intended for binary compatibility only", level = DeprecationLevel.HIDDEN) @Deprecated("This declaration is intended for binary compatibility only", level = DeprecationLevel.HIDDEN)
internal companion object { internal companion object {
@JvmField // For binary compatibility with previous `data object Unknown` @JvmField // For binary compatibility with previous `data object Unknown`
public val INSTANCE: Unknown = Unknown(Metadata(kind = 99), true) public val INSTANCE: Unknown = Unknown(Metadata(kind = 99, metadataVersion = JvmMetadataVersion.CURRENT.toIntArray()), true)
} }
} }
@@ -635,7 +644,7 @@ public sealed class KotlinClassMetadata {
kmClass: KmClass, kmClass: KmClass,
metadataVersion: IntArray = COMPATIBLE_METADATA_VERSION, metadataVersion: IntArray = COMPATIBLE_METADATA_VERSION,
extraInt: Int = 0, extraInt: Int = 0,
): Metadata = Class(kmClass, metadataVersion, extraInt).write() ): Metadata = Class(kmClass, JvmMetadataVersion(metadataVersion), extraInt).write()
/** /**
@@ -655,7 +664,7 @@ public sealed class KotlinClassMetadata {
kmPackage: KmPackage, kmPackage: KmPackage,
metadataVersion: IntArray = COMPATIBLE_METADATA_VERSION, metadataVersion: IntArray = COMPATIBLE_METADATA_VERSION,
extraInt: Int = 0, extraInt: Int = 0,
): Metadata = FileFacade(kmPackage, metadataVersion, extraInt).write() ): Metadata = FileFacade(kmPackage, JvmMetadataVersion(metadataVersion), extraInt).write()
/** /**
* Writes [kmLambda] as the synthetic class metadata. * Writes [kmLambda] as the synthetic class metadata.
@@ -674,7 +683,7 @@ public sealed class KotlinClassMetadata {
kmLambda: KmLambda, kmLambda: KmLambda,
metadataVersion: IntArray = COMPATIBLE_METADATA_VERSION, metadataVersion: IntArray = COMPATIBLE_METADATA_VERSION,
extraInt: Int = 0, extraInt: Int = 0,
): Metadata = SyntheticClass(kmLambda, metadataVersion, extraInt).write() ): Metadata = SyntheticClass(kmLambda, JvmMetadataVersion(metadataVersion), extraInt).write()
/** /**
* Writes synthetic class metadata. * Writes synthetic class metadata.
@@ -692,7 +701,7 @@ public sealed class KotlinClassMetadata {
public fun writeSyntheticClass( public fun writeSyntheticClass(
metadataVersion: IntArray = COMPATIBLE_METADATA_VERSION, metadataVersion: IntArray = COMPATIBLE_METADATA_VERSION,
extraInt: Int = 0, extraInt: Int = 0,
): Metadata = SyntheticClass(null, metadataVersion, extraInt).write() ): Metadata = SyntheticClass(null, JvmMetadataVersion(metadataVersion), extraInt).write()
/** /**
* Writes metadata of the multi-file class facade. * Writes metadata of the multi-file class facade.
@@ -714,7 +723,7 @@ public sealed class KotlinClassMetadata {
public fun writeMultiFileClassFacade( public fun writeMultiFileClassFacade(
partClassNames: List<String>, metadataVersion: IntArray = COMPATIBLE_METADATA_VERSION, partClassNames: List<String>, metadataVersion: IntArray = COMPATIBLE_METADATA_VERSION,
extraInt: Int = 0, extraInt: Int = 0,
): Metadata = MultiFileClassFacade(partClassNames, metadataVersion, extraInt).write() ): Metadata = MultiFileClassFacade(partClassNames, JvmMetadataVersion(metadataVersion), extraInt).write()
/** /**
* Writes the metadata of the multi-file class part. * Writes the metadata of the multi-file class part.
@@ -738,7 +747,7 @@ public sealed class KotlinClassMetadata {
facadeClassName: String, facadeClassName: String,
metadataVersion: IntArray = COMPATIBLE_METADATA_VERSION, metadataVersion: IntArray = COMPATIBLE_METADATA_VERSION,
extraInt: Int = 0, extraInt: Int = 0,
): Metadata = MultiFileClassPart(kmPackage, facadeClassName, metadataVersion, extraInt).write() ): Metadata = MultiFileClassPart(kmPackage, facadeClassName, JvmMetadataVersion(metadataVersion), extraInt).write()
/** /**
* Reads and parses the given annotation data of a Kotlin JVM class file and returns the correct type of [KotlinClassMetadata] encoded by * Reads and parses the given annotation data of a Kotlin JVM class file and returns the correct type of [KotlinClassMetadata] encoded by
@@ -771,14 +780,14 @@ public sealed class KotlinClassMetadata {
* [annotationData] may be obtained reflectively, constructed manually or with helper [kotlinx.metadata.jvm.Metadata] function, * [annotationData] may be obtained reflectively, constructed manually or with helper [kotlinx.metadata.jvm.Metadata] function,
* or equivalent [KotlinClassHeader] can be used. * or equivalent [KotlinClassHeader] can be used.
* *
* This method can read only supported metadata versions (see [COMPATIBLE_METADATA_VERSION] for definition). * This method can read only supported metadata versions (see [JvmMetadataVersion.CURRENT] for definition).
* It will throw an exception if the metadata version is greater than what kotlinx-metadata-jvm understands. * It will throw an exception if the metadata version is greater than what kotlinx-metadata-jvm understands.
* It is suitable when your tooling cannot tolerate reading potentially incomplete or incorrect information due to version differences. * It is suitable when your tooling cannot tolerate reading potentially incomplete or incorrect information due to version differences.
* It is also the only method that allows metadata transformation and `KotlinClassMetadata.write` subsequent calls. * It is also the only method that allows metadata transformation and `KotlinClassMetadata.write` subsequent calls.
* *
* @throws IllegalArgumentException if the metadata version is unsupported * @throws IllegalArgumentException if the metadata version is unsupported or if metadata is corrupted
* *
* @see COMPATIBLE_METADATA_VERSION * @see JvmMetadataVersion.CURRENT
*/ */
@JvmStatic @JvmStatic
public fun readStrict(annotationData: Metadata): KotlinClassMetadata = readMetadataImpl(annotationData, lenient = false) public fun readStrict(annotationData: Metadata): KotlinClassMetadata = readMetadataImpl(annotationData, lenient = false)
@@ -791,13 +800,13 @@ public sealed class KotlinClassMetadata {
* or equivalent [KotlinClassHeader] can be used. * or equivalent [KotlinClassHeader] can be used.
* *
* This method makes best effort to read unsupported metadata versions. * This method makes best effort to read unsupported metadata versions.
* If metadata version is greater than [COMPATIBLE_METADATA_VERSION] + 1, this method may ignore parts of the metadata it does not understand but it will not throw an exception. * If metadata version is greater than [JvmMetadataVersion.CURRENT] + 1, this method may ignore parts of the metadata it does not understand but it will not throw an exception.
* Because obtained metadata can be incomplete, its [KotlinClassMetadata.write] method will throw an exception. * Because obtained metadata can be incomplete, its [KotlinClassMetadata.write] method will throw an exception.
* This method still cannot read metadata produced by pre-1.0 compilers. * This method still cannot read metadata produced by pre-1.0 compilers.
* *
* @throws IllegalArgumentException if the metadata version is that of Kotlin 1.0 or metadata format has been changed in an unpredictable way and reading of incompatible metadata is not possible * @throws IllegalArgumentException if the metadata version is that of Kotlin 1.0 or metadata format has been changed in an unpredictable way and reading of incompatible metadata is not possible
* *
* @see COMPATIBLE_METADATA_VERSION * @see JvmMetadataVersion.CURRENT
*/ */
@JvmStatic @JvmStatic
public fun readLenient(annotationData: Metadata): KotlinClassMetadata = readMetadataImpl(annotationData, lenient = true) public fun readLenient(annotationData: Metadata): KotlinClassMetadata = readMetadataImpl(annotationData, lenient = true)
@@ -807,6 +816,13 @@ public sealed class KotlinClassMetadata {
throw IllegalArgumentException("This $name cannot be written because it represents metadata read in lenient mode") throw IllegalArgumentException("This $name cannot be written because it represents metadata read in lenient mode")
} }
internal fun checkMetadataVersion(version: JvmMetadataVersion) {
require(version.major >= 1 && (version.major > 1 || version.minor >= 4)) {
"This version of kotlinx-metadata-jvm doesn't support writing Kotlin metadata of version earlier than 1.4. " +
"Please change the version from $version to at least [1, 4]."
}
}
/** /**
* A class file kind signifying that the corresponding class file contains a declaration of a Kotlin class. * A class file kind signifying that the corresponding class file contains a declaration of a Kotlin class.
* *
@@ -860,8 +876,10 @@ public sealed class KotlinClassMetadata {
* *
* @see Metadata.metadataVersion * @see Metadata.metadataVersion
*/ */
@JvmField // TODO: move it somewhere since it is also used in KotlinModuleMetadata? @JvmField
public val COMPATIBLE_METADATA_VERSION: IntArray = JvmMetadataVersion.INSTANCE.toArray().copyOf() @Deprecated("Use JvmMetadataVersion.CURRENT instead", ReplaceWith("JvmMetadataVersion.CURRENT"), DeprecationLevel.WARNING)
public val COMPATIBLE_METADATA_VERSION: IntArray = CompilerMetadataVersion.INSTANCE.toArray().copyOf()
} }
} }
@@ -21,7 +21,7 @@ import kotlinx.metadata.jvm.internal.JvmReadUtils.throwIfNotCompatible
import kotlinx.metadata.jvm.internal.wrapIntoMetadataExceptionWhenNeeded import kotlinx.metadata.jvm.internal.wrapIntoMetadataExceptionWhenNeeded
import kotlinx.metadata.jvm.internal.wrapWriteIntoIAE import kotlinx.metadata.jvm.internal.wrapWriteIntoIAE
import org.jetbrains.kotlin.metadata.jvm.JvmModuleProtoBuf import org.jetbrains.kotlin.metadata.jvm.JvmModuleProtoBuf
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion as CompilerMetadataVersion
import org.jetbrains.kotlin.metadata.jvm.deserialization.ModuleMapping import org.jetbrains.kotlin.metadata.jvm.deserialization.ModuleMapping
import org.jetbrains.kotlin.metadata.jvm.deserialization.PackageParts import org.jetbrains.kotlin.metadata.jvm.deserialization.PackageParts
import org.jetbrains.kotlin.metadata.jvm.deserialization.serializeToByteArray import org.jetbrains.kotlin.metadata.jvm.deserialization.serializeToByteArray
@@ -181,9 +181,9 @@ public class KotlinModuleMetadata private constructor(
@UnstableMetadataApi @UnstableMetadataApi
@JvmStatic @JvmStatic
@JvmOverloads @JvmOverloads
public fun write(kmModule: KmModule, metadataVersion: IntArray = COMPATIBLE_METADATA_VERSION): ByteArray = wrapWriteIntoIAE { public fun write(kmModule: KmModule, metadataVersion: JvmMetadataVersion = JvmMetadataVersion.CURRENT): ByteArray = wrapWriteIntoIAE {
val w = Writer().also { it.writeModule(kmModule) } val w = Writer().also { it.writeModule(kmModule) }
return w.b.build().serializeToByteArray(JvmMetadataVersion(*metadataVersion), 0) return w.b.build().serializeToByteArray(CompilerMetadataVersion(metadataVersion.toIntArray(), false), 0)
} }
private fun dataFromBytes(bytes: ByteArray): ModuleMapping { private fun dataFromBytes(bytes: ByteArray): ModuleMapping {
@@ -71,11 +71,4 @@ internal object JvmReadUtils {
throw IllegalArgumentException("Provided Metadata instance has version $jvmMetadataVersion, $postfix") throw IllegalArgumentException("Provided Metadata instance has version $jvmMetadataVersion, $postfix")
} }
} }
internal fun checkMetadataVersion(version: IntArray) {
require(version.size >= 2 && version[0] >= 1 && (version[0] > 1 || version[1] >= 4)) {
"This version of kotlinx-metadata-jvm doesn't support writing Kotlin metadata of version earlier than 1.4. " +
"Please change the version from ${version.toList()} to at least [1, 4]."
}
}
} }
@@ -29,7 +29,7 @@ class DifferentVersionsTest {
@Test @Test
fun readsCurrentVersion() { fun readsCurrentVersion() {
assertContentEquals(KotlinClassMetadata.COMPATIBLE_METADATA_VERSION, metadata.metadataVersion) assertContentEquals(JvmMetadataVersion.INSTANCE.toArray(), metadata.metadataVersion)
assertIs<KotlinClassMetadata.Class>(KotlinClassMetadata.readStrict(metadata)) assertIs<KotlinClassMetadata.Class>(KotlinClassMetadata.readStrict(metadata))
assertIs<KotlinClassMetadata.Class>(KotlinClassMetadata.readLenient(metadata)) assertIs<KotlinClassMetadata.Class>(KotlinClassMetadata.readLenient(metadata))
} }
@@ -51,7 +51,7 @@ class DifferentVersionsTest {
@Test @Test
fun readsArbitraryFutureVersion() { fun readsArbitraryFutureVersion() {
val md = metadata.changeVersion(intArrayOf(2, 5, 0)) val md = metadata.changeVersion(intArrayOf(7, 5, 0))
assertFailsWith<IllegalArgumentException> { KotlinClassMetadata.readStrict(md) } assertFailsWith<IllegalArgumentException> { KotlinClassMetadata.readStrict(md) }
assertIs<KotlinClassMetadata.Class>(KotlinClassMetadata.readLenient(md)) assertIs<KotlinClassMetadata.Class>(KotlinClassMetadata.readLenient(md))
} }
@@ -7,6 +7,7 @@ package kotlinx.metadata.test;
import kotlin.Metadata; import kotlin.Metadata;
import kotlinx.metadata.KmClass; import kotlinx.metadata.KmClass;
import kotlinx.metadata.jvm.JvmMetadataVersion;
import kotlinx.metadata.jvm.KotlinClassHeader; import kotlinx.metadata.jvm.KotlinClassHeader;
import kotlinx.metadata.jvm.KotlinClassMetadata; import kotlinx.metadata.jvm.KotlinClassMetadata;
import org.junit.Test; import org.junit.Test;
@@ -32,7 +33,8 @@ public class JavaUsageTest {
Metadata m = MetadataSmokeTest.class.getAnnotation(Metadata.class); Metadata m = MetadataSmokeTest.class.getAnnotation(Metadata.class);
KotlinClassMetadata clazz1 = ((KotlinClassMetadata) Objects.requireNonNull(KotlinClassMetadata.readStrict(m))); KotlinClassMetadata clazz1 = ((KotlinClassMetadata) Objects.requireNonNull(KotlinClassMetadata.readStrict(m)));
Metadata written = clazz1.write(); Metadata written = clazz1.write();
assertArrayEquals(written.mv(), KotlinClassMetadata.COMPATIBLE_METADATA_VERSION); //noinspection KotlinInternalInJava
assertArrayEquals(written.mv(), JvmMetadataVersion.CURRENT.toIntArray());
assertEquals(50, written.xi()); assertEquals(50, written.xi());
} }
} }
@@ -0,0 +1,36 @@
/*
* Copyright 2010-2023 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 kotlinx.metadata.jvm.JvmMetadataVersion
import kotlin.test.*
class JvmMetadataVersionTest {
fun mv(major: Int, minor: Int, patch: Int) = JvmMetadataVersion(major, minor, patch)
@Test
fun testEquality() {
assertEquals(mv(1, 2, 3), mv(1, 2, 3))
assertNotEquals(mv(0, 2, 3), mv(1, 2, 3))
assertNotEquals(mv(1, 0, 3), mv(1, 2, 3))
assertNotEquals(mv(1, 2, 3), mv(1, 2, 0))
}
@Test
fun testComparison() {
assertTrue(mv(1, 2, 3) > mv(1, 2, 0))
assertTrue(mv(1, 0, 3) < mv(1, 2, 0))
assertTrue(mv(1, 2, 3) < mv(2, 0, 0))
}
@Test
fun testConstructor() {
assertFailsWith<IllegalArgumentException> { mv(-1, 0, 0) }
assertFailsWith<IllegalArgumentException> { mv(0, -1, 0) }
assertFailsWith<IllegalArgumentException> { mv(0, 0, -1) }
}
}
@@ -6,9 +6,10 @@
package kotlinx.metadata.test package kotlinx.metadata.test
import kotlinx.metadata.KmClass import kotlinx.metadata.KmClass
import kotlinx.metadata.jvm.JvmMetadataVersion
import kotlinx.metadata.jvm.KotlinClassMetadata import kotlinx.metadata.jvm.KotlinClassMetadata
import kotlinx.metadata.jvm.Metadata import kotlinx.metadata.jvm.Metadata
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion as CompilerMetadataVersion
import org.jetbrains.kotlin.protobuf.InvalidProtocolBufferException import org.jetbrains.kotlin.protobuf.InvalidProtocolBufferException
import org.junit.Test import org.junit.Test
import kotlin.test.* import kotlin.test.*
@@ -18,7 +19,7 @@ class MetadataExceptionsTest {
fun testReadMalformedInput() { fun testReadMalformedInput() {
val malformedInput = "abcdefdkdgwaydgyuawdfg543awyudfuiawty" // random string guaranteed by dropping ball on a keyboard val malformedInput = "abcdefdkdgwaydgyuawdfg543awyudfuiawty" // random string guaranteed by dropping ball on a keyboard
val malformedMetadata = val malformedMetadata =
Metadata(KotlinClassMetadata.CLASS_KIND, KotlinClassMetadata.COMPATIBLE_METADATA_VERSION, arrayOf(malformedInput)) Metadata(KotlinClassMetadata.CLASS_KIND, CompilerMetadataVersion.INSTANCE.toArray(), arrayOf(malformedInput))
val e = assertFailsWith<IllegalArgumentException> { val e = assertFailsWith<IllegalArgumentException> {
(KotlinClassMetadata.readStrict(malformedMetadata) as KotlinClassMetadata.Class) (KotlinClassMetadata.readStrict(malformedMetadata) as KotlinClassMetadata.Class)
} }
@@ -29,7 +30,7 @@ class MetadataExceptionsTest {
fun testWriteMalformedClass() { fun testWriteMalformedClass() {
val kmClass = KmClass() // kotlin.UninitializedPropertyAccessException: lateinit property name has not been initialized val kmClass = KmClass() // kotlin.UninitializedPropertyAccessException: lateinit property name has not been initialized
val e = assertFailsWith<IllegalArgumentException> { val e = assertFailsWith<IllegalArgumentException> {
KotlinClassMetadata.Class(kmClass, KotlinClassMetadata.COMPATIBLE_METADATA_VERSION, 0).write() KotlinClassMetadata.Class(kmClass, JvmMetadataVersion.CURRENT, 0).write()
} }
assertIs<UninitializedPropertyAccessException>(e.cause) assertIs<UninitializedPropertyAccessException>(e.cause)
} }
@@ -49,10 +50,10 @@ class MetadataExceptionsTest {
@Test @Test
fun testReadNewerVersion() { fun testReadNewerVersion() {
val versionPlus2 = JvmMetadataVersion.INSTANCE.next().next() val versionPlus2 = CompilerMetadataVersion.INSTANCE.next().next()
doTestVersion( doTestVersion(
versionPlus2.toArray(), versionPlus2.toArray(),
"version $versionPlus2, while maximum supported version is ${JvmMetadataVersion.INSTANCE_NEXT}" "version $versionPlus2, while maximum supported version is ${CompilerMetadataVersion.INSTANCE_NEXT}"
) )
} }
@@ -60,7 +61,7 @@ class MetadataExceptionsTest {
fun testInvalidVersion() { fun testInvalidVersion() {
doTestVersion(intArrayOf(), "instance does not have metadataVersion in it and therefore is malformed and cannot be read") doTestVersion(intArrayOf(), "instance does not have metadataVersion in it and therefore is malformed and cannot be read")
doTestVersion( doTestVersion(
JvmMetadataVersion.INVALID_VERSION.toArray(), CompilerMetadataVersion.INVALID_VERSION.toArray(),
"instance does not have metadataVersion in it and therefore is malformed and cannot be read" "instance does not have metadataVersion in it and therefore is malformed and cannot be read"
) )
} }
@@ -63,7 +63,7 @@ class MetadataSmokeTest {
} }
} }
val annotationData = KotlinClassMetadata.Class(klass, KotlinClassMetadata.COMPATIBLE_METADATA_VERSION, 0).write() val annotationData = KotlinClassMetadata.Class(klass, JvmMetadataVersion.CURRENT, 0).write()
// Then, produce the bytecode of a .class file with ASM // Then, produce the bytecode of a .class file with ASM
@@ -179,7 +179,7 @@ class MetadataSmokeTest {
@OptIn(UnstableMetadataApi::class) @OptIn(UnstableMetadataApi::class)
fun metadataVersionEarlierThan1_4() { fun metadataVersionEarlierThan1_4() {
val dummy = MetadataSmokeTest::class.java.readMetadataAsKmClass() val dummy = MetadataSmokeTest::class.java.readMetadataAsKmClass()
val mv = intArrayOf(1, 3) val mv = JvmMetadataVersion(1, 3)
assertFailsWith<IllegalArgumentException> { KotlinClassMetadata.Class(dummy, mv, 0).write() } // We can't write empty KmClass() assertFailsWith<IllegalArgumentException> { KotlinClassMetadata.Class(dummy, mv, 0).write() } // We can't write empty KmClass()
assertFailsWith<IllegalArgumentException> { KotlinClassMetadata.FileFacade(KmPackage(), mv, 0).write() } assertFailsWith<IllegalArgumentException> { KotlinClassMetadata.FileFacade(KmPackage(), mv, 0).write() }
assertFailsWith<IllegalArgumentException> { KotlinClassMetadata.MultiFileClassFacade(listOf("A"), mv, 0).write() } assertFailsWith<IllegalArgumentException> { KotlinClassMetadata.MultiFileClassFacade(listOf("A"), mv, 0).write() }
@@ -5,27 +5,47 @@
package kotlinx.metadata.test package kotlinx.metadata.test
import kotlinx.metadata.jvm.JvmMetadataVersion
import kotlinx.metadata.jvm.KotlinClassMetadata import kotlinx.metadata.jvm.KotlinClassMetadata
import org.junit.Ignore import kotlinx.metadata.jvm.Metadata
import org.junit.Test import org.junit.Test
import kotlin.test.assertContentEquals
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith import kotlin.test.assertFailsWith
class WritersContractTest { class WritersContractTest {
val classMd = WritersContractTest::class.java.getMetadata() val classMd = WritersContractTest::class.java.getMetadata()
val l: () -> Unit = {} val l: () -> Unit = {}
val lambdaMd = l::class.java.getMetadata() val lambdaMd = l::class.java.getMetadata()
// val fileFacadeMd = TODO val fileFacadeMd = Class.forName("kotlinx.metadata.test.testdata.FileFacade").getMetadata()
val multiFileFacadeMd = Class.forName("kotlinx.metadata.test.testdata.MultiFileClassFacade").getMetadata()
val multiFilePartMd = Class.forName("kotlinx.metadata.test.testdata.MultiFileClassFacade__MultiFileClassFacade1Kt").getMetadata()
val unknown = Metadata(99, metadataVersion = intArrayOf(2, 0, 0), extraString = "blabla")
val everyType = listOf(classMd, fileFacadeMd, lambdaMd, multiFileFacadeMd, multiFilePartMd, unknown)
@Test @Test
fun lenientDataCantBeWritten() { fun lenientDataCantBeWritten() = everyType.forEach { md ->
val lenientClass = KotlinClassMetadata.readLenient(classMd) val lenientClass = KotlinClassMetadata.readLenient(md)
assertFailsWith<IllegalArgumentException> { lenientClass.write() } assertFailsWith<IllegalArgumentException> { lenientClass.write() }
} }
@Test @Test
fun oldVersionCantBeWritten() { fun oldVersionCantBeWritten() = everyType.forEach { md ->
val writeableClass = KotlinClassMetadata.readStrict(classMd) as KotlinClassMetadata.Class val writeableClass = KotlinClassMetadata.readStrict(md)
writeableClass.version = intArrayOf(1, 2, 0) writeableClass.version = JvmMetadataVersion(1, 2)
assertFailsWith<IllegalArgumentException> { writeableClass.write() } assertFailsWith<IllegalArgumentException> { writeableClass.write() }
} }
@Test
fun transformIsIdentical() = everyType.forEach { before ->
val after = KotlinClassMetadata.transform(before) { }
// note: data1 and data2 will always differ
assertEquals(before.kind, after.kind)
assertContentEquals(before.metadataVersion, after.metadataVersion)
assertEquals(before.extraInt, after.extraInt)
assertEquals(before.extraString, after.extraString)
assertEquals(before.packageName, after.packageName)
}
} }
@@ -0,0 +1,9 @@
/*
* Copyright 2010-2023 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:JvmName("FileFacade")
package kotlinx.metadata.test.testdata
fun insideFileFacade() {}
@@ -0,0 +1,10 @@
/*
* Copyright 2010-2023 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:JvmMultifileClass
@file:JvmName("MultiFileClassFacade")
package kotlinx.metadata.test.testdata
fun partOne() {}
@@ -0,0 +1,10 @@
/*
* Copyright 2010-2023 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:JvmMultifileClass
@file:JvmName("MultiFileClassFacade")
package kotlinx.metadata.test.testdata
fun partTwo() {}
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.jvm.abi package org.jetbrains.kotlin.jvm.abi
import kotlinx.metadata.* import kotlinx.metadata.*
import kotlinx.metadata.jvm.JvmMetadataVersion
import kotlinx.metadata.jvm.KotlinClassMetadata import kotlinx.metadata.jvm.KotlinClassMetadata
import kotlinx.metadata.jvm.Metadata import kotlinx.metadata.jvm.Metadata
import kotlinx.metadata.jvm.localDelegatedProperties import kotlinx.metadata.jvm.localDelegatedProperties
@@ -46,7 +47,7 @@ fun abiMetadataProcessor(annotationVisitor: AnnotationVisitor): AnnotationVisito
// TODO: maybe jvm-abi-gen should throw this exception by default, and not only in tests. // TODO: maybe jvm-abi-gen should throw this exception by default, and not only in tests.
if (System.getProperty("idea.is.unit.test").toBoolean()) { if (System.getProperty("idea.is.unit.test").toBoolean()) {
val actual = "${metadataVersion[0]}.${metadataVersion[1]}" val actual = "${metadataVersion[0]}.${metadataVersion[1]}"
val expected = KotlinClassMetadata.COMPATIBLE_METADATA_VERSION.let { "${it[0]}.${it[1]}" } val expected = JvmMetadataVersion.CURRENT.toString()
throw AssertionError( throw AssertionError(
"jvm-abi-gen can't process class file with the new metadata version because the version of kotlinx-metadata-jvm " + "jvm-abi-gen can't process class file with the new metadata version because the version of kotlinx-metadata-jvm " +
"it depends on is too old.\n" + "it depends on is too old.\n" +