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

Adding coroutines to analysis process #128

Open
wants to merge 2 commits into
base: master
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
2 changes: 2 additions & 0 deletions kotlin-bundled-compiler/.classpath
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
<classpath>
<classpathentry exported="true" kind="lib" path="lib/kotlin-scripting-impl.jar"/>
<classpathentry exported="true" kind="lib" path="lib/annotations-13.0.jar"/>
<classpathentry exported="true" kind="lib" path="lib/kotlinx-coroutines-core.jar"/>
<classpathentry exported="true" kind="lib" path="lib/kotlinx-coroutines-jdk8.jar"/>
<classpathentry exported="true" kind="lib" path="lib/ide-dependencies.jar"/>
<classpathentry exported="true" kind="lib" path="lib/kotlin-compiler.jar" sourcepath="lib/kotlin-compiler-sources.jar"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
Expand Down
10 changes: 8 additions & 2 deletions kotlin-bundled-compiler/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@ Bundle-ClassPath: .,
lib/kotlin-formatter.jar,
lib/ide-dependencies.jar,
lib/annotations-13.0.jar,
lib/kotlin-scripting-impl.jar
Export-Package:
lib/kotlin-scripting-impl.jar,
lib/kotlinx-coroutines-core.jar,
lib/kotlinx-coroutines-jdk8.jar
Export-Package:
com.intellij,
com.intellij.codeInsight,
com.intellij.codeInsight.completion.scope,
Expand Down Expand Up @@ -180,6 +182,9 @@ Export-Package:
kotlin.collections,
kotlin.comparisons,
kotlin.concurrent,
kotlin.coroutines,
kotlin.coroutines.intrinsics,
kotlin.coroutines.jvm.internal,
kotlin.internal;x-internal:=true,
kotlin.io,
kotlin.jvm,
Expand All @@ -197,6 +202,7 @@ Export-Package:
kotlin.script.templates.standard,
kotlin.sequences,
kotlin.text,
kotlinx.coroutines,
org.jetbrains.annotations,
org.jetbrains.kotlin,
org.jetbrains.kotlin.analyzer,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,44 +16,51 @@
*******************************************************************************/
package org.jetbrains.kotlin.core.model

import org.eclipse.jdt.core.IJavaProject
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.async
import kotlinx.coroutines.runBlocking
import org.jetbrains.kotlin.core.resolve.AnalysisResultWithProvider
import org.jetbrains.kotlin.core.resolve.EclipseAnalyzerFacadeForJVM
import org.jetbrains.kotlin.core.resolve.KotlinAnalysisScope
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.core.builder.KotlinPsiManager

data class FileAnalysisResults(val file: KtFile, val analysisResult: AnalysisResultWithProvider)
data class FileAnalysisResults(val file: KtFile, val analysisResult: Deferred<AnalysisResultWithProvider>)

