K2: suppress exception when reading unsupported metadata

Before the change, the compiler threw exception in the unmuted tests,
because it tried to load metadata even though it had an unsupported
version. Use the same approach as in K1 (see
DeserializedDescriptorResolver).

Now the tests are unmuted, but note that test data differs from K1. K1
does not report errors related to the class `a.A` because it loads this
class as a _Java class_, so calling its constructor and methods somehow
works. This behavior is questionable since the compiler surely knows
that it is a Kotlin class, but with an unsupported metadata version.
Trying to interpret it as a Java class may lead to subtle problems. So
it's safer for now to avoid loading Kotlin classes with unsupported
metadata versions in K2.

 #KT-60795 Fixed
This commit is contained in:
Alexander Udalov
2023-08-11 23:01:04 +02:00
committed by Space Team
parent 8f720ad24b
commit 8738ffb84f
4 changed files with 98 additions and 6 deletions
@@ -33,6 +33,7 @@ import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.StandardClassIds
import org.jetbrains.kotlin.protobuf.InvalidProtocolBufferException
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
import org.jetbrains.kotlin.serialization.deserialization.IncompatibleVersionErrorData
import org.jetbrains.kotlin.serialization.deserialization.builtins.BuiltInSerializerProtocol
@@ -82,7 +83,9 @@ class JvmClassFileBasedSymbolProvider(
val header = kotlinClass.classHeader
val data = header.data ?: header.incompatibleData ?: return null
val strings = header.strings ?: return null
val (nameResolver, packageProto) = JvmProtoBufUtil.readPackageDataFrom(data, strings)
val (nameResolver, packageProto) = parseProto(kotlinClass) {
JvmProtoBufUtil.readPackageDataFrom(data, strings)
} ?: return null
val source = JvmPackagePartSource(
kotlinClass, packageProto, nameResolver, kotlinClass.incompatibility, kotlinClass.isPreReleaseInvisible,
@@ -142,7 +145,9 @@ class JvmClassFileBasedSymbolProvider(
if (kotlinClass.classHeader.kind != KotlinClassHeader.Kind.CLASS || kotlinClass.classId != classId) return null
val data = kotlinClass.classHeader.data ?: kotlinClass.classHeader.incompatibleData ?: return null
val strings = kotlinClass.classHeader.strings ?: return null
val (nameResolver, classProto) = JvmProtoBufUtil.readClassDataFrom(data, strings)
val (nameResolver, classProto) = parseProto(kotlinClass) {
JvmProtoBufUtil.readClassDataFrom(data, strings)
} ?: return null
return ClassMetadataFindResult.Metadata(
nameResolver,
@@ -190,4 +195,19 @@ class JvmClassFileBasedSymbolProvider(
private fun String?.toPath(): Path? {
return this?.let { Paths.get(it).normalize() }
}
private inline fun <T : Any> parseProto(klass: KotlinJvmBinaryClass, block: () -> T): T? =
try {
block()
} catch (e: Throwable) {
if (session.languageVersionSettings.getFlag(AnalysisFlags.skipMetadataVersionCheck) ||
klass.classHeader.metadataVersion.isCompatible(ownMetadataVersion)
) {
throw if (e is InvalidProtocolBufferException)
IllegalStateException("Could not read data from ${klass.location}", e)
else e
}
null
}
}