Document that read/write methods may throw IAE and make sure this is true:
- Replace !! with requireNotNull() - Add try/catch to wrap all other exceptions (mainly protobuf parser ones) - Add tests for malformed input for both read and write - Hide InconsistentMetadataException from public API - Replace its external usages with IllegalArgumentException
This commit is contained in:
committed by
Space Team
parent
64838d5ec9
commit
4b524b8589
@@ -121,11 +121,6 @@ public final class kotlinx/metadata/FlagsKt {
|
||||
public static final fun flagsOf ([Lkotlinx/metadata/Flag;)I
|
||||
}
|
||||
|
||||
public final class kotlinx/metadata/InconsistentKotlinMetadataException : java/lang/RuntimeException {
|
||||
public fun <init> (Ljava/lang/String;Ljava/lang/Throwable;)V
|
||||
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/Throwable;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
}
|
||||
|
||||
public final class kotlinx/metadata/KmAnnotation {
|
||||
public fun <init> (Ljava/lang/String;Ljava/util/Map;)V
|
||||
public fun equals (Ljava/lang/Object;)Z
|
||||
|
||||
@@ -26,6 +26,11 @@ import kotlin.LazyThreadSafetyMode.PUBLICATION
|
||||
* It is recommended to study documentation for each subclass and decide what subclasses one has to handle in the `when` expression,
|
||||
* trying to cover as much as possible.
|
||||
* Normally, one would need at least a [Class] and a [FileFacade], as these are two most common kinds.
|
||||
*
|
||||
* Most of the subclasses offer a conversion method to transform metadata into a Km data structure — for example, [KotlinClassMetadata.Class.toKmClass].
|
||||
* Km data structures represent Kotlin declarations and offer variety of properties to introspect and alter them.
|
||||
* Note that parsing may be lazy, depending on the implementation; therefore, one may not see an [IllegalArgumentException] indicating malformed metadata
|
||||
* until one calls `toKmClass` or a similar method.
|
||||
*/
|
||||
sealed class KotlinClassMetadata(val annotationData: Metadata) {
|
||||
|
||||
@@ -37,16 +42,17 @@ sealed class KotlinClassMetadata(val annotationData: Metadata) {
|
||||
*/
|
||||
class Class internal constructor(annotationData: Metadata) : KotlinClassMetadata(annotationData) {
|
||||
private val classData by lazy(PUBLICATION) {
|
||||
val data1 = (annotationData.data1.takeIf(Array<*>::isNotEmpty)
|
||||
?: throw InconsistentKotlinMetadataException("data1 must not be empty"))
|
||||
JvmProtoBufUtil.readClassDataFrom(data1, annotationData.data2)
|
||||
JvmProtoBufUtil.readClassDataFrom(annotationData.requireNotEmpty(), annotationData.data2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new [KmClass] instance created from this class metadata.
|
||||
*
|
||||
* @throws IllegalArgumentException if metadata is malformed and can't be transformed into [KmClass].
|
||||
*/
|
||||
fun toKmClass(): KmClass =
|
||||
fun toKmClass(): KmClass = wrapIntoMetadataExceptionWhenNeeded {
|
||||
KmClass().apply(this::accept)
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the given visitor visit metadata of this class.
|
||||
@@ -100,16 +106,17 @@ sealed class KotlinClassMetadata(val annotationData: Metadata) {
|
||||
*/
|
||||
class FileFacade internal constructor(annotationData: Metadata) : KotlinClassMetadata(annotationData) {
|
||||
private val packageData by lazy(PUBLICATION) {
|
||||
val data1 = (annotationData.data1.takeIf(Array<*>::isNotEmpty)
|
||||
?: throw InconsistentKotlinMetadataException("data1 must not be empty"))
|
||||
JvmProtoBufUtil.readPackageDataFrom(data1, annotationData.data2)
|
||||
JvmProtoBufUtil.readPackageDataFrom(annotationData.requireNotEmpty(), annotationData.data2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new [KmPackage] instance from this file facade metadata.
|
||||
*
|
||||
* @throws IllegalArgumentException if metadata is malformed and can't be transformed into [KmPackage].
|
||||
*/
|
||||
fun toKmPackage(): KmPackage =
|
||||
fun toKmPackage(): KmPackage = wrapIntoMetadataExceptionWhenNeeded {
|
||||
KmPackage().apply(this::accept)
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the given visitor visit metadata of this file facade.
|
||||
@@ -167,9 +174,12 @@ sealed class KotlinClassMetadata(val annotationData: Metadata) {
|
||||
/**
|
||||
* Creates a new [KmLambda] instance from this synthetic class metadata.
|
||||
* Returns `null` if this synthetic class does not represent a lambda.
|
||||
*
|
||||
* @throws IllegalArgumentException if metadata is malformed and can't be transformed into [KmLambda].
|
||||
*/
|
||||
fun toKmLambda(): KmLambda? =
|
||||
fun toKmLambda(): KmLambda? = wrapIntoMetadataExceptionWhenNeeded {
|
||||
if (isLambda) KmLambda().apply(this::accept) else null
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if this synthetic class is a class file compiled for a Kotlin lambda.
|
||||
@@ -181,13 +191,13 @@ sealed class KotlinClassMetadata(val annotationData: Metadata) {
|
||||
* Makes the given visitor visit metadata of this file facade, if this synthetic class represents a Kotlin lambda
|
||||
* (`isLambda` == true).
|
||||
*
|
||||
* Throws [IllegalStateException] if this synthetic class does not represent a Kotlin lambda.
|
||||
* Throws [IllegalArgumentException] if this synthetic class does not represent a Kotlin lambda.
|
||||
*
|
||||
* @param v the visitor that must visit this lambda
|
||||
*/
|
||||
@Deprecated(VISITOR_API_MESSAGE)
|
||||
fun accept(v: KmLambdaVisitor) {
|
||||
if (!isLambda) throw IllegalStateException(
|
||||
if (!isLambda) throw IllegalArgumentException(
|
||||
"accept(KmLambdaVisitor) is only possible for synthetic classes which are lambdas (isLambda = true)"
|
||||
)
|
||||
|
||||
@@ -318,9 +328,7 @@ sealed class KotlinClassMetadata(val annotationData: Metadata) {
|
||||
*/
|
||||
class MultiFileClassPart internal constructor(annotationData: Metadata) : KotlinClassMetadata(annotationData) {
|
||||
private val packageData by lazy(PUBLICATION) {
|
||||
val data1 = (annotationData.data1.takeIf(Array<*>::isNotEmpty)
|
||||
?: throw InconsistentKotlinMetadataException("data1 must not be empty"))
|
||||
JvmProtoBufUtil.readPackageDataFrom(data1, annotationData.data2)
|
||||
JvmProtoBufUtil.readPackageDataFrom(annotationData.requireNotEmpty(), annotationData.data2)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -331,9 +339,12 @@ sealed class KotlinClassMetadata(val annotationData: Metadata) {
|
||||
|
||||
/**
|
||||
* Creates a new [KmPackage] instance from this multi-file class part metadata.
|
||||
*
|
||||
* @throws IllegalArgumentException if metadata is malformed and can't be transformed into [KmPackage].
|
||||
*/
|
||||
fun toKmPackage(): KmPackage =
|
||||
fun toKmPackage(): KmPackage = wrapIntoMetadataExceptionWhenNeeded {
|
||||
KmPackage().apply(this::accept)
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the given visitor visit metadata of this multi-file class part.
|
||||
@@ -395,12 +406,16 @@ sealed class KotlinClassMetadata(val annotationData: Metadata) {
|
||||
* [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 [Metadata.extraInt]),
|
||||
* 0 by default
|
||||
*
|
||||
* @throws IllegalArgumentException if [kmClass] is not correct and cannot be written or if [metadataVersion] is not supported for writing.
|
||||
*/
|
||||
fun writeClass(
|
||||
kmClass: KmClass,
|
||||
metadataVersion: IntArray = COMPATIBLE_METADATA_VERSION,
|
||||
extraInt: Int = 0
|
||||
): Class = Class.Writer().also { kmClass.accept(it) }.write(metadataVersion, extraInt)
|
||||
): Class = wrapWriteIntoIAE {
|
||||
Class.Writer().also { kmClass.accept(it) }.write(metadataVersion, extraInt)
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes [kmPackage] contents as the file facade metadata.
|
||||
@@ -409,12 +424,16 @@ sealed class KotlinClassMetadata(val annotationData: Metadata) {
|
||||
* [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 [Metadata.extraInt]),
|
||||
* 0 by default
|
||||
*
|
||||
* @throws IllegalArgumentException if [kmPackage] is not correct and cannot be written or if [metadataVersion] is not supported for writing.
|
||||
*/
|
||||
fun writeFileFacade(
|
||||
kmPackage: KmPackage,
|
||||
metadataVersion: IntArray = COMPATIBLE_METADATA_VERSION,
|
||||
extraInt: Int = 0
|
||||
): FileFacade = FileFacade.Writer().also { kmPackage.accept(it) }.write(metadataVersion, extraInt)
|
||||
): FileFacade = wrapWriteIntoIAE {
|
||||
FileFacade.Writer().also { kmPackage.accept(it) }.write(metadataVersion, extraInt)
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes [kmLambda] as the synthetic class metadata.
|
||||
@@ -423,12 +442,16 @@ sealed class KotlinClassMetadata(val annotationData: Metadata) {
|
||||
* [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 [Metadata.extraInt]),
|
||||
* 0 by default
|
||||
*
|
||||
* @throws IllegalArgumentException if [kmLambda] is not correct and cannot be written or if [metadataVersion] is not supported for writing.
|
||||
*/
|
||||
fun writeLambda(
|
||||
kmLambda: KmLambda,
|
||||
metadataVersion: IntArray = COMPATIBLE_METADATA_VERSION,
|
||||
extraInt: Int = 0
|
||||
): SyntheticClass = SyntheticClass.Writer().also { kmLambda.accept(it) }.write(metadataVersion, extraInt)
|
||||
): SyntheticClass = wrapWriteIntoIAE {
|
||||
SyntheticClass.Writer().also { kmLambda.accept(it) }.write(metadataVersion, extraInt)
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes synthetic class metadata.
|
||||
@@ -437,6 +460,8 @@ sealed class KotlinClassMetadata(val annotationData: Metadata) {
|
||||
* [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 [Metadata.extraInt]),
|
||||
* 0 by default
|
||||
*
|
||||
* @throws IllegalArgumentException if [metadataVersion] is not supported for writing.
|
||||
*/
|
||||
fun writeSyntheticClass(
|
||||
metadataVersion: IntArray = COMPATIBLE_METADATA_VERSION,
|
||||
@@ -451,6 +476,8 @@ sealed class KotlinClassMetadata(val annotationData: Metadata) {
|
||||
* [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 [Metadata.extraInt]),
|
||||
* 0 by default
|
||||
*
|
||||
* @throws IllegalArgumentException if [metadataVersion] is not supported for writing.
|
||||
*/
|
||||
fun writeMultiFileClassFacade(
|
||||
partClassNames: List<String>, metadataVersion: IntArray = COMPATIBLE_METADATA_VERSION,
|
||||
@@ -465,24 +492,26 @@ sealed class KotlinClassMetadata(val annotationData: Metadata) {
|
||||
* [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 [Metadata.extraInt]),
|
||||
* 0 by default
|
||||
*
|
||||
* @throws IllegalArgumentException if [kmPackage] is not correct and cannot be written or if [metadataVersion] is not supported for writing.
|
||||
*/
|
||||
fun writeMultiFileClassPart(
|
||||
kmPackage: KmPackage,
|
||||
facadeClassName: String,
|
||||
metadataVersion: IntArray = COMPATIBLE_METADATA_VERSION,
|
||||
extraInt: Int = 0
|
||||
): MultiFileClassPart = MultiFileClassPart.Writer().also { kmPackage.accept(it) }.write(facadeClassName, metadataVersion, extraInt)
|
||||
|
||||
): MultiFileClassPart = wrapWriteIntoIAE {
|
||||
MultiFileClassPart.Writer().also { kmPackage.accept(it) }.write(facadeClassName, metadataVersion, extraInt)
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads and parses the given annotation data of a Kotlin JVM class file and returns the correct type of [KotlinClassMetadata] encoded by
|
||||
* this annotation, or `null` if this annotation encodes an unsupported kind of Kotlin classes or has an unsupported metadata version.
|
||||
* this annotation, or `null` if this annotation data has an unsupported metadata version.
|
||||
*
|
||||
* [annotationData] may be obtained reflectively, constructed manually or with helper [kotlinx.metadata.jvm.Metadata] function,
|
||||
* or equivalent [KotlinClassHeader] can be used.
|
||||
*
|
||||
* Throws [InconsistentKotlinMetadataException] if the metadata has inconsistencies which signal that it may have been
|
||||
* modified by a separate tool.
|
||||
* @throws IllegalArgumentException if the metadata cannot be parsed from binary format or is inconsistent with itself
|
||||
*/
|
||||
@JvmStatic
|
||||
fun read(annotationData: Metadata): KotlinClassMetadata? {
|
||||
@@ -492,19 +521,14 @@ sealed class KotlinClassMetadata(val annotationData: Metadata) {
|
||||
).isCompatibleWithCurrentCompilerVersion()
|
||||
) return null
|
||||
|
||||
return try {
|
||||
when (annotationData.kind) {
|
||||
CLASS_KIND -> Class(annotationData)
|
||||
FILE_FACADE_KIND -> FileFacade(annotationData)
|
||||
SYNTHETIC_CLASS_KIND -> SyntheticClass(annotationData)
|
||||
MULTI_FILE_CLASS_FACADE_KIND -> MultiFileClassFacade(annotationData)
|
||||
MULTI_FILE_CLASS_PART_KIND -> MultiFileClassPart(annotationData)
|
||||
else -> Unknown(annotationData)
|
||||
}
|
||||
} catch (e: InconsistentKotlinMetadataException) {
|
||||
throw e
|
||||
} catch (e: Throwable) {
|
||||
throw InconsistentKotlinMetadataException("Exception occurred when reading Kotlin metadata", e)
|
||||
// All data is loaded lazily, no exceptions here to handle
|
||||
return when (annotationData.kind) {
|
||||
CLASS_KIND -> Class(annotationData)
|
||||
FILE_FACADE_KIND -> FileFacade(annotationData)
|
||||
SYNTHETIC_CLASS_KIND -> SyntheticClass(annotationData)
|
||||
MULTI_FILE_CLASS_FACADE_KIND -> MultiFileClassFacade(annotationData)
|
||||
MULTI_FILE_CLASS_PART_KIND -> MultiFileClassPart(annotationData)
|
||||
else -> Unknown(annotationData)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,12 @@
|
||||
* 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", "DEPRECATION")
|
||||
@file:Suppress(
|
||||
"MemberVisibilityCanBePrivate",
|
||||
"DEPRECATION",
|
||||
"INVISIBLE_MEMBER", // InconsistentKotlinMetadataException
|
||||
"INVISIBLE_REFERENCE"
|
||||
)
|
||||
|
||||
package kotlinx.metadata.jvm
|
||||
|
||||
@@ -11,6 +16,8 @@ import kotlinx.metadata.*
|
||||
import kotlinx.metadata.internal.accept
|
||||
import kotlinx.metadata.jvm.internal.IgnoreInApiDump
|
||||
import kotlinx.metadata.jvm.KotlinClassMetadata.Companion.COMPATIBLE_METADATA_VERSION
|
||||
import kotlinx.metadata.jvm.internal.wrapIntoMetadataExceptionWhenNeeded
|
||||
import kotlinx.metadata.jvm.internal.wrapWriteIntoIAE
|
||||
import org.jetbrains.kotlin.metadata.jvm.JvmModuleProtoBuf
|
||||
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion
|
||||
import org.jetbrains.kotlin.metadata.jvm.deserialization.ModuleMapping
|
||||
@@ -123,25 +130,20 @@ class KotlinModuleMetadata(@Suppress("MemberVisibilityCanBePrivate") val bytes:
|
||||
* Parses the given byte array with the .kotlin_module file content and returns the [KotlinModuleMetadata] instance,
|
||||
* or `null` if this byte array encodes a module with an unsupported metadata version.
|
||||
*
|
||||
* Throws [InconsistentKotlinMetadataException] if an error happened while parsing the given byte array,
|
||||
* @throws IllegalArgumentException if an error happened while parsing the given byte array,
|
||||
* which means that it's either not the content of a .kotlin_module file, or it has been corrupted.
|
||||
*/
|
||||
@JvmStatic
|
||||
@UnstableMetadataApi
|
||||
fun read(bytes: ByteArray): KotlinModuleMetadata? {
|
||||
try {
|
||||
return wrapIntoMetadataExceptionWhenNeeded {
|
||||
val result = KotlinModuleMetadata(bytes)
|
||||
if (result.data == ModuleMapping.EMPTY) return null
|
||||
|
||||
if (result.data == ModuleMapping.CORRUPTED) {
|
||||
throw InconsistentKotlinMetadataException("Data doesn't look like the content of a .kotlin_module file")
|
||||
when (result.data) {
|
||||
ModuleMapping.EMPTY -> null
|
||||
ModuleMapping.CORRUPTED ->
|
||||
throw InconsistentKotlinMetadataException("Data is not the content of a .kotlin_module file, or it has been corrupted.")
|
||||
else -> result
|
||||
}
|
||||
|
||||
return result
|
||||
} catch (e: InconsistentKotlinMetadataException) {
|
||||
throw e
|
||||
} catch (e: Throwable) {
|
||||
throw InconsistentKotlinMetadataException("Exception occurred when reading Kotlin metadata", e)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,10 +152,13 @@ class KotlinModuleMetadata(@Suppress("MemberVisibilityCanBePrivate") val bytes:
|
||||
*
|
||||
* @param metadataVersion metadata version to be written to the metadata (see [Metadata.metadataVersion]),
|
||||
* [KotlinClassMetadata.COMPATIBLE_METADATA_VERSION] by default
|
||||
*
|
||||
* @throws IllegalArgumentException if [kmModule] is not correct and cannot be written or if [metadataVersion] is not supported for writing.
|
||||
*/
|
||||
@UnstableMetadataApi
|
||||
fun write(kmModule: KmModule, metadataVersion: IntArray = COMPATIBLE_METADATA_VERSION): KotlinModuleMetadata =
|
||||
fun write(kmModule: KmModule, metadataVersion: IntArray = COMPATIBLE_METADATA_VERSION): KotlinModuleMetadata = wrapWriteIntoIAE {
|
||||
Writer().also { kmModule.accept(it) }.write(metadataVersion)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") // InconsistentKotlinMetadataException
|
||||
|
||||
package kotlinx.metadata.jvm.internal
|
||||
|
||||
import kotlinx.metadata.InconsistentKotlinMetadataException
|
||||
|
||||
internal fun Metadata.requireNotEmpty(): Array<String> = data1.takeIf(Array<*>::isNotEmpty)
|
||||
?: throw InconsistentKotlinMetadataException("Metadata is missing: kotlin.Metadata.data1 must not be an empty array")
|
||||
|
||||
internal inline fun <T> wrapIntoMetadataExceptionWhenNeeded(block: () -> T): T {
|
||||
return try {
|
||||
block()
|
||||
} catch (e: Throwable) {
|
||||
throw when (e) {
|
||||
is IllegalArgumentException -> e // rethrow IAE as it is already correct exception type
|
||||
is VirtualMachineError, is ThreadDeath -> e // rethrow VM errors
|
||||
// other exceptions, like IOOBE or InvalidProtocolBufferException from proto parser
|
||||
else -> InconsistentKotlinMetadataException("Exception occurred when reading Kotlin metadata", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal inline fun <T> wrapWriteIntoIAE(block: () -> T): T {
|
||||
return try {
|
||||
block()
|
||||
} catch (e: Throwable) {
|
||||
throw when (e) {
|
||||
is IllegalArgumentException -> e // rethrow IAE as it is already correct exception type
|
||||
is VirtualMachineError, is ThreadDeath -> e // rethrow VM errors
|
||||
// lateinit vars or proto writer can throw exceptions if required data is missing
|
||||
else -> IllegalArgumentException("Kotlin metadata is not correct and can not be written", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.KmClass
|
||||
import kotlinx.metadata.jvm.KotlinClassMetadata
|
||||
import kotlinx.metadata.jvm.Metadata
|
||||
import org.jetbrains.kotlin.protobuf.InvalidProtocolBufferException
|
||||
import org.junit.Test
|
||||
import kotlin.test.assertFailsWith
|
||||
import kotlin.test.assertIs
|
||||
|
||||
class MetadataExceptionsTest {
|
||||
@Test
|
||||
fun testReadMalformedInput() {
|
||||
val malformedInput = "abcdefdkdgwaydgyuawdfg543awyudfuiawty" // random string guaranteed by dropping ball on a keyboard
|
||||
val malformedMetadata =
|
||||
Metadata(KotlinClassMetadata.CLASS_KIND, KotlinClassMetadata.COMPATIBLE_METADATA_VERSION, arrayOf(malformedInput))
|
||||
val e = assertFailsWith<IllegalArgumentException> {
|
||||
(KotlinClassMetadata.read(malformedMetadata) as KotlinClassMetadata.Class).toKmClass()
|
||||
}
|
||||
assertIs<InvalidProtocolBufferException>(e.cause)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testWriteMalformedClass() {
|
||||
val kmClass = KmClass() // kotlin.UninitializedPropertyAccessException: lateinit property name has not been initialized
|
||||
val e = assertFailsWith<IllegalArgumentException> {
|
||||
KotlinClassMetadata.writeClass(kmClass)
|
||||
}
|
||||
assertIs<UninitializedPropertyAccessException>(e.cause)
|
||||
}
|
||||
}
|
||||
+5
-2
@@ -1,8 +1,11 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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
|
||||
|
||||
class InconsistentKotlinMetadataException(message: String, cause: Throwable? = null) : RuntimeException(message, cause)
|
||||
/**
|
||||
* A generic exception that indicates problems with metadata deserialization.
|
||||
*/
|
||||
internal class InconsistentKotlinMetadataException(message: String, cause: Throwable? = null) : IllegalArgumentException(message, cause)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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:Suppress("DEPRECATION")
|
||||
@@ -347,7 +347,7 @@ private inline fun ProtoBuf.TypeParameter.accept(
|
||||
visit: (flags: Flags, name: String, id: Int, variance: KmVariance) -> KmTypeParameterVisitor?,
|
||||
c: ReadContext
|
||||
) {
|
||||
val variance = when (variance!!) {
|
||||
val variance = when (requireNotNull(variance)) {
|
||||
ProtoBuf.TypeParameter.Variance.IN -> KmVariance.IN
|
||||
ProtoBuf.TypeParameter.Variance.OUT -> KmVariance.OUT
|
||||
ProtoBuf.TypeParameter.Variance.INV -> KmVariance.INVARIANT
|
||||
@@ -384,7 +384,7 @@ private fun ProtoBuf.Type.accept(v: KmTypeVisitor, c: ReadContext) {
|
||||
}
|
||||
|
||||
for (argument in argumentList) {
|
||||
val variance = when (argument.projection!!) {
|
||||
val variance = when (requireNotNull(argument.projection)) {
|
||||
ProtoBuf.Type.Argument.Projection.IN -> KmVariance.IN
|
||||
ProtoBuf.Type.Argument.Projection.OUT -> KmVariance.OUT
|
||||
ProtoBuf.Type.Argument.Projection.INV -> KmVariance.INVARIANT
|
||||
@@ -451,13 +451,13 @@ private fun ProtoBuf.Contract.accept(v: KmContractVisitor, c: ReadContext) {
|
||||
for (effect in effectList) {
|
||||
if (!effect.hasEffectType()) continue
|
||||
|
||||
val effectType = when (effect.effectType!!) {
|
||||
val effectType = when (requireNotNull(effect.effectType)) {
|
||||
ProtoBuf.Effect.EffectType.RETURNS_CONSTANT -> KmEffectType.RETURNS_CONSTANT
|
||||
ProtoBuf.Effect.EffectType.CALLS -> KmEffectType.CALLS
|
||||
ProtoBuf.Effect.EffectType.RETURNS_NOT_NULL -> KmEffectType.RETURNS_NOT_NULL
|
||||
}
|
||||
|
||||
val effectKind = if (!effect.hasKind()) null else when (effect.kind!!) {
|
||||
val effectKind = if (!effect.hasKind()) null else when (requireNotNull(effect.kind)) {
|
||||
ProtoBuf.Effect.InvocationKind.AT_MOST_ONCE -> KmEffectInvocationKind.AT_MOST_ONCE
|
||||
ProtoBuf.Effect.InvocationKind.EXACTLY_ONCE -> KmEffectInvocationKind.EXACTLY_ONCE
|
||||
ProtoBuf.Effect.InvocationKind.AT_LEAST_ONCE -> KmEffectInvocationKind.AT_LEAST_ONCE
|
||||
@@ -491,7 +491,7 @@ private fun ProtoBuf.Expression.accept(v: KmEffectExpressionVisitor, c: ReadCont
|
||||
|
||||
if (hasConstantValue()) {
|
||||
v.visitConstantValue(
|
||||
when (constantValue!!) {
|
||||
when (requireNotNull(constantValue)) {
|
||||
ProtoBuf.Expression.ConstantValue.TRUE -> true
|
||||
ProtoBuf.Expression.ConstantValue.FALSE -> false
|
||||
ProtoBuf.Expression.ConstantValue.NULL -> null
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.
|
||||
*/
|
||||
|
||||
@@ -866,6 +866,7 @@ class KmType(var flags: Flags) : KmTypeVisitor() {
|
||||
* Populates the given visitor with data in this type.
|
||||
*
|
||||
* @param visitor the visitor which will visit data in this type
|
||||
* @throws IllegalArgumentException if type metadata is inconsistent
|
||||
*/
|
||||
@Deprecated(VISITOR_API_MESSAGE)
|
||||
fun accept(visitor: KmTypeVisitor) {
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
package org.jetbrains.kotlin.kotlinp
|
||||
|
||||
import kotlinx.metadata.InconsistentKotlinMetadataException
|
||||
import kotlinx.metadata.jvm.KotlinClassMetadata
|
||||
import kotlinx.metadata.jvm.KotlinModuleMetadata
|
||||
import kotlinx.metadata.jvm.UnstableMetadataApi
|
||||
@@ -30,7 +29,7 @@ class Kotlinp(private val settings: KotlinpSettings) {
|
||||
val header = file.readKotlinClassHeader() ?: throw KotlinpException("file is not a Kotlin class file: $file")
|
||||
return try {
|
||||
KotlinClassMetadata.read(header)
|
||||
} catch (e: InconsistentKotlinMetadataException) {
|
||||
} catch (e: IllegalArgumentException) {
|
||||
throw KotlinpException("inconsistent Kotlin metadata: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,7 +171,7 @@ private fun printType(type: KmType): String {
|
||||
} else {
|
||||
val (variance, argumentType) = argument
|
||||
if (variance == null || argumentType == null)
|
||||
throw InconsistentKotlinMetadataException("Variance and type must be set for non-star type projection")
|
||||
throw IllegalArgumentException("Variance and type must be set for non-star type projection")
|
||||
val argumentTypeString = printType(argumentType)
|
||||
buildString {
|
||||
if (variance != KmVariance.INVARIANT) {
|
||||
|
||||
Reference in New Issue
Block a user