Skip to content

Commit

Permalink
Merge pull request #641 from zhelenskiy/zhelenskiy/remove_internals
Browse files Browse the repository at this point in the history
Replace compiler internal API with compiler main function call besides completion
  • Loading branch information
zoobestik authored Nov 23, 2023
2 parents bb899c9 + 8c149f2 commit 2d3e522
Show file tree
Hide file tree
Showing 20 changed files with 535 additions and 343 deletions.
26 changes: 2 additions & 24 deletions common/src/main/kotlin/component/KotlinEnvironment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,7 @@ import com.intellij.openapi.util.Disposer
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
import org.jetbrains.kotlin.cli.common.arguments.parseCommandLineArguments
import org.jetbrains.kotlin.cli.common.createPhaseConfig
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSourceLocation
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.cli.js.K2JsIrCompiler
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots
Expand All @@ -18,7 +14,6 @@ import org.jetbrains.kotlin.config.CommonConfigurationKeys
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.config.languageVersionSettings
import org.jetbrains.kotlin.ir.backend.js.jsPhases
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
import org.jetbrains.kotlin.serialization.js.JsModuleDescriptor
import org.jetbrains.kotlin.serialization.js.KotlinJavascriptSerializationUtil
Expand All @@ -39,15 +34,14 @@ class KotlinEnvironment(
* See [org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments] and
* [org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments] for list of possible flags
*/
private val additionalCompilerArguments: List<String> = listOf(
val additionalCompilerArguments: List<String> = listOf(
"-opt-in=kotlin.ExperimentalStdlibApi",
"-opt-in=kotlin.time.ExperimentalTime",
"-opt-in=kotlin.RequiresOptIn",
"-opt-in=kotlin.ExperimentalUnsignedTypes",
"-opt-in=kotlin.contracts.ExperimentalContracts",
"-opt-in=kotlin.experimental.ExperimentalTypeInference",
"-Xcontext-receivers",
"-XXLanguage:+RangeUntilOperator"
)
}

Expand Down Expand Up @@ -81,22 +75,6 @@ class KotlinEnvironment(
put(JSConfigurationKeys.WASM_ENABLE_ASSERTS, false)
}

private val messageCollector = object : MessageCollector {
override fun clear() {}
override fun hasErrors(): Boolean {
return false
}

override fun report(
severity: CompilerMessageSeverity,
message: String,
location: CompilerMessageSourceLocation?
) {
}
}

val jsIrPhaseConfig = createPhaseConfig(jsPhases, K2JsIrCompiler().createArguments(), messageCollector)

private val environment = KotlinCoreEnvironment.createForProduction(
parentDisposable = Disposer.newDisposable(),
configuration = configuration.copy(),
Expand Down Expand Up @@ -128,4 +106,4 @@ class KotlinEnvironment(
}
}
}
}
}
119 changes: 119 additions & 0 deletions src/main/kotlin/com/compiler/server/compiler/components/CliUtils.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package com.compiler.server.compiler.components

import com.compiler.server.model.CompilerDiagnostics
import com.compiler.server.model.ErrorDescriptor
import com.compiler.server.model.ProjectSeveriry
import com.compiler.server.model.TextInterval
import org.jetbrains.kotlin.cli.common.CLICompiler
import org.jetbrains.kotlin.cli.common.CLITool
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.*
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSourceLocation
import org.jetbrains.kotlin.cli.common.messages.MessageRenderer
import org.jetbrains.kotlin.psi.KtFile
import java.nio.file.Path
import java.nio.file.Paths
import java.util.*
import kotlin.io.path.*


sealed class CompilationResult<out T> {
abstract val compilerDiagnostics: CompilerDiagnostics

fun <R> flatMap(action: (T) -> CompilationResult<R>): CompilationResult<R> = when (this) {
is Compiled -> {
val innerResult = action(result)
val newDiagnostics = (compilerDiagnostics.map.keys + innerResult.compilerDiagnostics.map.keys).associateWith {
val l1 = compilerDiagnostics.map[it]
val l2 = innerResult.compilerDiagnostics.map[it]
if (l1 != null && l2 != null) l1 + l2 else (l1 ?: l2)!!
}.let(::CompilerDiagnostics)
when (innerResult) {
is Compiled -> innerResult.copy(compilerDiagnostics = newDiagnostics)
is NotCompiled -> innerResult.copy(compilerDiagnostics = newDiagnostics)
}
}
is NotCompiled -> this
}
fun <R> map(action: (T) -> R): CompilationResult<R> = when (this) {
is Compiled -> Compiled(compilerDiagnostics, action(result))
is NotCompiled -> this
}
}

data class Compiled<T>(override val compilerDiagnostics: CompilerDiagnostics, val result: T) : CompilationResult<T>()

data class NotCompiled(override val compilerDiagnostics: CompilerDiagnostics) : CompilationResult<Nothing>()