public object KotlinAnalysisFileCache {
private @Volatile var lastAnalysedFileCache: FileAnalysisResults? = null
object KotlinAnalysisFileCache {

@Synchronized fun getAnalysisResult(file: KtFile): AnalysisResultWithProvider {
return getImmediatlyFromCache(file) ?: run {
@Volatile
private var lastAnalysedFileCache: FileAnalysisResults? = null

fun getAnalysisResult(file: KtFile): AnalysisResultWithProvider {
return runBlocking {
getImmediatelyFromCache(file)?.await()
} ?: runBlocking {
val environment = getEnvironment(file.project)!!
val analysisResult = resolve(file, environment)

lastAnalysedFileCache = FileAnalysisResults(file, analysisResult)
lastAnalysedFileCache!!.analysisResult
val analysisResult = KotlinAnalysisScope.async {
resolve(file, environment)
}
saveLastResult(file, analysisResult).analysisResult.await()
}
}

@Synchronized
private fun saveLastResult(file: KtFile, result: Deferred<AnalysisResultWithProvider>): FileAnalysisResults =
FileAnalysisResults(file, result).also {
lastAnalysedFileCache = it
}

fun resetCache() {
lastAnalysedFileCache = null
}

private fun resolve(file: KtFile, environment: KotlinCommonEnvironment): AnalysisResultWithProvider {
return when (environment) {
private fun resolve(file: KtFile, environment: KotlinCommonEnvironment): AnalysisResultWithProvider =
when (environment) {
is KotlinScriptEnvironment -> EclipseAnalyzerFacadeForJVM.analyzeScript(environment, file)
is KotlinEnvironment -> EclipseAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration(environment, listOf(file))
else -> throw IllegalArgumentException("Could not analyze file with environment: $environment")
}
}

@Synchronized
private fun getImmediatlyFromCache(file: KtFile): AnalysisResultWithProvider? {
return if (lastAnalysedFileCache != null && lastAnalysedFileCache!!.file == file)
lastAnalysedFileCache!!.analysisResult
else
null
}
private fun getImmediatelyFromCache (ktFile: KtFile) : Deferred<AnalysisResultWithProvider>? =
lastAnalysedFileCache?.takeIf { it.file == ktFile }?.analysisResult
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
*******************************************************************************/
package org.jetbrains.kotlin.core.model

import kotlinx.coroutines.Deferred
import kotlinx.coroutines.async
import kotlinx.coroutines.runBlocking
import org.eclipse.core.resources.IFile
import org.eclipse.core.resources.IProject
import org.eclipse.core.resources.IResourceChangeEvent
Expand All @@ -24,52 +27,52 @@ import org.eclipse.jdt.core.IJavaProject
import org.eclipse.jdt.core.JavaCore
import org.jetbrains.kotlin.analyzer.AnalysisResult
import org.jetbrains.kotlin.core.builder.KotlinPsiManager
import org.jetbrains.kotlin.core.resolve.EclipseAnalyzerFacadeForJVM
import org.jetbrains.kotlin.core.utils.ProjectUtils
import java.util.concurrent.ConcurrentHashMap
import org.jetbrains.kotlin.core.resolve.AnalysisResultWithProvider
import org.jetbrains.kotlin.core.resolve.KotlinAnalysisScope
import org.jetbrains.kotlin.core.resolve.KotlinAnalyzer
import java.util.concurrent.*

public object KotlinAnalysisProjectCache : IResourceChangeListener {
private val cachedAnalysisResults = ConcurrentHashMap<IProject, AnalysisResult>()
object KotlinAnalysisProjectCache : IResourceChangeListener {
private val cachedAnalysisResults = ConcurrentHashMap<IProject, Deferred<AnalysisResultWithProvider>>()

public fun resetCache(project: IProject) {
fun resetCache(project: IProject) {
synchronized(project) {
cachedAnalysisResults.remove(project)
cachedAnalysisResults.remove(project)?.cancel()
}
}

public fun resetAllCaches() {
fun resetAllCaches() {
cachedAnalysisResults.keys.toList().forEach {
resetCache(it)
}
}

public fun getAnalysisResult(javaProject: IJavaProject): AnalysisResult {
val project = javaProject.getProject()
return synchronized(project) {
val analysisResult = cachedAnalysisResults.get(project) ?: run {
EclipseAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration(
KotlinEnvironment.getEnvironment(project),
ProjectUtils.getSourceFiles(javaProject.getProject())).analysisResult
}

cachedAnalysisResults.putIfAbsent(project, analysisResult) ?: analysisResult
}
fun cancelablePostAnalysis(javaProject: IJavaProject, post: (AnalysisResult) -> Unit) {
try {
resetCache(javaProject.project)
post(getAnalysisResult(javaProject).analysisResult)
} catch (e: CancellationException) {}
}

public @Synchronized fun getAnalysisResultIfCached(project: IProject): AnalysisResult? {
return cachedAnalysisResults.get(project)
fun getAnalysisResult(javaProject: IJavaProject) : AnalysisResultWithProvider {
return runBlocking {
val project = javaProject.project
synchronized(this@KotlinAnalysisProjectCache) {
cachedAnalysisResults.getOrPut(project) { KotlinAnalyzer.analyzeProjectAsync(project) }
}.await()
}
}

override fun resourceChanged(event: IResourceChangeEvent) {
when (event.getType()) {
when (event.type) {
IResourceChangeEvent.PRE_DELETE,
IResourceChangeEvent.PRE_CLOSE,
IResourceChangeEvent.PRE_BUILD -> event.getDelta()?.accept { delta ->
val resource = delta.getResource()
IResourceChangeEvent.PRE_BUILD -> event.delta?.accept { delta ->
val resource = delta.resource
if (resource is IFile) {
val javaProject = JavaCore.create(resource.getProject())
if (KotlinPsiManager.isKotlinSourceFile(resource, javaProject)) {
cachedAnalysisResults.remove(resource.getProject())
resetCache(resource.getProject())
}

return@accept false
Expand All @@ -79,4 +82,4 @@ public object KotlinAnalysisProjectCache : IResourceChangeListener {
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ object EclipseAnalyzerFacadeForJVM {
}

val allFiles = LinkedHashSet<KtFile>(filesSet)
val addedFiles = filesSet.map { getPath(it) }.filterNotNull().toSet()
val addedFiles = filesSet.mapNotNull { getPath(it) }.toSet()
ProjectUtils.getSourceFilesWithDependencies(environment.javaProject).filterNotTo(allFiles) {
getPath(it) in addedFiles
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.jetbrains.kotlin.core.resolve

import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.joinAll
import kotlinx.coroutines.runBlocking
import kotlin.coroutines.CoroutineContext

object KotlinAnalysisScope : CoroutineScope {

private val job: Job by lazy { SupervisorJob() }

override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Default

@JvmStatic
fun join() = runBlocking {
job.children.toList().joinAll()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,13 @@
*******************************************************************************/
package org.jetbrains.kotlin.core.resolve

import kotlinx.coroutines.Deferred
import kotlinx.coroutines.async
import org.eclipse.core.resources.IProject
import org.jetbrains.kotlin.core.model.KotlinAnalysisFileCache
import org.jetbrains.kotlin.core.model.KotlinEnvironment
import org.jetbrains.kotlin.core.model.getEnvironment
import org.jetbrains.kotlin.core.utils.ProjectUtils
import org.jetbrains.kotlin.psi.KtFile

object KotlinAnalyzer {
Expand All @@ -34,27 +37,25 @@ object KotlinAnalyzer {
files.size == 1 -> analyzeFile(files.single())

else -> {
val environment = getEnvironment(files.first().project)
if (environment == null) {
val environment = getEnvironment(files.first().project) ?:
throw IllegalStateException("There is no environment for project: ${files.first().project}")
}

if (environment !is KotlinEnvironment) {

environment as? KotlinEnvironment ?:
throw IllegalStateException("Only KotlinEnvironment can be used to analyze several files")
}


analyzeFiles(environment, files)
}
}
}

fun analyzeProject(eclipseProject: IProject): AnalysisResultWithProvider {
val environment = KotlinEnvironment.getEnvironment(eclipseProject)
return analyzeFiles(environment, emptyList())
}

fun analyzeProjectAsync(project: IProject) : Deferred<AnalysisResultWithProvider> =
KotlinAnalysisScope.async {
analyzeFiles(
KotlinEnvironment.getEnvironment(project),
ProjectUtils.getSourceFiles(project))
}

private fun analyzeFiles(kotlinEnvironment: KotlinEnvironment,
filesToAnalyze: Collection<KtFile>): AnalysisResultWithProvider {
return EclipseAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration(kotlinEnvironment, filesToAnalyze)
}
filesToAnalyze: Collection<KtFile>): AnalysisResultWithProvider =
EclipseAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration(kotlinEnvironment, filesToAnalyze)
}
Loading