Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cache Symbol Index #489

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
8 changes: 5 additions & 3 deletions server/src/main/kotlin/org/javacs/kt/CompilerClassPath.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class CompilerClassPath(private val config: CompilerConfiguration, private val d
val classPath = mutableSetOf<ClassPathEntry>()
val outputDirectory: File = Files.createTempDirectory("klsBuildOutput").toFile()
val javaHome: String? = System.getProperty("java.home", null)
var classpathCached = false

var compiler = Compiler(javaSourcePath, classPath.map { it.compiledJar }.toSet(), buildScriptClassPath, outputDirectory)
private set
Expand All @@ -45,17 +46,18 @@ class CompilerClassPath(private val config: CompilerConfiguration, private val d

if (updateClassPath) {
val newClassPath = resolver.classpathOrEmpty
if (newClassPath != classPath) {
if (newClassPath.entries != classPath) {
synchronized(classPath) {
syncPaths(classPath, newClassPath, "class path") { it.compiledJar }
syncPaths(classPath, newClassPath.entries, "class path") { it.compiledJar }
}
classpathCached = newClassPath.cacheHit
refreshCompiler = true
}

async.compute {
val newClassPathWithSources = resolver.classpathWithSources
synchronized(classPath) {
syncPaths(classPath, newClassPathWithSources, "class path with sources") { it.compiledJar }
syncPaths(classPath, newClassPathWithSources.entries, "class path with sources") { it.compiledJar }
}
}
}
Expand Down
5 changes: 2 additions & 3 deletions server/src/main/kotlin/org/javacs/kt/SourcePath.kt
Original file line number Diff line number Diff line change
Expand Up @@ -290,8 +290,6 @@ class SourcePath(
}

fun refreshDependencyIndexes() {
compileAllFiles()

val module = files.values.first { it.module != null }.module
if (module != null) {
refreshDependencyIndexes(module)
Expand All @@ -315,7 +313,7 @@ class SourcePath(
* Refreshes the indexes. If already done, refreshes only the declarations in the files that were changed.
*/
private fun refreshDependencyIndexes(module: ModuleDescriptor) = indexAsync.execute {
if (indexEnabled) {
if (indexEnabled && !cp.classpathCached) {
val declarations = getDeclarationDescriptors(files.values)
index.refresh(module, declarations)
}
Expand Down Expand Up @@ -344,6 +342,7 @@ class SourcePath(
LOG.info("Refreshing source path")
files.values.forEach { it.clean() }
files.values.forEach { it.compile() }
refreshDependencyIndexes()
}
}

Expand Down
10 changes: 4 additions & 6 deletions server/src/main/kotlin/org/javacs/kt/index/SymbolIndex.kt
Original file line number Diff line number Diff line change
Expand Up @@ -85,17 +85,15 @@ class SymbolIndex(
private val databaseService: DatabaseService
) {
private val db: Database by lazy {
databaseService.db ?: Database.connect("jdbc:h2:mem:symbolindex;DB_CLOSE_DELAY=-1", "org.h2.Driver")
}

var progressFactory: Progress.Factory = Progress.Factory.None

init {
val db = databaseService.db ?: Database.connect("jdbc:h2:mem:symbolindex;DB_CLOSE_DELAY=-1", "org.h2.Driver")
transaction(db) {
SchemaUtils.createMissingTablesAndColumns(Symbols, Locations, Ranges, Positions)
}
db
}

var progressFactory: Progress.Factory = Progress.Factory.None

/** Rebuilds the entire index. May take a while. */
fun refresh(module: ModuleDescriptor, exclusions: Sequence<DeclarationDescriptor>) {
val started = System.currentTimeMillis()
Expand Down
4 changes: 2 additions & 2 deletions server/src/test/kotlin/org/javacs/kt/ClassPathTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class ClassPathTest {

val resolvers = defaultClassPathResolver(listOf(workspaceRoot))
print(resolvers)
val classPath = resolvers.classpathOrEmpty.map { it.toString() }
val classPath = resolvers.classpathOrEmpty.entries.map { it.toString() }

assertThat(classPath, hasItem(containsString("junit")))
}
Expand All @@ -36,7 +36,7 @@ class ClassPathTest {

val resolvers = defaultClassPathResolver(listOf(workspaceRoot))
print(resolvers)
val classPath = resolvers.classpathOrEmpty.map { it.toString() }
val classPath = resolvers.classpathOrEmpty.entries.map { it.toString() }

assertThat(classPath, hasItem(containsString("junit")))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import java.nio.file.Paths
/** Backup classpath that find Kotlin in the user's Maven/Gradle home or kotlinc's libraries folder. */
object BackupClassPathResolver : ClassPathResolver {
override val resolverType: String = "Backup"
override val classpath: Set<ClassPathEntry> get() = findKotlinStdlib()?.let { setOf(it) }.orEmpty().map { ClassPathEntry(it, null) }.toSet()
override val classpath: ClassPathResult get() = ClassPathResult(findKotlinStdlib()?.let { setOf(it) }.orEmpty().map { ClassPathEntry(it, null) }.toSet())
}

fun findKotlinStdlib(): Path? =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,16 +107,16 @@ internal class CachedClassPathResolver(
}
}

override val classpath: Set<ClassPathEntry> get() {
override val classpath: ClassPathResult get() {
cachedClassPathEntries.let { if (!dependenciesChanged()) {
LOG.info("Classpath has not changed. Fetching from cache")
return it
return ClassPathResult(it, cacheHit = true)
} }

LOG.info("Cached classpath is outdated or not found. Resolving again")

val newClasspath = wrapped.classpath
updateClasspathCache(newClasspath, false)
updateClasspathCache(newClasspath.entries, false)

return newClasspath
}
Expand All @@ -135,11 +135,13 @@ internal class CachedClassPathResolver(
return newBuildScriptClasspath
}

override val classpathWithSources: Set<ClassPathEntry> get() {
cachedClassPathMetadata?.let { if (!dependenciesChanged() && it.includesSources) return cachedClassPathEntries }
override val classpathWithSources: ClassPathResult get() {
cachedClassPathMetadata?.let { if (!dependenciesChanged() && it.includesSources) {
return ClassPathResult(cachedClassPathEntries, cacheHit = true)
} }

val newClasspath = wrapped.classpathWithSources
updateClasspathCache(newClasspath, true)
updateClasspathCache(newClasspath.entries, true)

return newClasspath
}
Expand Down
24 changes: 12 additions & 12 deletions shared/src/main/kotlin/org/javacs/kt/classpath/ClassPathResolver.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ import kotlin.math.max
interface ClassPathResolver {
val resolverType: String

val classpath: Set<ClassPathEntry> // may throw exceptions
val classpathOrEmpty: Set<ClassPathEntry> // does not throw exceptions
val classpath: ClassPathResult // may throw exceptions
val classpathOrEmpty: ClassPathResult // does not throw exceptions
get() = try {
classpath
} catch (e: Exception) {
LOG.warn("Could not resolve classpath using {}: {}", resolverType, e.message)
emptySet<ClassPathEntry>()
ClassPathResult(emptySet<ClassPathEntry>())
}

val buildScriptClasspath: Set<Path>
Expand All @@ -27,7 +27,7 @@ interface ClassPathResolver {
emptySet<Path>()
}

val classpathWithSources: Set<ClassPathEntry> get() = classpath
val classpathWithSources: ClassPathResult get() = classpath

/**
* This should return the current build file version.
Expand All @@ -43,7 +43,7 @@ interface ClassPathResolver {
/** A default empty classpath implementation */
val empty = object : ClassPathResolver {
override val resolverType = "[]"
override val classpath = emptySet<ClassPathEntry>()
override val classpath = ClassPathResult(emptySet<ClassPathEntry>())
}
}
}
Expand All @@ -61,22 +61,22 @@ infix fun ClassPathResolver.or(other: ClassPathResolver): ClassPathResolver = Fi
/** The union of two class path resolvers. */
internal class UnionClassPathResolver(val lhs: ClassPathResolver, val rhs: ClassPathResolver) : ClassPathResolver {
override val resolverType: String get() = "(${lhs.resolverType} + ${rhs.resolverType})"
override val classpath get() = lhs.classpath + rhs.classpath
override val classpathOrEmpty get() = lhs.classpathOrEmpty + rhs.classpathOrEmpty
override val classpath get() = ClassPathResult(lhs.classpath.entries + rhs.classpath.entries)
override val classpathOrEmpty get() = ClassPathResult(lhs.classpathOrEmpty.entries + rhs.classpathOrEmpty.entries)
override val buildScriptClasspath get() = lhs.buildScriptClasspath + rhs.buildScriptClasspath
override val buildScriptClasspathOrEmpty get() = lhs.buildScriptClasspathOrEmpty + rhs.buildScriptClasspathOrEmpty
override val classpathWithSources get() = lhs.classpathWithSources + rhs.classpathWithSources
override val classpathWithSources get() = ClassPathResult(lhs.classpathWithSources.entries + rhs.classpathWithSources.entries)
override val currentBuildFileVersion: Long get() = max(lhs.currentBuildFileVersion, rhs.currentBuildFileVersion)
}

internal class FirstNonEmptyClassPathResolver(val lhs: ClassPathResolver, val rhs: ClassPathResolver) : ClassPathResolver {
override val resolverType: String get() = "(${lhs.resolverType} or ${rhs.resolverType})"
override val classpath get() = lhs.classpath.takeIf { it.isNotEmpty() } ?: rhs.classpath
override val classpathOrEmpty get() = lhs.classpathOrEmpty.takeIf { it.isNotEmpty() } ?: rhs.classpathOrEmpty
override val classpath get() = ClassPathResult(lhs.classpath.entries.takeIf { it.isNotEmpty() } ?: rhs.classpath.entries)
override val classpathOrEmpty get() = ClassPathResult(lhs.classpathOrEmpty.entries.takeIf { it.isNotEmpty() } ?: rhs.classpathOrEmpty.entries)
override val buildScriptClasspath get() = lhs.buildScriptClasspath.takeIf { it.isNotEmpty() } ?: rhs.buildScriptClasspath
override val buildScriptClasspathOrEmpty get() = lhs.buildScriptClasspathOrEmpty.takeIf { it.isNotEmpty() } ?: rhs.buildScriptClasspathOrEmpty
override val classpathWithSources get() = lhs.classpathWithSources.takeIf {
override val classpathWithSources get() = ClassPathResult(lhs.classpathWithSources.entries.takeIf {
it.isNotEmpty()
} ?: rhs.classpathWithSources
} ?: rhs.classpathWithSources.entries)
override val currentBuildFileVersion: Long get() = max(lhs.currentBuildFileVersion, rhs.currentBuildFileVersion)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package org.javacs.kt.classpath

data class ClassPathResult(
val entries: Set<ClassPathEntry>,
val cacheHit: Boolean = false
)
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ internal class GradleClassPathResolver(private val path: Path, private val inclu
override val resolverType: String = "Gradle"
private val projectDirectory: Path get() = path.parent

override val classpath: Set<ClassPathEntry> get() {
override val classpath: ClassPathResult get() {
val scripts = listOf("projectClassPathFinder.gradle")
val tasks = listOf("kotlinLSPProjectDeps")

return readDependenciesViaGradleCLI(projectDirectory, scripts, tasks)
return ClassPathResult(readDependenciesViaGradleCLI(projectDirectory, scripts, tasks)
.apply { if (isNotEmpty()) LOG.info("Successfully resolved dependencies for '${projectDirectory.fileName}' using Gradle") }
.map { ClassPathEntry(it, null) }.toSet()
.map { ClassPathEntry(it, null) }.toSet())
}
override val buildScriptClasspath: Set<Path> get() {
return if (includeKotlinDSL) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ internal class MavenClassPathResolver private constructor(private val pom: Path)

override val resolverType: String = "Maven"

override val classpath: Set<ClassPathEntry> get() {
override val classpath: ClassPathResult get() {
val dependenciesOutput = generateMavenDependencyList(pom)
val artifacts = readMavenDependencyList(dependenciesOutput)

Expand All @@ -27,10 +27,10 @@ internal class MavenClassPathResolver private constructor(private val pom: Path)
Files.deleteIfExists(dependenciesOutput)

this.artifacts = artifacts
return artifacts.mapNotNull { findMavenArtifact(it, false)?.let { it1 -> ClassPathEntry(it1, null) } }.toSet()
return ClassPathResult(artifacts.mapNotNull { findMavenArtifact(it, false)?.let { it1 -> ClassPathEntry(it1, null) } }.toSet())
}

override val classpathWithSources: Set<ClassPathEntry> get() {
override val classpathWithSources: ClassPathResult get() {
// Fetch artifacts if not yet present.
var artifacts: Set<Artifact>
if (this.artifacts != null) {
Expand All @@ -47,11 +47,11 @@ internal class MavenClassPathResolver private constructor(private val pom: Path)
artifacts = readMavenDependencyListWithSources(artifacts, sourcesOutput)

Files.deleteIfExists(sourcesOutput)
return artifacts.mapNotNull {
return ClassPathResult(artifacts.mapNotNull {
findMavenArtifact(it, false)?.let {
it1 -> ClassPathEntry(it1, if (it.source) findMavenArtifact(it, it.source) else null)
}
}.toSet()
}.toSet())
}

override val currentBuildFileVersion: Long get() = pom.toFile().lastModified()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,19 @@ internal class ShellClassPathResolver(
private val workingDir: Path? = null
) : ClassPathResolver {
override val resolverType: String = "Shell"
override val classpath: Set<ClassPathEntry> get() {
override val classpath: ClassPathResult get() {
val workingDirectory = workingDir?.toFile() ?: script.toAbsolutePath().parent.toFile()
val cmd = script.toString()
LOG.info("Run {} in {}", cmd, workingDirectory)
val process = ProcessBuilder(cmd).directory(workingDirectory).start()

return process.inputStream.bufferedReader().readText()
return ClassPathResult(process.inputStream.bufferedReader().readText()
.split(File.pathSeparator)
.asSequence()
.map { it.trim() }
.filter { it.isNotEmpty() }
.map { ClassPathEntry(Paths.get(it), null) }
.toSet()
.toSet())
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@ import java.nio.file.Path
/** A classpath resolver that ensures another resolver contains the stdlib */
internal class WithStdlibResolver(private val wrapped: ClassPathResolver) : ClassPathResolver {
override val resolverType: String get() = "Stdlib + ${wrapped.resolverType}"
override val classpath: Set<ClassPathEntry> get() = wrapWithStdlibEntries(wrapped.classpath)
override val classpathOrEmpty: Set<ClassPathEntry> get() = wrapWithStdlibEntries(wrapped.classpathOrEmpty)
override val classpath: ClassPathResult get() = wrapWithStdlibEntries(wrapped.classpath.entries)
override val classpathOrEmpty: ClassPathResult get() = wrapWithStdlibEntries(wrapped.classpathOrEmpty.entries)
override val buildScriptClasspath: Set<Path> get() = wrapWithStdlib(wrapped.buildScriptClasspath)
override val buildScriptClasspathOrEmpty: Set<Path> get() = wrapWithStdlib(wrapped.buildScriptClasspathOrEmpty)
override val classpathWithSources: Set<ClassPathEntry> get() = wrapWithStdlibEntries(wrapped.classpathWithSources)
override val classpathWithSources: ClassPathResult get() = wrapWithStdlibEntries(wrapped.classpathWithSources.entries)
override val currentBuildFileVersion: Long get() = wrapped.currentBuildFileVersion
}

private fun wrapWithStdlibEntries(paths: Set<ClassPathEntry>): Set<ClassPathEntry> {
return wrapWithStdlib(paths.map { it.compiledJar }.toSet()).map { ClassPathEntry(it, paths.find { it1 -> it1.compiledJar == it }?.sourceJar) }.toSet()
private fun wrapWithStdlibEntries(paths: Set<ClassPathEntry>): ClassPathResult {
return ClassPathResult(wrapWithStdlib(paths.map { it.compiledJar }.toSet()).map { ClassPathEntry(it, paths.find { it1 -> it1.compiledJar == it }?.sourceJar) }.toSet())
}

private fun wrapWithStdlib(paths: Set<Path>): Set<Path> {
Expand Down