diff --git a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/Repl.kt b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/ReplApi.kt similarity index 100% rename from compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/Repl.kt rename to compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/ReplApi.kt diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/repl/GenericRepl.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/repl/GenericRepl.kt index ede265431a6..a34414b51d2 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/repl/GenericRepl.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/repl/GenericRepl.kt @@ -18,164 +18,14 @@ package org.jetbrains.kotlin.cli.jvm.repl import com.intellij.openapi.Disposable import com.intellij.openapi.diagnostic.Logger -import com.intellij.openapi.vfs.CharsetToolkit -import com.intellij.psi.PsiFileFactory -import com.intellij.psi.impl.PsiFileFactoryImpl -import com.intellij.testFramework.LightVirtualFile -import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys -import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport import org.jetbrains.kotlin.cli.common.messages.MessageCollector import org.jetbrains.kotlin.cli.common.repl.* -import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles -import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment -import org.jetbrains.kotlin.cli.jvm.config.JvmClasspathRoot import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots -import org.jetbrains.kotlin.cli.jvm.repl.messages.DiagnosticMessageHolder -import org.jetbrains.kotlin.cli.jvm.repl.messages.ReplTerminalDiagnosticMessageHolder -import org.jetbrains.kotlin.codegen.ClassBuilderFactories -import org.jetbrains.kotlin.codegen.KotlinCodegenFacade -import org.jetbrains.kotlin.codegen.state.GenerationState import org.jetbrains.kotlin.config.CompilerConfiguration -import org.jetbrains.kotlin.config.JVMConfigurationKeys -import org.jetbrains.kotlin.descriptors.ScriptDescriptor -import org.jetbrains.kotlin.idea.KotlinLanguage -import org.jetbrains.kotlin.parsing.KotlinParserDefinition -import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.script.KotlinScriptDefinition -import org.jetbrains.kotlin.script.KotlinScriptExternalDependencies -import java.io.File private val logger = Logger.getInstance(GenericRepl::class.java) -open class GenericReplChecker( - disposable: Disposable, - val scriptDefinition: KotlinScriptDefinition, - val compilerConfiguration: CompilerConfiguration, - messageCollector: MessageCollector -) : ReplChecker { - protected val environment = run { - compilerConfiguration.apply { - add(JVMConfigurationKeys.SCRIPT_DEFINITIONS, scriptDefinition) - put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, messageCollector) - put(JVMConfigurationKeys.RETAIN_OUTPUT_IN_MEMORY, true) - } - KotlinCoreEnvironment.createForProduction(disposable, compilerConfiguration, EnvironmentConfigFiles.JVM_CONFIG_FILES) - } - - protected val psiFileFactory: PsiFileFactoryImpl = PsiFileFactory.getInstance(environment.project) as PsiFileFactoryImpl - - // "line" - is the unit of evaluation here, could in fact consists of several character lines - protected class LineState( - val codeLine: ReplCodeLine, - val psiFile: KtFile, - val errorHolder: DiagnosticMessageHolder) - - protected var lineState: LineState? = null - - fun createDiagnosticHolder() = ReplTerminalDiagnosticMessageHolder() - - @Synchronized - override fun check(codeLine: ReplCodeLine, history: List): ReplCheckResult { - val virtualFile = - LightVirtualFile("line${codeLine.no}${KotlinParserDefinition.STD_SCRIPT_EXT}", KotlinLanguage.INSTANCE, codeLine.code).apply { - charset = CharsetToolkit.UTF8_CHARSET - } - val psiFile: KtFile = psiFileFactory.trySetupPsiForFile(virtualFile, KotlinLanguage.INSTANCE, true, false) as KtFile? - ?: error("Script file not analyzed at line ${codeLine.no}: ${codeLine.code}") - - val errorHolder = createDiagnosticHolder() - - val syntaxErrorReport = AnalyzerWithCompilerReport.Companion.reportSyntaxErrors(psiFile, errorHolder) - - if (!syntaxErrorReport.isHasErrors) { - lineState = LineState(codeLine, psiFile, errorHolder) - } - - return when { - syntaxErrorReport.isHasErrors && syntaxErrorReport.isAllErrorsAtEof -> ReplCheckResult.Incomplete(history) - syntaxErrorReport.isHasErrors -> ReplCheckResult.Error(history, errorHolder.renderedDiagnostics) - else -> ReplCheckResult.Ok(history) - } - } -} - - -open class GenericReplCompiler( - disposable: Disposable, - scriptDefinition: KotlinScriptDefinition, - compilerConfiguration: CompilerConfiguration, - messageCollector: MessageCollector -) : ReplCompiler, GenericReplChecker(disposable, scriptDefinition, compilerConfiguration, messageCollector) { - private val analyzerEngine = CliReplAnalyzerEngine(environment) - - private var lastDependencies: KotlinScriptExternalDependencies? = null - - private val descriptorsHistory = ReplHistory() - - @Synchronized - override fun compile(codeLine: ReplCodeLine, history: List): ReplCompileResult { - checkAndUpdateReplHistoryCollection(descriptorsHistory, history)?.let { - return@compile ReplCompileResult.HistoryMismatch(descriptorsHistory.lines, it) - } - - val (psiFile, errorHolder) = run { - if (lineState == null || lineState!!.codeLine != codeLine) { - val res = check(codeLine, history) - when (res) { - is ReplCheckResult.Incomplete -> return@compile ReplCompileResult.Incomplete(res.updatedHistory) - is ReplCheckResult.Error -> return@compile ReplCompileResult.Error(res.updatedHistory, res.message, res.location) - is ReplCheckResult.Ok -> {} // continue - } - } - Pair(lineState!!.psiFile, lineState!!.errorHolder) - } - - val newDependencies = scriptDefinition.getDependenciesFor(psiFile, environment.project, lastDependencies) - var classpathAddendum: List? = null - if (lastDependencies != newDependencies) { - lastDependencies = newDependencies - classpathAddendum = newDependencies?.let { environment.updateClasspath(it.classpath.map(::JvmClasspathRoot)) } - } - - val analysisResult = analyzerEngine.analyzeReplLine(psiFile, codeLine.no) - AnalyzerWithCompilerReport.Companion.reportDiagnostics(analysisResult.diagnostics, errorHolder) - val scriptDescriptor = when (analysisResult) { - is CliReplAnalyzerEngine.ReplLineAnalysisResult.WithErrors -> return ReplCompileResult.Error(descriptorsHistory.lines, errorHolder.renderedDiagnostics) - is CliReplAnalyzerEngine.ReplLineAnalysisResult.Successful -> analysisResult.scriptDescriptor - else -> error("Unexpected result ${analysisResult.javaClass}") - } - - val state = GenerationState( - psiFile.project, - ClassBuilderFactories.binaries(false), - analyzerEngine.module, - analyzerEngine.trace.bindingContext, - listOf(psiFile), - compilerConfiguration - ) - state.replSpecific.scriptResultFieldName = SCRIPT_RESULT_FIELD_NAME - state.replSpecific.earlierScriptsForReplInterpreter = descriptorsHistory.values - state.beforeCompile() - KotlinCodegenFacade.generatePackage( - state, - psiFile.script!!.getContainingKtFile().packageFqName, - setOf(psiFile.script!!.getContainingKtFile()), - org.jetbrains.kotlin.codegen.CompilationErrorHandler.THROW_EXCEPTION) - - descriptorsHistory.add(codeLine, scriptDescriptor) - - return ReplCompileResult.CompiledClasses(descriptorsHistory.lines, - state.factory.asList().map { CompiledClassData(it.relativePath, it.asByteArray()) }, - state.replSpecific.hasResult, - classpathAddendum ?: emptyList()) - } - - companion object { - private val SCRIPT_RESULT_FIELD_NAME = "\$\$result" - } -} - - open class GenericRepl( disposable: Disposable, scriptDefinition: KotlinScriptDefinition, diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/repl/GenericReplChecker.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/repl/GenericReplChecker.kt new file mode 100644 index 00000000000..ae968dc613f --- /dev/null +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/repl/GenericReplChecker.kt @@ -0,0 +1,93 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.cli.jvm.repl + +import com.intellij.openapi.Disposable +import com.intellij.openapi.vfs.CharsetToolkit +import com.intellij.psi.PsiFileFactory +import com.intellij.psi.impl.PsiFileFactoryImpl +import com.intellij.testFramework.LightVirtualFile +import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys +import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport +import org.jetbrains.kotlin.cli.common.messages.MessageCollector +import org.jetbrains.kotlin.cli.common.repl.ReplCheckResult +import org.jetbrains.kotlin.cli.common.repl.ReplChecker +import org.jetbrains.kotlin.cli.common.repl.ReplCodeLine +import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles +import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment +import org.jetbrains.kotlin.cli.jvm.repl.messages.DiagnosticMessageHolder +import org.jetbrains.kotlin.cli.jvm.repl.messages.ReplTerminalDiagnosticMessageHolder +import org.jetbrains.kotlin.config.CompilerConfiguration +import org.jetbrains.kotlin.config.JVMConfigurationKeys +import org.jetbrains.kotlin.idea.KotlinLanguage +import org.jetbrains.kotlin.parsing.KotlinParserDefinition +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.script.KotlinScriptDefinition + +open class GenericReplChecker( + disposable: Disposable, + val scriptDefinition: KotlinScriptDefinition, + val compilerConfiguration: CompilerConfiguration, + messageCollector: MessageCollector +) : ReplChecker { + protected val environment = run { + compilerConfiguration.apply { + add(JVMConfigurationKeys.SCRIPT_DEFINITIONS, scriptDefinition) + put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, messageCollector) + put(JVMConfigurationKeys.RETAIN_OUTPUT_IN_MEMORY, true) + } + KotlinCoreEnvironment.createForProduction(disposable, compilerConfiguration, EnvironmentConfigFiles.JVM_CONFIG_FILES) + } + + protected val psiFileFactory: PsiFileFactoryImpl = PsiFileFactory.getInstance(environment.project) as PsiFileFactoryImpl + + // "line" - is the unit of evaluation here, could in fact consists of several character lines + protected class LineState( + val codeLine: ReplCodeLine, + val psiFile: KtFile, + val errorHolder: DiagnosticMessageHolder) + + protected var lineState: LineState? = null + + fun createDiagnosticHolder() = ReplTerminalDiagnosticMessageHolder() + + @Synchronized + override fun check(codeLine: ReplCodeLine, history: List): ReplCheckResult { + val virtualFile = + LightVirtualFile("line${codeLine.no}${KotlinParserDefinition.STD_SCRIPT_EXT}", KotlinLanguage.INSTANCE, codeLine.code).apply { + charset = CharsetToolkit.UTF8_CHARSET + } + val psiFile: KtFile = psiFileFactory.trySetupPsiForFile(virtualFile, KotlinLanguage.INSTANCE, true, false) as KtFile? + ?: error("Script file not analyzed at line ${codeLine.no}: ${codeLine.code}") + + val errorHolder = createDiagnosticHolder() + + val syntaxErrorReport = AnalyzerWithCompilerReport.Companion.reportSyntaxErrors(psiFile, errorHolder) + + if (!syntaxErrorReport.isHasErrors) { + lineState = LineState(codeLine, psiFile, errorHolder) + } + + return when { + syntaxErrorReport.isHasErrors && syntaxErrorReport.isAllErrorsAtEof -> ReplCheckResult.Incomplete(history) + syntaxErrorReport.isHasErrors -> ReplCheckResult.Error(history, errorHolder.renderedDiagnostics) + else -> ReplCheckResult.Ok(history) + } + } +} + + diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/repl/GenericReplCompiler.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/repl/GenericReplCompiler.kt new file mode 100644 index 00000000000..ab914580553 --- /dev/null +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/repl/GenericReplCompiler.kt @@ -0,0 +1,106 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.cli.jvm.repl + +import com.intellij.openapi.Disposable +import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport +import org.jetbrains.kotlin.cli.common.messages.MessageCollector +import org.jetbrains.kotlin.cli.common.repl.* +import org.jetbrains.kotlin.cli.jvm.config.JvmClasspathRoot +import org.jetbrains.kotlin.codegen.ClassBuilderFactories +import org.jetbrains.kotlin.codegen.KotlinCodegenFacade +import org.jetbrains.kotlin.codegen.state.GenerationState +import org.jetbrains.kotlin.config.CompilerConfiguration +import org.jetbrains.kotlin.descriptors.ScriptDescriptor +import org.jetbrains.kotlin.script.KotlinScriptDefinition +import org.jetbrains.kotlin.script.KotlinScriptExternalDependencies +import java.io.File + +open class GenericReplCompiler( + disposable: Disposable, + scriptDefinition: KotlinScriptDefinition, + compilerConfiguration: CompilerConfiguration, + messageCollector: MessageCollector +) : ReplCompiler, GenericReplChecker(disposable, scriptDefinition, compilerConfiguration, messageCollector) { + private val analyzerEngine = CliReplAnalyzerEngine(environment) + + private var lastDependencies: KotlinScriptExternalDependencies? = null + + private val descriptorsHistory = ReplHistory() + + @Synchronized + override fun compile(codeLine: ReplCodeLine, history: List): ReplCompileResult { + checkAndUpdateReplHistoryCollection(descriptorsHistory, history)?.let { + return@compile ReplCompileResult.HistoryMismatch(descriptorsHistory.lines, it) + } + + val (psiFile, errorHolder) = run { + if (lineState == null || lineState!!.codeLine != codeLine) { + val res = check(codeLine, history) + when (res) { + is ReplCheckResult.Incomplete -> return@compile ReplCompileResult.Incomplete(res.updatedHistory) + is ReplCheckResult.Error -> return@compile ReplCompileResult.Error(res.updatedHistory, res.message, res.location) + is ReplCheckResult.Ok -> {} // continue + } + } + Pair(lineState!!.psiFile, lineState!!.errorHolder) + } + + val newDependencies = scriptDefinition.getDependenciesFor(psiFile, environment.project, lastDependencies) + var classpathAddendum: List? = null + if (lastDependencies != newDependencies) { + lastDependencies = newDependencies + classpathAddendum = newDependencies?.let { environment.updateClasspath(it.classpath.map(::JvmClasspathRoot)) } + } + + val analysisResult = analyzerEngine.analyzeReplLine(psiFile, codeLine.no) + AnalyzerWithCompilerReport.Companion.reportDiagnostics(analysisResult.diagnostics, errorHolder) + val scriptDescriptor = when (analysisResult) { + is CliReplAnalyzerEngine.ReplLineAnalysisResult.WithErrors -> return ReplCompileResult.Error(descriptorsHistory.lines, errorHolder.renderedDiagnostics) + is CliReplAnalyzerEngine.ReplLineAnalysisResult.Successful -> analysisResult.scriptDescriptor + else -> error("Unexpected result ${analysisResult.javaClass}") + } + + val state = GenerationState( + psiFile.project, + ClassBuilderFactories.binaries(false), + analyzerEngine.module, + analyzerEngine.trace.bindingContext, + listOf(psiFile), + compilerConfiguration + ) + state.replSpecific.scriptResultFieldName = SCRIPT_RESULT_FIELD_NAME + state.replSpecific.earlierScriptsForReplInterpreter = descriptorsHistory.values + state.beforeCompile() + KotlinCodegenFacade.generatePackage( + state, + psiFile.script!!.getContainingKtFile().packageFqName, + setOf(psiFile.script!!.getContainingKtFile()), + org.jetbrains.kotlin.codegen.CompilationErrorHandler.THROW_EXCEPTION) + + descriptorsHistory.add(codeLine, scriptDescriptor) + + return ReplCompileResult.CompiledClasses(descriptorsHistory.lines, + state.factory.asList().map { CompiledClassData(it.relativePath, it.asByteArray()) }, + state.replSpecific.hasResult, + classpathAddendum ?: emptyList()) + } + + companion object { + private val SCRIPT_RESULT_FIELD_NAME = "\$\$result" + } +}