A Kotlin library for reading and modifying binary metadata in Kotlin/JVM compiler-generated files.
—
Functionality for reading and writing .kotlin_module files that contain package structure information and compilation metadata. Module metadata files store information about how Kotlin packages are organized and which classes belong to which packages.
Main class for handling Kotlin module metadata operations.
/**
* Represents Kotlin module metadata from .kotlin_module files
* @param kmModule The module structure containing package information
* @param version The metadata version used for this module
*/
class KotlinModuleMetadata(
var kmModule: KmModule,
var version: JvmMetadataVersion
) {
/**
* Writes the module metadata to a byte array suitable for .kotlin_module files
* @return ByteArray containing the serialized module metadata
*/
fun write(): ByteArray
}Static function for reading module metadata from byte arrays.
companion object {
/**
* Reads module metadata from a byte array (typically from .kotlin_module files)
* @param bytes The byte array containing module metadata
* @return KotlinModuleMetadata instance with parsed module information
*/
@JvmStatic
fun read(bytes: ByteArray): KotlinModuleMetadata
}Usage Examples:
import kotlin.metadata.jvm.KotlinModuleMetadata
import java.io.File
// Read module metadata from file
val moduleBytes = File("META-INF/main.kotlin_module").readBytes()
val moduleMetadata = KotlinModuleMetadata.read(moduleBytes)
println("Module version: ${moduleMetadata.version}")
println("Package parts: ${moduleMetadata.kmModule.packageParts.keys}")
// Access package information
moduleMetadata.kmModule.packageParts.forEach { (packageName, parts) ->
println("Package: $packageName")
println(" File facades: ${parts.fileFacades}")
println(" Multi-file parts: ${parts.multiFileClassParts}")
}
// Write modified metadata back to bytes
val modifiedBytes = moduleMetadata.write()
File("META-INF/modified.kotlin_module").writeBytes(modifiedBytes)Core module structure containing package organization information.
/**
* Represents the structure of a Kotlin module
*/
class KmModule {
/**
* Map of package names to their constituent parts
* Key: package name (dot-separated)
* Value: KmPackageParts containing facades and multi-file class information
*/
val packageParts: MutableMap<String, KmPackageParts>
/**
* List of optional annotation classes defined in this module
*/
val optionalAnnotationClasses: MutableList<KmClass>
}Represents the parts that make up a Kotlin package.
/**
* Represents the parts that constitute a Kotlin package
* @param fileFacades List of file facade class names for top-level declarations
* @param multiFileClassParts Map of multi-file class part names to their facade names
*/
class KmPackageParts(
val fileFacades: MutableList<String>,
val multiFileClassParts: MutableMap<String, String>
)Usage Examples:
// Create new module metadata
val module = KmModule()
// Add package with file facades
val packageParts = KmPackageParts(
fileFacades = mutableListOf("UtilsKt", "HelpersKt"),
multiFileClassParts = mutableMapOf(
"StringUtils__Part1Kt" to "StringUtilsKt",
"StringUtils__Part2Kt" to "StringUtilsKt"
)
)
module.packageParts["com.example.utils"] = packageParts
// Create module metadata
val moduleMetadata = KotlinModuleMetadata(
kmModule = module,
version = JvmMetadataVersion(1, 6, 0)
)
// Write to bytes
val bytes = moduleMetadata.write()Detailed operations on package structure information.
// Examine package structure
moduleMetadata.kmModule.packageParts.forEach { (packageName, parts) ->
println("Package: $packageName")
// File facades contain top-level functions and properties
if (parts.fileFacades.isNotEmpty()) {
println(" File facades:")
parts.fileFacades.forEach { facade ->
println(" $facade")
}
}
// Multi-file class parts are used when @JvmMultifileClass is used
if (parts.multiFileClassParts.isNotEmpty()) {
println(" Multi-file class parts:")
parts.multiFileClassParts.forEach { (partName, facadeName) ->
println(" $partName -> $facadeName")
}
}
}
// Add new package parts
val newPackageParts = KmPackageParts(
fileFacades = mutableListOf("NewUtilsKt"),
multiFileClassParts = mutableMapOf()
)
moduleMetadata.kmModule.packageParts["com.example.new"] = newPackageParts
// Modify existing package parts
val existingParts = moduleMetadata.kmModule.packageParts["com.example.utils"]
existingParts?.fileFacades?.add("AdditionalUtilsKt")Working with optional annotation classes in modules.
// Access optional annotation classes
moduleMetadata.kmModule.optionalAnnotationClasses.forEach { annotationClass ->
println("Optional annotation class: ${annotationClass.name}")
// Examine annotation properties
annotationClass.constructors.forEach { constructor ->
constructor.valueParameters.forEach { param ->
println(" Parameter: ${param.name} of type ${param.type}")
}
}
}
// Add optional annotation class (requires KmClass from kotlinx-metadata-core)
// This would typically be done when creating new module metadataModule metadata files are typically located at:
META-INF/main.kotlin_module - Main module metadataMETA-INF/<source-set>.kotlin_module - Source set specific metadataThe read() function may throw exceptions if the byte array contains invalid or corrupted module metadata. Always handle potential parsing exceptions:
try {
val moduleMetadata = KotlinModuleMetadata.read(bytes)
// Process metadata
} catch (e: Exception) {
println("Failed to parse module metadata: ${e.message}")
}Install with Tessl CLI
npx tessl i tessl/maven-org-jetbrains-kotlin--kotlin-metadata-jvm