fun CLICompiler<*>.tryCompilation(arguments: List<String>): CompilationResult<Unit> = tryCompilation(arguments) {}
fun <T> CLICompiler<*>.tryCompilation(arguments: List<String>, onSuccess: () -> T): CompilationResult<T> {
val diagnosticsMap = mutableMapOf<String?, MutableList<ErrorDescriptor>>()
val exitCode = CLITool.doMainNoExit(this, arguments.toTypedArray(), object : MessageRenderer {
override fun renderPreamble(): String = ""

override fun render(
severity: CompilerMessageSeverity,
message: String,
location: CompilerMessageSourceLocation?
): String {
val textInterval = location?.let {
TextInterval(
start = TextInterval.TextPosition(location.line, location.column),
end = TextInterval.TextPosition(location.lineEnd, location.columnEnd)
)
}
val messageSeverity: ProjectSeveriry = when (severity) {
EXCEPTION, ERROR -> ProjectSeveriry.ERROR
STRONG_WARNING, WARNING -> ProjectSeveriry.WARNING
INFO, LOGGING, OUTPUT -> ProjectSeveriry.INFO
}
diagnosticsMap.getOrPut(location?.path) { mutableListOf() }.add(ErrorDescriptor(textInterval, message, messageSeverity, className = "WARNING".takeIf { messageSeverity == ProjectSeveriry.WARNING }))
return ""
}

override fun renderUsage(usage: String): String =
render(STRONG_WARNING, usage, null)

override fun renderConclusion(): String = ""

override fun getName(): String = "Redirector"
})
val diagnostics = CompilerDiagnostics(diagnosticsMap)
return when {
diagnostics.any { it.severity == ProjectSeveriry.ERROR } -> NotCompiled(diagnostics)
exitCode.code != 0 -> ErrorDescriptor(
severity = ProjectSeveriry.ERROR,
message = "Compiler finished with non-null exit code ${exitCode.code}: ${exitCode.name}",
interval = null
).let { NotCompiled(CompilerDiagnostics(mapOf(null to listOf(it)))) }

else -> Compiled(result = onSuccess(), compilerDiagnostics = diagnostics)
}
}

@OptIn(ExperimentalPathApi::class)
fun <T> usingTempDirectory(action: (path: Path) -> T): T {
val path = getTempDirectory()
path.createDirectories()
return try {
action(path)
} finally {
path.deleteRecursively()
}
}

private fun getTempDirectory(): Path {
val dir = System.getProperty("java.io.tmpdir")
val sessionId = UUID.randomUUID().toString().replace("-", "")
return Paths.get(dir, sessionId)
}

fun List<KtFile>.writeToIoFiles(inputDir: Path): List<Path> {
val ioFiles = map { inputDir / it.name }
for ((ioFile, ktFile) in ioFiles zip this) {
ioFile.writeText(ktFile.text)
}
return ioFiles
}

