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 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 final class kotlinx/metadata/KmAnnotation {
|
||||||
public fun <init> (Ljava/lang/String;Ljava/util/Map;)V
|
public fun <init> (Ljava/lang/String;Ljava/util/Map;)V
|
||||||
public fun equals (Ljava/lang/Object;)Z
|
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,
|
* 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.
|
* 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.
|
* 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) {
|
sealed class KotlinClassMetadata(val annotationData: Metadata) {
|
||||||
|
|
||||||
@@ -37,16 +42,17 @@ sealed class KotlinClassMetadata(val annotationData: Metadata) {
|
|||||||
*/
|
*/
|
||||||
class Class internal constructor(annotationData: Metadata) : KotlinClassMetadata(annotationData) {
|
class Class internal constructor(annotationData: Metadata) : KotlinClassMetadata(annotationData) {
|
||||||
private val classData by lazy(PUBLICATION) {
|
private val classData by lazy(PUBLICATION) {
|
||||||
val data1 = (annotationData.data1.takeIf(Array<*>::isNotEmpty)
|
JvmProtoBufUtil.readClassDataFrom(annotationData.requireNotEmpty(), annotationData.data2)
|
||||||
?: throw InconsistentKotlinMetadataException("data1 must not be empty"))
|
|
||||||
JvmProtoBufUtil.readClassDataFrom(data1, annotationData.data2)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a new [KmClass] instance created from this class metadata.
|
* 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)
|
KmClass().apply(this::accept)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Makes the given visitor visit metadata of this class.
|
* 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) {
|
class FileFacade internal constructor(annotationData: Metadata) : KotlinClassMetadata(annotationData) {
|
||||||
private val packageData by lazy(PUBLICATION) {
|
private val packageData by lazy(PUBLICATION) {
|
||||||
val data1 = (annotationData.data1.takeIf(Array<*>::isNotEmpty)
|
JvmProtoBufUtil.readPackageDataFrom(annotationData.requireNotEmpty(), annotationData.data2)
|
||||||
?: throw InconsistentKotlinMetadataException("data1 must not be empty"))
|
|
||||||
JvmProtoBufUtil.readPackageDataFrom(data1, annotationData.data2)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new [KmPackage] instance from this file facade metadata.
|
* 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)
|
KmPackage().apply(this::accept)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Makes the given visitor visit metadata of this file facade.
|
* 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.
|
* Creates a new [KmLambda] instance from this synthetic class metadata.
|
||||||
* Returns `null` if this synthetic class does not represent a lambda.
|
* 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
|
if (isLambda) KmLambda().apply(this::accept) else null
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns `true` if this synthetic class is a class file compiled for a Kotlin lambda.
|
* 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
|
* Makes the given visitor visit metadata of this file facade, if this synthetic class represents a Kotlin lambda
|
||||||
* (`isLambda` == true).
|
* (`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
|
* @param v the visitor that must visit this lambda
|
||||||
*/
|
*/
|
||||||
@Deprecated(VISITOR_API_MESSAGE)
|
@Deprecated(VISITOR_API_MESSAGE)
|
||||||
fun accept(v: KmLambdaVisitor) {
|
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)"
|
"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) {
|
class MultiFileClassPart internal constructor(annotationData: Metadata) : KotlinClassMetadata(annotationData) {
|
||||||
private val packageData by lazy(PUBLICATION) {
|
private val packageData by lazy(PUBLICATION) {
|
||||||
val data1 = (annotationData.data1.takeIf(Array<*>::isNotEmpty)
|
JvmProtoBufUtil.readPackageDataFrom(annotationData.requireNotEmpty(), annotationData.data2)
|
||||||
?: throw InconsistentKotlinMetadataException("data1 must not be empty"))
|
|
||||||
JvmProtoBufUtil.readPackageDataFrom(data1, annotationData.data2)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -331,9 +339,12 @@ sealed class KotlinClassMetadata(val annotationData: Metadata) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new [KmPackage] instance from this multi-file class part 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)
|
KmPackage().apply(this::accept)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Makes the given visitor visit metadata of this multi-file class part.
|
* 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]`
|
* [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]),
|
* @param extraInt the value of the class-level flags to be written to the metadata (see [Metadata.extraInt]),
|
||||||
* 0 by default
|
* 0 by default
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException if [kmClass] is not correct and cannot be written or if [metadataVersion] is not supported for writing.
|
||||||
*/
|
*/
|
||||||
fun writeClass(
|
fun writeClass(
|
||||||
kmClass: KmClass,
|
kmClass: KmClass,
|
||||||
metadataVersion: IntArray = COMPATIBLE_METADATA_VERSION,
|
metadataVersion: IntArray = COMPATIBLE_METADATA_VERSION,
|
||||||
extraInt: Int = 0
|
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.
|
* 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]`
|
* [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]),
|
* @param extraInt the value of the class-level flags to be written to the metadata (see [Metadata.extraInt]),
|
||||||
* 0 by default
|
* 0 by default
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException if [kmPackage] is not correct and cannot be written or if [metadataVersion] is not supported for writing.
|
||||||
*/
|
*/
|
||||||
fun writeFileFacade(
|
fun writeFileFacade(
|
||||||
kmPackage: KmPackage,
|
kmPackage: KmPackage,
|
||||||
metadataVersion: IntArray = COMPATIBLE_METADATA_VERSION,
|
metadataVersion: IntArray = COMPATIBLE_METADATA_VERSION,
|
||||||
extraInt: Int = 0
|
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.
|
* 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]`
|
* [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]),
|
* @param extraInt the value of the class-level flags to be written to the metadata (see [Metadata.extraInt]),
|
||||||
* 0 by default
|
* 0 by default
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException if [kmLambda] is not correct and cannot be written or if [metadataVersion] is not supported for writing.
|
||||||
*/
|
*/
|
||||||
fun writeLambda(
|
fun writeLambda(
|
||||||
kmLambda: KmLambda,
|
kmLambda: KmLambda,
|
||||||
metadataVersion: IntArray = COMPATIBLE_METADATA_VERSION,
|
metadataVersion: IntArray = COMPATIBLE_METADATA_VERSION,
|
||||||
extraInt: Int = 0
|
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.
|
* 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]`
|
* [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]),
|
* @param extraInt the value of the class-level flags to be written to the metadata (see [Metadata.extraInt]),
|
||||||
* 0 by default
|
* 0 by default
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException if [metadataVersion] is not supported for writing.
|
||||||
*/
|
*/
|
||||||
fun writeSyntheticClass(
|
fun writeSyntheticClass(
|
||||||
metadataVersion: IntArray = COMPATIBLE_METADATA_VERSION,
|
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]`
|
* [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]),
|
* @param extraInt the value of the class-level flags to be written to the metadata (see [Metadata.extraInt]),
|
||||||
* 0 by default
|
* 0 by default
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException if [metadataVersion] is not supported for writing.
|
||||||
*/
|
*/
|
||||||
fun writeMultiFileClassFacade(
|
fun writeMultiFileClassFacade(
|
||||||
partClassNames: List<String>, metadataVersion: IntArray = COMPATIBLE_METADATA_VERSION,
|
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]`
|
* [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]),
|
* @param extraInt the value of the class-level flags to be written to the metadata (see [Metadata.extraInt]),
|
||||||
* 0 by default
|
* 0 by default
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException if [kmPackage] is not correct and cannot be written or if [metadataVersion] is not supported for writing.
|
||||||
*/
|
*/
|
||||||
fun writeMultiFileClassPart(
|
fun writeMultiFileClassPart(
|
||||||
kmPackage: KmPackage,
|
kmPackage: KmPackage,
|
||||||
facadeClassName: String,
|
facadeClassName: String,
|
||||||
metadataVersion: IntArray = COMPATIBLE_METADATA_VERSION,
|
metadataVersion: IntArray = COMPATIBLE_METADATA_VERSION,
|
||||||
extraInt: Int = 0
|
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
|
* 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,
|
* [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.
|
||||||
*
|
*
|
||||||
* Throws [InconsistentKotlinMetadataException] if the metadata has inconsistencies which signal that it may have been
|
* @throws IllegalArgumentException if the metadata cannot be parsed from binary format or is inconsistent with itself
|
||||||
* modified by a separate tool.
|
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun read(annotationData: Metadata): KotlinClassMetadata? {
|
fun read(annotationData: Metadata): KotlinClassMetadata? {
|
||||||
@@ -492,19 +521,14 @@ sealed class KotlinClassMetadata(val annotationData: Metadata) {
|
|||||||
).isCompatibleWithCurrentCompilerVersion()
|
).isCompatibleWithCurrentCompilerVersion()
|
||||||
) return null
|
) return null
|
||||||
|
|
||||||
return try {
|
// All data is loaded lazily, no exceptions here to handle
|
||||||
when (annotationData.kind) {
|
return when (annotationData.kind) {
|
||||||
CLASS_KIND -> Class(annotationData)
|
CLASS_KIND -> Class(annotationData)
|
||||||
FILE_FACADE_KIND -> FileFacade(annotationData)
|
FILE_FACADE_KIND -> FileFacade(annotationData)
|
||||||
SYNTHETIC_CLASS_KIND -> SyntheticClass(annotationData)
|
SYNTHETIC_CLASS_KIND -> SyntheticClass(annotationData)
|
||||||
MULTI_FILE_CLASS_FACADE_KIND -> MultiFileClassFacade(annotationData)
|
MULTI_FILE_CLASS_FACADE_KIND -> MultiFileClassFacade(annotationData)
|
||||||
MULTI_FILE_CLASS_PART_KIND -> MultiFileClassPart(annotationData)
|
MULTI_FILE_CLASS_PART_KIND -> MultiFileClassPart(annotationData)
|
||||||
else -> Unknown(annotationData)
|
else -> Unknown(annotationData)
|
||||||
}
|
|
||||||
} catch (e: InconsistentKotlinMetadataException) {
|
|
||||||
throw e
|
|
||||||
} catch (e: Throwable) {
|
|
||||||
throw InconsistentKotlinMetadataException("Exception occurred when reading Kotlin metadata", e)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
* 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
|
package kotlinx.metadata.jvm
|
||||||
|
|
||||||
@@ -11,6 +16,8 @@ import kotlinx.metadata.*
|
|||||||
import kotlinx.metadata.internal.accept
|
import kotlinx.metadata.internal.accept
|
||||||
import kotlinx.metadata.jvm.internal.IgnoreInApiDump
|
import kotlinx.metadata.jvm.internal.IgnoreInApiDump
|
||||||
import kotlinx.metadata.jvm.KotlinClassMetadata.Companion.COMPATIBLE_METADATA_VERSION
|
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.JvmModuleProtoBuf
|
||||||
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion
|
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion
|
||||||
import org.jetbrains.kotlin.metadata.jvm.deserialization.ModuleMapping
|
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,
|
* 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.
|
* 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.
|
* which means that it's either not the content of a .kotlin_module file, or it has been corrupted.
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
@UnstableMetadataApi
|
@UnstableMetadataApi
|
||||||
fun read(bytes: ByteArray): KotlinModuleMetadata? {
|
fun read(bytes: ByteArray): KotlinModuleMetadata? {
|
||||||
try {
|
return wrapIntoMetadataExceptionWhenNeeded {
|
||||||
val result = KotlinModuleMetadata(bytes)
|
val result = KotlinModuleMetadata(bytes)
|
||||||
if (result.data == ModuleMapping.EMPTY) return null
|
when (result.data) {
|
||||||
|
ModuleMapping.EMPTY -> null
|
||||||
if (result.data == ModuleMapping.CORRUPTED) {
|
ModuleMapping.CORRUPTED ->
|
||||||
throw InconsistentKotlinMetadataException("Data doesn't look like the content of a .kotlin_module file")
|
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]),
|
* @param metadataVersion metadata version to be written to the metadata (see [Metadata.metadataVersion]),
|
||||||
* [KotlinClassMetadata.COMPATIBLE_METADATA_VERSION] by default
|
* [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
|
@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)
|
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.
|
* 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
|
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.
|
* 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")
|
@file:Suppress("DEPRECATION")
|
||||||
@@ -347,7 +347,7 @@ private inline fun ProtoBuf.TypeParameter.accept(
|
|||||||
visit: (flags: Flags, name: String, id: Int, variance: KmVariance) -> KmTypeParameterVisitor?,
|
visit: (flags: Flags, name: String, id: Int, variance: KmVariance) -> KmTypeParameterVisitor?,
|
||||||
c: ReadContext
|
c: ReadContext
|
||||||
) {
|
) {
|
||||||
val variance = when (variance!!) {
|
val variance = when (requireNotNull(variance)) {
|
||||||
ProtoBuf.TypeParameter.Variance.IN -> KmVariance.IN
|
ProtoBuf.TypeParameter.Variance.IN -> KmVariance.IN
|
||||||
ProtoBuf.TypeParameter.Variance.OUT -> KmVariance.OUT
|
ProtoBuf.TypeParameter.Variance.OUT -> KmVariance.OUT
|
||||||
ProtoBuf.TypeParameter.Variance.INV -> KmVariance.INVARIANT
|
ProtoBuf.TypeParameter.Variance.INV -> KmVariance.INVARIANT
|
||||||
@@ -384,7 +384,7 @@ private fun ProtoBuf.Type.accept(v: KmTypeVisitor, c: ReadContext) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (argument in argumentList) {
|
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.IN -> KmVariance.IN
|
||||||
ProtoBuf.Type.Argument.Projection.OUT -> KmVariance.OUT
|
ProtoBuf.Type.Argument.Projection.OUT -> KmVariance.OUT
|
||||||
ProtoBuf.Type.Argument.Projection.INV -> KmVariance.INVARIANT
|
ProtoBuf.Type.Argument.Projection.INV -> KmVariance.INVARIANT
|
||||||
@@ -451,13 +451,13 @@ private fun ProtoBuf.Contract.accept(v: KmContractVisitor, c: ReadContext) {
|
|||||||
for (effect in effectList) {
|
for (effect in effectList) {
|
||||||
if (!effect.hasEffectType()) continue
|
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.RETURNS_CONSTANT -> KmEffectType.RETURNS_CONSTANT
|
||||||
ProtoBuf.Effect.EffectType.CALLS -> KmEffectType.CALLS
|
ProtoBuf.Effect.EffectType.CALLS -> KmEffectType.CALLS
|
||||||
ProtoBuf.Effect.EffectType.RETURNS_NOT_NULL -> KmEffectType.RETURNS_NOT_NULL
|
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.AT_MOST_ONCE -> KmEffectInvocationKind.AT_MOST_ONCE
|
||||||
ProtoBuf.Effect.InvocationKind.EXACTLY_ONCE -> KmEffectInvocationKind.EXACTLY_ONCE
|
ProtoBuf.Effect.InvocationKind.EXACTLY_ONCE -> KmEffectInvocationKind.EXACTLY_ONCE
|
||||||
ProtoBuf.Effect.InvocationKind.AT_LEAST_ONCE -> KmEffectInvocationKind.AT_LEAST_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()) {
|
if (hasConstantValue()) {
|
||||||
v.visitConstantValue(
|
v.visitConstantValue(
|
||||||
when (constantValue!!) {
|
when (requireNotNull(constantValue)) {
|
||||||
ProtoBuf.Expression.ConstantValue.TRUE -> true
|
ProtoBuf.Expression.ConstantValue.TRUE -> true
|
||||||
ProtoBuf.Expression.ConstantValue.FALSE -> false
|
ProtoBuf.Expression.ConstantValue.FALSE -> false
|
||||||
ProtoBuf.Expression.ConstantValue.NULL -> null
|
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.
|
* 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.
|
* Populates the given visitor with data in this type.
|
||||||
*
|
*
|
||||||
* @param visitor the visitor which will visit 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)
|
@Deprecated(VISITOR_API_MESSAGE)
|
||||||
fun accept(visitor: KmTypeVisitor) {
|
fun accept(visitor: KmTypeVisitor) {
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.kotlinp
|
package org.jetbrains.kotlin.kotlinp
|
||||||
|
|
||||||
import kotlinx.metadata.InconsistentKotlinMetadataException
|
|
||||||
import kotlinx.metadata.jvm.KotlinClassMetadata
|
import kotlinx.metadata.jvm.KotlinClassMetadata
|
||||||
import kotlinx.metadata.jvm.KotlinModuleMetadata
|
import kotlinx.metadata.jvm.KotlinModuleMetadata
|
||||||
import kotlinx.metadata.jvm.UnstableMetadataApi
|
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")
|
val header = file.readKotlinClassHeader() ?: throw KotlinpException("file is not a Kotlin class file: $file")
|
||||||
return try {
|
return try {
|
||||||
KotlinClassMetadata.read(header)
|
KotlinClassMetadata.read(header)
|
||||||
} catch (e: InconsistentKotlinMetadataException) {
|
} catch (e: IllegalArgumentException) {
|
||||||
throw KotlinpException("inconsistent Kotlin metadata: ${e.message}")
|
throw KotlinpException("inconsistent Kotlin metadata: ${e.message}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -171,7 +171,7 @@ private fun printType(type: KmType): String {
|
|||||||
} else {
|
} else {
|
||||||
val (variance, argumentType) = argument
|
val (variance, argumentType) = argument
|
||||||
if (variance == null || argumentType == null)
|
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)
|
val argumentTypeString = printType(argumentType)
|
||||||
buildString {
|
buildString {
|
||||||
if (variance != KmVariance.INVARIANT) {
|
if (variance != KmVariance.INVARIANT) {
|
||||||
|
|||||||
Reference in New Issue
Block a user