Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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 kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withTimeoutOrNull
import org.jetbrains.kotlin.K1Deprecation
Expand Down Expand Up @@ -53,6 +53,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
Expand All @@ -79,6 +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.seconds

/**
* Base class shared by [CompilationEnvironment] (production) and the test-only
Expand All @@ -99,11 +101,13 @@ internal abstract class AbstractCompilationEnvironment(

companion object {
/** Max time close() will block the (main) thread draining background workers before disposal. */
const val CLOSE_DRAIN_TIMEOUT_MS = 2_000L
val CLOSE_DRAIN_TIMEOUT = 2.seconds

init {
System.setProperty("java.awt.headless", "true")
setupIdeaStandaloneExecution()

Logger.setFactory { name -> SLF4JLogger(name) }
}
}

Expand Down Expand Up @@ -345,7 +349,7 @@ internal abstract class AbstractCompilationEnvironment(
// teardown, so the join is bounded by a timeout to avoid an ANR if a read is slow; the
// project.isDisposed guards cover the rare case where the timeout fires before draining.
if (::ktSymbolIndex.isInitialized) {
runBlocking { withTimeoutOrNull(CLOSE_DRAIN_TIMEOUT_MS) { ktSymbolIndex.close() } }
runBlocking { withTimeoutOrNull(CLOSE_DRAIN_TIMEOUT) { ktSymbolIndex.close() } }
}

Disposer.dispose(disposable)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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)
}
}
}
Original file line number Diff line number Diff line change
@@ -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")
}
}
Loading