Do not allow writing metadata versions that are too high

because a format may be incorrect for future compilers

#KT-64230 Fixed
This commit is contained in:
Leonid Startsev
2023-12-08 16:53:42 +01:00
committed by Space Team
parent 0d4dfbcf0c
commit af23706a9a
3 changed files with 31 additions and 7 deletions
@@ -112,5 +112,8 @@ public class JvmMetadataVersion(public val major: Int, public val minor: Int, pu
*/
@JvmField
public val LATEST_STABLE_SUPPORTED: JvmMetadataVersion = JvmMetadataVersion(CompilerMetadataVersion.INSTANCE.toArray())
@JvmField
internal val HIGHEST_ALLOWED_TO_WRITE: JvmMetadataVersion = JvmMetadataVersion(CompilerMetadataVersion.INSTANCE_NEXT.toArray())
}
}
@@ -106,7 +106,7 @@ public sealed class KotlinClassMetadata {
override fun write(): Metadata {
throwIfNotWriteable(isAllowedToWrite, "class")
checkMetadataVersion(version)
checkMetadataVersionForWrite(version)
return wrapWriteIntoIAE {
val writer = ClassWriter(JvmStringTable())
writer.writeClass(kmClass)
@@ -198,7 +198,7 @@ public sealed class KotlinClassMetadata {
override fun write(): Metadata {
throwIfNotWriteable(isAllowedToWrite, "file facade")
checkMetadataVersion(version)
checkMetadataVersionForWrite(version)
return wrapWriteIntoIAE {
val writer = PackageWriter(JvmStringTable())
writer.writePackage(kmPackage)
@@ -286,7 +286,7 @@ public sealed class KotlinClassMetadata {
override fun write(): Metadata {
throwIfNotWriteable(isAllowedToWrite, if (isLambda) "lambda" else "synthetic class")
checkMetadataVersion(version)
checkMetadataVersionForWrite(version)
return if (isLambda) wrapWriteIntoIAE {
val writer = LambdaWriter(JvmStringTable())
writer.writeLambda(kmLambda!!)
@@ -424,7 +424,7 @@ public sealed class KotlinClassMetadata {
override fun write(): Metadata {
throwIfNotWriteable(isAllowedToWrite, "multi-file class facade")
checkMetadataVersion(version)
checkMetadataVersionForWrite(version)
return Metadata(
MULTI_FILE_CLASS_FACADE_KIND, version.toIntArray(),
partClassNames.toTypedArray<String>(), extraInt = flags
@@ -506,7 +506,7 @@ public sealed class KotlinClassMetadata {
override fun write(): Metadata {
throwIfNotWriteable(isAllowedToWrite, "multi-file class part")
checkMetadataVersion(version)
checkMetadataVersionForWrite(version)
return wrapWriteIntoIAE {
val writer = PackageWriter(JvmStringTable())
writer.writePackage(kmPackage)
@@ -586,7 +586,7 @@ public sealed class KotlinClassMetadata {
override fun write(): Metadata {
throwIfNotWriteable(!lenient, "unknown kind")
checkMetadataVersion(version)
checkMetadataVersionForWrite(version)
return Metadata(
original.kind,
version.toIntArray(),
@@ -817,11 +817,14 @@ public sealed class KotlinClassMetadata {
throw IllegalArgumentException("This $name cannot be written because it represents metadata read in lenient mode")
}
internal fun checkMetadataVersion(version: JvmMetadataVersion) {
private fun checkMetadataVersionForWrite(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]."
}
require(version <= JvmMetadataVersion.HIGHEST_ALLOWED_TO_WRITE) {
"kotlinx-metadata-jvm cannot write metadata for future compiler versions. Requested to write version $version, but highest known version is ${JvmMetadataVersion.HIGHEST_ALLOWED_TO_WRITE}"
}
}
/**
@@ -48,4 +48,22 @@ class WritersContractTest {
assertEquals(before.extraString, after.extraString)
assertEquals(before.packageName, after.packageName)
}
@Test
fun futureVersionCantBeWritten() = everyType.forEach { before ->
val md = KotlinClassMetadata.readStrict(before)
md.version = JvmMetadataVersion(3, 4, 5)
assertFailsWith<IllegalArgumentException> { md.write() }
}
@Test
fun nextVersionWrite() = everyType.forEach { before ->
val md = KotlinClassMetadata.readStrict(before)
val ver = md.version
assertEquals(2, ver.major) // to correctly handle future case 2.x -> 3.0
md.version = JvmMetadataVersion(ver.major, ver.minor + 1, ver.patch)
md.write() // OK
md.version = JvmMetadataVersion(ver.major, ver.minor + 2, ver.patch)
assertFailsWith<IllegalArgumentException> { md.write() }
}
}