val PATH_SEPARATOR: String = java.io.File.pathSeparator
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package com.compiler.server.compiler.components
import com.compiler.server.compiler.KotlinFile
import com.compiler.server.compiler.KotlinResolutionFacade
import com.compiler.server.model.Analysis
import com.compiler.server.model.ErrorDescriptor
import com.compiler.server.model.CompilerDiagnostics
import com.compiler.server.model.ProjectType
import com.intellij.psi.PsiElement
import com.intellij.psi.tree.TokenSet
Expand Down Expand Up @@ -72,11 +72,11 @@ class CompletionProvider(
importVariants(file, prefix, errors, line, character, projectType)
} else emptyList()
descriptorInfo.descriptors.toMutableList().apply {
sortWith(Comparator { a, b ->
sortWith { a, b ->
val (a1, a2) = a.presentableName()
val (b1, b2) = b.presentableName()
("$a1$a2").compareTo("$b1$b2", true)
})
}
}.mapNotNull { descriptor -> completionVariantFor(prefix, descriptor, element) } +
keywordsCompletionVariants(KtTokens.KEYWORDS, prefix) +
keywordsCompletionVariants(KtTokens.SOFT_KEYWORDS, prefix) +
Expand Down Expand Up @@ -113,15 +113,16 @@ class CompletionProvider(
private fun importVariants(
file: KotlinFile,
prefix: String,
errors: Map<String, List<ErrorDescriptor>>,
compilerDiagnostics: CompilerDiagnostics,
line: Int,
character: Int,
projectType: ProjectType
): List<Completion> {
val importCompletionVariants = indexationProvider.getClassesByName(prefix, projectType)
?.map { it.toCompletion() } ?: emptyList()
val currentErrors = errors[file.kotlinFile.name]?.filter {
it.interval.start.line == line &&
val currentErrors = compilerDiagnostics.map[file.kotlinFile.name]?.filter {
it.interval != null &&
it.interval.start.line == line &&
it.interval.start.ch <= character &&
it.interval.end.line == line &&
it.interval.end.ch >= character &&
Expand Down Expand Up @@ -257,7 +258,7 @@ class CompletionProvider(

// This code is a fragment of org.jetbrains.kotlin.idea.completion.CompletionSession from Kotlin IDE Plugin
// with a few simplifications which were possible because webdemo has very restricted environment (and well,
// because requirements on compeltion' quality in web-demo are lower)
// because requirements on completion' quality in web-demo are lower)
private inner class VisibilityFilter(
private val inDescriptor: DeclarationDescriptor,
private val bindingContext: BindingContext,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import com.intellij.psi.PsiFile
import component.KotlinEnvironment
import model.Completion
import org.jetbrains.kotlin.analyzer.AnalysisResult
import org.jetbrains.kotlin.cli.js.klib.TopDownAnalyzerFacadeForWasmJs
import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport
import org.jetbrains.kotlin.cli.js.klib.TopDownAnalyzerFacadeForJSIR
import org.jetbrains.kotlin.cli.js.klib.TopDownAnalyzerFacadeForWasmJs
import org.jetbrains.kotlin.cli.jvm.compiler.CliBindingTrace
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM
Expand All @@ -31,7 +31,6 @@ import org.jetbrains.kotlin.incremental.components.InlineConstTracker
import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.ir.backend.js.MainModule
import org.jetbrains.kotlin.ir.backend.js.ModulesStructure
import org.jetbrains.kotlin.js.config.ErrorTolerancePolicy
import org.jetbrains.kotlin.js.config.JsConfig
import org.jetbrains.kotlin.js.resolve.JsPlatformAnalyzerServices
import org.jetbrains.kotlin.name.Name
Expand Down Expand Up @@ -60,15 +59,15 @@ class ErrorAnalyzer(
projectType: ProjectType
): ErrorsAndAnalysis {
val analysis = when {
projectType.isJvmRelated() ->analysisOf(files, coreEnvironment)
projectType.isJvmRelated() -> analysisOf(files, coreEnvironment)
projectType.isJsRelated() -> analyzeFileForJs(files, coreEnvironment)
projectType == ProjectType.WASM -> analyzeFileForWasm(files, coreEnvironment)
else -> throw IllegalArgumentException("Unknown platform: $projectType")
}
return ErrorsAndAnalysis(
errorsFrom(
analysis.analysisResult.bindingContext.diagnostics.all(),
files.associate { it.name to anylizeErrorsFrom(it, projectType) },
CompilerDiagnostics(files.associate { it.name to analyzeErrorsFrom(it, projectType) }),
projectType
),
analysis
Expand Down Expand Up @@ -233,24 +232,13 @@ class ErrorAnalyzer(

fun errorsFrom(
diagnostics: Collection<Diagnostic>,
errors: Map<String, List<ErrorDescriptor>>,
compilerDiagnostics: CompilerDiagnostics,
projectType: ProjectType
): Map<String, List<ErrorDescriptor>> {
return (errors and errorsFrom(diagnostics, projectType)).map { (fileName, errors) ->
fileName to errors.sortedWith { o1, o2 ->
val line = o1.interval.start.line.compareTo(o2.interval.start.line)
when (line) {
0 -> o1.interval.start.ch.compareTo(o2.interval.start.ch)
else -> line
}
}
}.toMap()
}

fun isOnlyWarnings(errors: Map<String, List<ErrorDescriptor>>) =
errors.none { it.value.any { error -> error.severity == ProjectSeveriry.ERROR } }
): CompilerDiagnostics = (compilerDiagnostics.map and errorsFrom(diagnostics, projectType)).map { (fileName, errors) ->
fileName to errors.sortedWith(Comparator.comparing({ it.interval?.start }, nullsFirst()))
}.toMap().let(::CompilerDiagnostics)

private fun anylizeErrorsFrom(file: PsiFile, projectType: ProjectType): List<ErrorDescriptor> {
private fun analyzeErrorsFrom(file: PsiFile, projectType: ProjectType): List<ErrorDescriptor> {
class Visitor : PsiElementVisitor() {
val errors = mutableListOf<PsiErrorElement>()
override fun visitElement(element: PsiElement) {
Expand Down Expand Up @@ -347,7 +335,7 @@ class ErrorAnalyzer(
}
}.groupBy { it.first }.map { it.key to it.value.map { (_, error) -> error } }.toMap()

private infix fun Map<String, List<ErrorDescriptor>>.and(errors: Map<String, List<ErrorDescriptor>>) =
private infix fun Map<out String?, List<ErrorDescriptor>>.and(errors: Map<String, List<ErrorDescriptor>>) =
(this.toList() + errors.toList())
.groupBy { it.first }
.map { it.key to it.value.fold(emptyList<ErrorDescriptor>()) { acc, (_, errors) -> acc + errors } }
Expand All @@ -362,4 +350,4 @@ class ErrorAnalyzer(
}
}

data class ErrorsAndAnalysis(val errors: Map<String, List<ErrorDescriptor>>, val analysis: Analysis)
data class ErrorsAndAnalysis(val compilerDiagnostics: CompilerDiagnostics, val analysis: Analysis)
Loading

0 comments on commit 2d3e522

Please sign in to comment.