From c54b6cc0f0afe834aacb480c33fb5bab1271e518 Mon Sep 17 00:00:00 2001 From: seqradev Date: Sun, 1 Feb 2026 05:56:01 +0000 Subject: [PATCH] fix: Handle bytecode and Kotlin metadata safely --- .../src/main/kotlin/KotlinDependencyExt.kt | 9 ++++++- seqra-ir-core/build.gradle.kts | 5 +++- .../features/classpaths/KotlinMetadata.kt | 25 ++++++++++++++----- .../org/seqra/ir/impl/fs/ByteCodeLocations.kt | 14 ++++++++--- .../org/seqra/ir/impl/fs/JavaRuntime.kt | 5 ++++ .../ir/impl/fs/JavaRuntimeModuleLocation.kt | 14 +++++------ 6 files changed, 53 insertions(+), 19 deletions(-) diff --git a/buildSrc/src/main/kotlin/KotlinDependencyExt.kt b/buildSrc/src/main/kotlin/KotlinDependencyExt.kt index a1afd6d..2896c40 100644 --- a/buildSrc/src/main/kotlin/KotlinDependencyExt.kt +++ b/buildSrc/src/main/kotlin/KotlinDependencyExt.kt @@ -5,11 +5,18 @@ import org.seqra.common.id object KotlinDependencyExt { object Libs { - val kotlin_metadata_jvm = dep( + val kotlin_metadata_jvm_compile = dep( group = "org.jetbrains.kotlin", name = "kotlin-metadata-jvm", version = KotlinDependency.Versions.kotlin ) + + // note: binary compatibility required + val kotlin_metadata_jvm_runtime = dep( + group = "org.jetbrains.kotlin", + name = "kotlin-metadata-jvm", + version = "2.3.0" + ) } } diff --git a/seqra-ir-core/build.gradle.kts b/seqra-ir-core/build.gradle.kts index 8bd35f1..730f6a8 100644 --- a/seqra-ir-core/build.gradle.kts +++ b/seqra-ir-core/build.gradle.kts @@ -39,7 +39,10 @@ dependencies { compileOnly(Libs.hikaricp) implementation(KotlinDependency.Libs.kotlin_logging) - implementation(KotlinDependencyExt.Libs.kotlin_metadata_jvm) + compileOnly(KotlinDependencyExt.Libs.kotlin_metadata_jvm_compile) + runtimeOnly(KotlinDependencyExt.Libs.kotlin_metadata_jvm_runtime) { + exclude(group = "org.jetbrains.kotlin") + } implementation(KotlinDependency.Libs.kotlinx_serialization_core) implementation(Libs.jdot) implementation(Libs.guava) diff --git a/seqra-ir-core/src/main/kotlin/org/seqra/ir/impl/features/classpaths/KotlinMetadata.kt b/seqra-ir-core/src/main/kotlin/org/seqra/ir/impl/features/classpaths/KotlinMetadata.kt index 697344f..666a779 100644 --- a/seqra-ir-core/src/main/kotlin/org/seqra/ir/impl/features/classpaths/KotlinMetadata.kt +++ b/seqra-ir-core/src/main/kotlin/org/seqra/ir/impl/features/classpaths/KotlinMetadata.kt @@ -1,22 +1,35 @@ package org.seqra.ir.impl.features.classpaths +import mu.KLogging +import org.seqra.ir.api.jvm.JIRClassExtFeature +import org.seqra.ir.api.jvm.JIRClassOrInterface +import org.seqra.ir.api.jvm.ext.annotation import kotlin.metadata.KmConstructor import kotlin.metadata.KmFunction import kotlin.metadata.KmProperty import kotlin.metadata.KmTypeParameter import kotlin.metadata.jvm.KotlinClassMetadata -import org.seqra.ir.api.jvm.JIRClassExtFeature -import org.seqra.ir.api.jvm.JIRClassOrInterface -import org.seqra.ir.api.jvm.ext.annotation object KotlinMetadata : JIRClassExtFeature { const val METADATA_KEY = "kotlinClassMetadata" + private val logger = object : KLogging() {}.logger + + // avoid multiple reports of the same issue + private var failureReported = false + override fun extensionValuesOf(clazz: JIRClassOrInterface): Map? { - val kMetadata = clazz.kMetadata - if (kMetadata != null) { - return mapOf(METADATA_KEY to KotlinMetadataHolder(kMetadata)) + runCatching { + val kMetadata = clazz.kMetadata + if (kMetadata != null) { + return mapOf(METADATA_KEY to KotlinMetadataHolder(kMetadata)) + } + }.onFailure { ex -> + if (!failureReported) { + failureReported = true + logger.error(ex) { "Failed to load kotlin class metadata" } + } } return null diff --git a/seqra-ir-core/src/main/kotlin/org/seqra/ir/impl/fs/ByteCodeLocations.kt b/seqra-ir-core/src/main/kotlin/org/seqra/ir/impl/fs/ByteCodeLocations.kt index 6a4d6df..2291c2d 100644 --- a/seqra-ir-core/src/main/kotlin/org/seqra/ir/impl/fs/ByteCodeLocations.kt +++ b/seqra-ir-core/src/main/kotlin/org/seqra/ir/impl/fs/ByteCodeLocations.kt @@ -7,7 +7,7 @@ import java.io.File import java.nio.file.Paths import java.util.jar.JarFile -val logger = object : KLogging() {}.logger +private val logger = object : KLogging() {}.logger /** * Returns collection of `JIRByteCodeLocation` of a file or directory. @@ -15,7 +15,10 @@ val logger = object : KLogging() {}.logger * The method called of different files can have same locations in the result, so use `distinct()` to * filter duplicates out. */ -fun File.dirOrJarAsBytecodeLocation(runtimeVersion: JavaVersion, isRuntime: Boolean): Collection { +fun File.dirOrJarAsBytecodeLocation( + runtimeVersion: JavaVersion, + isRuntime: Boolean +): Collection = runCatching { if (isJar()) { return mutableSetOf().also { classPath(it) }.map { JarLocation(it, isRuntime, runtimeVersion) } } @@ -24,8 +27,11 @@ fun File.dirOrJarAsBytecodeLocation(runtimeVersion: JavaVersion, isRuntime: Bool return listOf(BuildFolderLocation(this)) } - error("$absolutePath is invalid bytecode location") -} + logger.error("Invalid bytecode location: $absolutePath") + return emptyList() +}.onFailure { + logger.error(it) { "Failed to read bytecode location" } +}.getOrNull() ?: emptyList() fun Collection.createNonRuntimeByteCodeLocations(runtimeVersion: JavaVersion): List = filterExisting() diff --git a/seqra-ir-core/src/main/kotlin/org/seqra/ir/impl/fs/JavaRuntime.kt b/seqra-ir-core/src/main/kotlin/org/seqra/ir/impl/fs/JavaRuntime.kt index bc99620..f831d60 100644 --- a/seqra-ir-core/src/main/kotlin/org/seqra/ir/impl/fs/JavaRuntime.kt +++ b/seqra-ir-core/src/main/kotlin/org/seqra/ir/impl/fs/JavaRuntime.kt @@ -1,5 +1,6 @@ package org.seqra.ir.impl.fs +import mu.KLogging import org.seqra.ir.api.jvm.JIRByteCodeLocation import org.seqra.ir.api.jvm.JavaVersion import java.io.File @@ -68,4 +69,8 @@ class JavaRuntime(private val javaHome: File) { .flatMap { it.dirOrJarAsBytecodeLocation(version, true) } .distinct() } + + companion object { + private val logger = object : KLogging() {}.logger + } } diff --git a/seqra-ir-core/src/main/kotlin/org/seqra/ir/impl/fs/JavaRuntimeModuleLocation.kt b/seqra-ir-core/src/main/kotlin/org/seqra/ir/impl/fs/JavaRuntimeModuleLocation.kt index ffac37c..c916e93 100644 --- a/seqra-ir-core/src/main/kotlin/org/seqra/ir/impl/fs/JavaRuntimeModuleLocation.kt +++ b/seqra-ir-core/src/main/kotlin/org/seqra/ir/impl/fs/JavaRuntimeModuleLocation.kt @@ -30,6 +30,7 @@ open class JavaRuntimeModuleLocation( override val currentHash: BigInteger get() { val hasher = Hashing.sha256().newHasher() + hasher.putString(module, UTF_8) useModule { moduleBase, moduleBasePath -> moduleBase.walk() .filter { it.isValidClassFile() } @@ -85,14 +86,13 @@ open class JavaRuntimeModuleLocation( return result } - private inline fun useModule(body: (Path, String) -> T): T { - val fs = newFs(javaHome) - ?: error("JRT file system not available") + private val fs by lazy { + newFs(javaHome) ?: error("JRT file system not available") + } - return fs.use { - val module = fs.getPath(MODULES, module) - body(module, "$module/") - } + private inline fun useModule(body: (Path, String) -> T): T { + val module = fs.getPath(MODULES, module) + return body(module, "$module/") } companion object {