From d99f96ea4f48babcc0bbbc724a6b04343a4782c1 Mon Sep 17 00:00:00 2001 From: Akash Yadav Date: Tue, 9 Jun 2026 00:00:43 +0530 Subject: [PATCH 1/4] fix: use custom Logger for Kotlin compiler Signed-off-by: Akash Yadav --- .../AbstractCompilationEnvironment.kt | 5 +- .../lsp/kotlin/compiler/util/SLF4JLogger.kt | 57 +++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 lsp/kotlin/src/main/java/com/itsaky/androidide/lsp/kotlin/compiler/util/SLF4JLogger.kt diff --git a/lsp/kotlin/src/main/java/com/itsaky/androidide/lsp/kotlin/compiler/AbstractCompilationEnvironment.kt b/lsp/kotlin/src/main/java/com/itsaky/androidide/lsp/kotlin/compiler/AbstractCompilationEnvironment.kt index dce87b311f..4a19f3de9f 100644 --- a/lsp/kotlin/src/main/java/com/itsaky/androidide/lsp/kotlin/compiler/AbstractCompilationEnvironment.kt +++ b/lsp/kotlin/src/main/java/com/itsaky/androidide/lsp/kotlin/compiler/AbstractCompilationEnvironment.kt @@ -4,12 +4,12 @@ import com.itsaky.androidide.lsp.kotlin.compiler.index.KtSymbolIndex import com.itsaky.androidide.lsp.kotlin.compiler.modules.KtModule import com.itsaky.androidide.lsp.kotlin.compiler.modules.asFlatSequence import com.itsaky.androidide.lsp.kotlin.compiler.modules.isSourceModule -import com.itsaky.androidide.lsp.kotlin.compiler.registrar.AnalysisApiServiceProvider import com.itsaky.androidide.lsp.kotlin.compiler.services.JavaModuleAccessibilityChecker import com.itsaky.androidide.lsp.kotlin.compiler.services.JavaModuleAnnotationsProvider import com.itsaky.androidide.lsp.kotlin.compiler.services.KtLspService import com.itsaky.androidide.lsp.kotlin.compiler.services.WriteAccessGuard import com.itsaky.androidide.lsp.kotlin.compiler.services.latestLanguageVersionSettings +import com.itsaky.androidide.lsp.kotlin.compiler.util.SLF4JLogger import org.jetbrains.kotlin.K1Deprecation import org.jetbrains.kotlin.analysis.api.platform.declarations.KotlinAnnotationsResolverFactory import org.jetbrains.kotlin.analysis.api.platform.declarations.KotlinDeclarationProviderFactory @@ -51,6 +51,7 @@ import org.jetbrains.kotlin.com.intellij.core.CorePackageIndex import org.jetbrains.kotlin.com.intellij.ide.highlighter.JavaFileType import org.jetbrains.kotlin.com.intellij.mock.MockApplication import org.jetbrains.kotlin.com.intellij.mock.MockProject +import org.jetbrains.kotlin.com.intellij.openapi.diagnostic.Logger import org.jetbrains.kotlin.com.intellij.openapi.editor.impl.DocumentWriteAccessGuard import org.jetbrains.kotlin.com.intellij.openapi.roots.PackageIndex import org.jetbrains.kotlin.com.intellij.openapi.util.Disposer @@ -99,6 +100,8 @@ internal abstract class AbstractCompilationEnvironment( init { System.setProperty("java.awt.headless", "true") setupIdeaStandaloneExecution() + + Logger.setFactory { name -> SLF4JLogger(name) } } } diff --git a/lsp/kotlin/src/main/java/com/itsaky/androidide/lsp/kotlin/compiler/util/SLF4JLogger.kt b/lsp/kotlin/src/main/java/com/itsaky/androidide/lsp/kotlin/compiler/util/SLF4JLogger.kt new file mode 100644 index 0000000000..dac66e7160 --- /dev/null +++ b/lsp/kotlin/src/main/java/com/itsaky/androidide/lsp/kotlin/compiler/util/SLF4JLogger.kt @@ -0,0 +1,57 @@ +package com.itsaky.androidide.lsp.kotlin.compiler.util + +import org.jetbrains.kotlin.com.intellij.openapi.diagnostic.Logger +import org.slf4j.LoggerFactory + +class SLF4JLogger(name: String) : Logger() { + private val logger = LoggerFactory.getLogger(name) + + override fun isDebugEnabled(): Boolean { + return logger.isDebugEnabled + } + + override fun debug(message: String?, t: Throwable?) { + if (t != null) { + logger.debug(message ?: "", t) + } else { + logger.debug(message ?: "") + } + } + + override fun info(message: String?, t: Throwable?) { + if (t != null) { + logger.info(message ?: "", t) + } else { + logger.info(message ?: "") + } + } + + override fun warn(message: String?, t: Throwable?) { + if (t != null) { + logger.warn(message ?: "", t) + } else { + logger.warn(message ?: "") + } + } + + override fun error(message: String?, t: Throwable?, vararg details: String?) { + val msg = message ?: "" + val detailStr = if (details.isNotEmpty()) { + details.filterNotNull().joinToString(System.lineSeparator()) + } else { + "" + } + + val fullMessage = if (detailStr.isNotEmpty()) { + if (msg.isNotEmpty()) "$msg: $detailStr" else detailStr + } else { + msg + } + + if (t != null) { + logger.error(fullMessage, t) + } else { + logger.error(fullMessage) + } + } +} From b38ce78ade15e6413dbb0ee720be7a8d913d3083 Mon Sep 17 00:00:00 2001 From: Akash Yadav Date: Tue, 23 Jun 2026 19:29:14 +0530 Subject: [PATCH 2/4] fix: increase drain timeout to 2 seconds Signed-off-by: Akash Yadav --- .../lsp/kotlin/compiler/AbstractCompilationEnvironment.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lsp/kotlin/src/main/java/com/itsaky/androidide/lsp/kotlin/compiler/AbstractCompilationEnvironment.kt b/lsp/kotlin/src/main/java/com/itsaky/androidide/lsp/kotlin/compiler/AbstractCompilationEnvironment.kt index 0a51aa185a..178ebe3d29 100644 --- a/lsp/kotlin/src/main/java/com/itsaky/androidide/lsp/kotlin/compiler/AbstractCompilationEnvironment.kt +++ b/lsp/kotlin/src/main/java/com/itsaky/androidide/lsp/kotlin/compiler/AbstractCompilationEnvironment.kt @@ -80,7 +80,7 @@ import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil import org.jetbrains.kotlin.psi.KtPsiFactory import java.nio.file.Path import kotlin.io.path.pathString -import kotlin.time.Duration.Companion.milliseconds +import kotlin.time.Duration.Companion.seconds /** * Base class shared by [CompilationEnvironment] (production) and the test-only @@ -101,7 +101,7 @@ internal abstract class AbstractCompilationEnvironment( companion object { /** Max time close() will block the (main) thread draining background workers before disposal. */ - val CLOSE_DRAIN_TIMEOUT = 2.milliseconds + val CLOSE_DRAIN_TIMEOUT = 2.seconds init { System.setProperty("java.awt.headless", "true") From a37531041ff5ac959ad1875b8cf531fc712d1f1c Mon Sep 17 00:00:00 2001 From: Akash Yadav Date: Tue, 23 Jun 2026 20:09:14 +0530 Subject: [PATCH 3/4] fix: compilation error Signed-off-by: Akash Yadav --- .../androidide/lsp/kotlin/compiler/CompilationEnvironment.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lsp/kotlin/src/main/java/com/itsaky/androidide/lsp/kotlin/compiler/CompilationEnvironment.kt b/lsp/kotlin/src/main/java/com/itsaky/androidide/lsp/kotlin/compiler/CompilationEnvironment.kt index 0702aa0694..1db2911f30 100644 --- a/lsp/kotlin/src/main/java/com/itsaky/androidide/lsp/kotlin/compiler/CompilationEnvironment.kt +++ b/lsp/kotlin/src/main/java/com/itsaky/androidide/lsp/kotlin/compiler/CompilationEnvironment.kt @@ -307,7 +307,7 @@ internal class CompilationEnvironment( // super.close() disposes the project, so an in-flight read can't touch a disposed project // (APPDEVFORALL-17R / ADFA-4384). Bounded so a slow read can't block shutdown indefinitely. runBlocking { - withTimeoutOrNull(CLOSE_DRAIN_TIMEOUT_MS) { + withTimeoutOrNull(CLOSE_DRAIN_TIMEOUT) { coroutineScope.coroutineContext[Job]?.cancelAndJoin() } } From 9e1fc29c7f8264f43c358a45969ccbdf5178fb73 Mon Sep 17 00:00:00 2001 From: Akash Yadav Date: Tue, 23 Jun 2026 20:18:13 +0530 Subject: [PATCH 4/4] fix: add test case to verify fix Signed-off-by: Akash Yadav --- .../kotlin/compiler/util/SLF4JLoggerTest.kt | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 lsp/kotlin/src/test/java/com/itsaky/androidide/lsp/kotlin/compiler/util/SLF4JLoggerTest.kt diff --git a/lsp/kotlin/src/test/java/com/itsaky/androidide/lsp/kotlin/compiler/util/SLF4JLoggerTest.kt b/lsp/kotlin/src/test/java/com/itsaky/androidide/lsp/kotlin/compiler/util/SLF4JLoggerTest.kt new file mode 100644 index 0000000000..bd89089b76 --- /dev/null +++ b/lsp/kotlin/src/test/java/com/itsaky/androidide/lsp/kotlin/compiler/util/SLF4JLoggerTest.kt @@ -0,0 +1,24 @@ +package com.itsaky.androidide.lsp.kotlin.compiler.util + +import com.google.common.truth.Truth.assertThat +import com.itsaky.androidide.lsp.kotlin.fixtures.KtLspTest +import org.jetbrains.kotlin.com.intellij.openapi.diagnostic.Logger +import org.junit.Test + +class SLF4JLoggerTest : KtLspTest() { + + @Test + fun `compiler logger factory routes to SLF4JLogger`() { + assertThat(Logger.getInstance("ADFA-4238")).isInstanceOf(SLF4JLogger::class.java) + } + + @Test + fun `error does not throw - regression guard for ADFA-4238`() { + // DefaultLogger.error() rethrows as AssertionError, escalating a recoverable FIR cache + // inconsistency into a crash (Sentry APPDEVFORALL-13D). SLF4JLogger must log, not throw. + val logger = Logger.getInstance("ADFA-4238") + logger.error("Inconsistency in the cache. Someone without context put a null value in the cache") + logger.error("with throwable", RuntimeException("boom")) + logger.error("with details", RuntimeException(), "detail-1", "detail-2") + } +}