Refactor scripting compiler plugin - move cli dependent parts out of the -impl jar
The kotlin-scripting-compiler-impl jar is used in the idea plugin and therefore should not depend on the cli parts of the compiler to avoid dependency from the plugin to the kotlin-compiler.jar. Therefore the cli-dependent parts were moved to the scripting plugin jar, which is used only in cli compiler based environments. Also implement required abstractions to allow this movement and drop some redundant dependencies to the cli parts in other projects.
This commit is contained in:
@@ -11,7 +11,6 @@ dependencies {
|
||||
compileOnly(project(":compiler:frontend.java"))
|
||||
compileOnly(project(":compiler:psi"))
|
||||
compileOnly(project(":compiler:plugin-api"))
|
||||
compileOnly(project(":compiler:cli"))
|
||||
compile(project(":kotlin-scripting-common"))
|
||||
compile(project(":kotlin-scripting-jvm"))
|
||||
compile(kotlinStdlib())
|
||||
|
||||
-36
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.scripting.configuration
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
||||
import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.scripting.definitions.loadScriptTemplatesFromClasspath
|
||||
import org.jetbrains.kotlin.scripting.definitions.reporter
|
||||
import kotlin.script.experimental.host.ScriptingHostConfiguration
|
||||
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
|
||||
|
||||
const val KOTLIN_SCRIPTING_PLUGIN_ID = "kotlin.scripting"
|
||||
|
||||
fun configureScriptDefinitions(
|
||||
scriptTemplates: List<String>,
|
||||
configuration: CompilerConfiguration,
|
||||
baseClassloader: ClassLoader,
|
||||
messageCollector: MessageCollector,
|
||||
hostConfiguration: ScriptingHostConfiguration
|
||||
) {
|
||||
// TODO: consider using escaping to allow kotlin escaped names in class names
|
||||
val templatesFromClasspath = loadScriptTemplatesFromClasspath(
|
||||
scriptTemplates, configuration.jvmClasspathRoots, emptyList(), baseClassloader, hostConfiguration, messageCollector.reporter
|
||||
)
|
||||
configuration.addAll(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS, templatesFromClasspath.toList())
|
||||
}
|
||||
|
||||
fun makeHostConfiguration(project: Project, configuration: CompilerConfiguration): ScriptingHostConfiguration =
|
||||
ScriptingHostConfiguration(defaultJvmScriptingHostConfiguration) {
|
||||
// TODO: add jdk path and other params if needed
|
||||
}
|
||||
+4
-9
@@ -5,19 +5,14 @@
|
||||
|
||||
package org.jetbrains.kotlin.scripting.definitions
|
||||
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
|
||||
import java.io.File
|
||||
import kotlin.script.experimental.annotations.KotlinScript
|
||||
import kotlin.script.experimental.api.KotlinType
|
||||
import kotlin.script.experimental.api.ScriptCompilationConfiguration
|
||||
import kotlin.script.experimental.api.ScriptEvaluationConfiguration
|
||||
import kotlin.script.experimental.api.fileExtension
|
||||
import kotlin.script.experimental.api.*
|
||||
import kotlin.script.experimental.host.ScriptingHostConfiguration
|
||||
import kotlin.script.experimental.host.configurationDependencies
|
||||
import kotlin.script.experimental.host.createCompilationConfigurationFromTemplate
|
||||
import kotlin.script.experimental.host.createEvaluationConfigurationFromTemplate
|
||||
import kotlin.script.experimental.jvm.JvmDependency
|
||||
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
|
||||
|
||||
class LazyScriptDefinitionFromDiscoveredClass internal constructor(
|
||||
baseHostConfiguration: ScriptingHostConfiguration,
|
||||
@@ -43,7 +38,7 @@ class LazyScriptDefinitionFromDiscoveredClass internal constructor(
|
||||
|
||||
private val configurations by lazy(LazyThreadSafetyMode.PUBLICATION) {
|
||||
messageReporter(
|
||||
CompilerMessageSeverity.LOGGING,
|
||||
ScriptDiagnostic.Severity.DEBUG,
|
||||
"Configure scripting: loading script definition class $className using classpath $classpath\n. ${Thread.currentThread().stackTrace}"
|
||||
)
|
||||
try {
|
||||
@@ -61,11 +56,11 @@ class LazyScriptDefinitionFromDiscoveredClass internal constructor(
|
||||
)
|
||||
compileCfg to evalCfg
|
||||
} catch (ex: ClassNotFoundException) {
|
||||
messageReporter(CompilerMessageSeverity.ERROR, "Cannot find script definition class $className")
|
||||
messageReporter(ScriptDiagnostic.Severity.ERROR, "Cannot find script definition class $className")
|
||||
InvalidScriptDefinition
|
||||
} catch (ex: Exception) {
|
||||
messageReporter(
|
||||
CompilerMessageSeverity.ERROR,
|
||||
ScriptDiagnostic.Severity.ERROR,
|
||||
"Error processing script definition class $className: ${ex.message}\nclasspath:\n${classpath.joinToString("\n", " ")}"
|
||||
)
|
||||
InvalidScriptDefinition
|
||||
|
||||
+14
-23
@@ -5,8 +5,6 @@
|
||||
|
||||
package org.jetbrains.kotlin.scripting.definitions
|
||||
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
|
||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
||||
import org.jetbrains.kotlin.scripting.resolve.KotlinScriptDefinitionFromAnnotatedTemplate
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||
import java.io.File
|
||||
@@ -14,21 +12,14 @@ import java.io.IOException
|
||||
import java.net.URLClassLoader
|
||||
import java.util.jar.JarFile
|
||||
import kotlin.script.experimental.annotations.KotlinScript
|
||||
import kotlin.script.experimental.api.KotlinType
|
||||
import kotlin.script.experimental.api.ScriptDiagnostic
|
||||
import kotlin.script.experimental.host.ScriptingHostConfiguration
|
||||
import kotlin.script.experimental.host.createCompilationConfigurationFromTemplate
|
||||
import kotlin.script.experimental.host.createEvaluationConfigurationFromTemplate
|
||||
import kotlin.script.templates.ScriptTemplateDefinition
|
||||
|
||||
const val SCRIPT_DEFINITION_MARKERS_PATH = "META-INF/kotlin/script/templates/"
|
||||
const val SCRIPT_DEFINITION_MARKERS_EXTENSION_WITH_DOT = ".classname"
|
||||
|
||||
typealias MessageReporter = (CompilerMessageSeverity, String) -> Unit
|
||||
|
||||
val MessageCollector.reporter: MessageReporter
|
||||
get() = { severity, message ->
|
||||
this.report(severity, message)
|
||||
}
|
||||
typealias MessageReporter = (ScriptDiagnostic.Severity, String) -> Unit
|
||||
|
||||
class ScriptDefinitionsFromClasspathDiscoverySource(
|
||||
private val classpath: List<File>,
|
||||
@@ -107,7 +98,7 @@ private fun scriptTemplatesDiscoverySequence(
|
||||
)
|
||||
if (notFoundClasses.isNotEmpty()) {
|
||||
messageReporter(
|
||||
CompilerMessageSeverity.STRONG_WARNING,
|
||||
ScriptDiagnostic.Severity.WARNING,
|
||||
"Configure scripting: unable to find script definitions [${notFoundClasses.joinToString(", ")}]"
|
||||
)
|
||||
}
|
||||
@@ -131,12 +122,12 @@ private fun scriptTemplatesDiscoverySequence(
|
||||
}
|
||||
else -> {
|
||||
// assuming that invalid classpath entries will be reported elsewhere anyway, so do not spam user with additional warnings here
|
||||
messageReporter(CompilerMessageSeverity.LOGGING, "Configure scripting: Unknown classpath entry $dep")
|
||||
messageReporter(ScriptDiagnostic.Severity.DEBUG, "Configure scripting: Unknown classpath entry $dep")
|
||||
}
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
messageReporter(
|
||||
CompilerMessageSeverity.STRONG_WARNING, "Configure scripting: unable to process classpath entry $dep: $e"
|
||||
ScriptDiagnostic.Severity.WARNING, "Configure scripting: unable to process classpath entry $dep: $e"
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -152,13 +143,13 @@ private fun scriptTemplatesDiscoverySequence(
|
||||
remainingDefinitionCandidates = notFoundDefinitions
|
||||
} catch (e: IOException) {
|
||||
messageReporter(
|
||||
CompilerMessageSeverity.STRONG_WARNING, "Configure scripting: unable to process classpath entry $dep: $e"
|
||||
ScriptDiagnostic.Severity.WARNING, "Configure scripting: unable to process classpath entry $dep: $e"
|
||||
)
|
||||
}
|
||||
}
|
||||
if (remainingDefinitionCandidates.isNotEmpty()) {
|
||||
messageReporter(
|
||||
CompilerMessageSeverity.STRONG_WARNING,
|
||||
ScriptDiagnostic.Severity.WARNING,
|
||||
"The following script definitions are not found in the classpath: [${remainingDefinitionCandidates.joinToString()}]"
|
||||
)
|
||||
}
|
||||
@@ -207,7 +198,7 @@ fun loadScriptTemplatesFromClasspath(
|
||||
}
|
||||
else -> {
|
||||
// assuming that invalid classpath entries will be reported elsewhere anyway, so do not spam user with additional warnings here
|
||||
messageReporter(CompilerMessageSeverity.LOGGING, "Configure scripting: Unknown classpath entry $dep")
|
||||
messageReporter(ScriptDiagnostic.Severity.DEBUG, "Configure scripting: Unknown classpath entry $dep")
|
||||
DefinitionsLoadPartitionResult(
|
||||
listOf(),
|
||||
remainingTemplates
|
||||
@@ -222,7 +213,7 @@ fun loadScriptTemplatesFromClasspath(
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
messageReporter(
|
||||
CompilerMessageSeverity.STRONG_WARNING,
|
||||
ScriptDiagnostic.Severity.WARNING,
|
||||
"Configure scripting: unable to process classpath entry $dep: $e"
|
||||
)
|
||||
}
|
||||
@@ -230,7 +221,7 @@ fun loadScriptTemplatesFromClasspath(
|
||||
|
||||
if (remainingTemplates.isNotEmpty()) {
|
||||
messageReporter(
|
||||
CompilerMessageSeverity.STRONG_WARNING,
|
||||
ScriptDiagnostic.Severity.WARNING,
|
||||
"Configure scripting: unable to find script definition classes: ${remainingTemplates.joinToString(", ")}"
|
||||
)
|
||||
}
|
||||
@@ -319,14 +310,14 @@ private fun loadScriptDefinition(
|
||||
}
|
||||
if (def != null) {
|
||||
messageReporter(
|
||||
CompilerMessageSeverity.LOGGING,
|
||||
ScriptDiagnostic.Severity.DEBUG,
|
||||
"Configure scripting: Added template $templateClassName from ${classpathWithLoader.classpath}"
|
||||
)
|
||||
return def
|
||||
}
|
||||
}
|
||||
messageReporter(
|
||||
CompilerMessageSeverity.STRONG_WARNING,
|
||||
ScriptDiagnostic.Severity.WARNING,
|
||||
"Configure scripting: $templateClassName is not marked with any known kotlin script annotation"
|
||||
)
|
||||
return null
|
||||
@@ -347,7 +338,7 @@ private fun loadScriptDefinition(
|
||||
ScriptDefinition.FromLegacyTemplate(hostConfiguration, cls.kotlin)
|
||||
}
|
||||
messageReporter(
|
||||
CompilerMessageSeverity.INFO,
|
||||
ScriptDiagnostic.Severity.INFO,
|
||||
"Added script definition $template to configuration: name = ${def.name}"
|
||||
)
|
||||
return def
|
||||
@@ -356,7 +347,7 @@ private fun loadScriptDefinition(
|
||||
} catch (ex: Exception) {
|
||||
// other exceptions - might be an error
|
||||
messageReporter(
|
||||
CompilerMessageSeverity.STRONG_WARNING,
|
||||
ScriptDiagnostic.Severity.WARNING,
|
||||
"Error on loading script definition $template: ${ex.message}"
|
||||
)
|
||||
}
|
||||
|
||||
-86
@@ -1,86 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.scripting.dependencies
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.cli.common.config.KotlinSourceRoot
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.createSourceFilesFromSourceRoots
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.scripting.definitions.ScriptDependenciesProvider
|
||||
import java.io.File
|
||||
import kotlin.script.experimental.api.valueOrNull
|
||||
|
||||
data class ScriptsCompilationDependencies(
|
||||
val classpath: List<File>,
|
||||
val sources: List<KtFile>,
|
||||
val sourceDependencies: List<SourceDependencies>
|
||||
) {
|
||||
data class SourceDependencies(
|
||||
val scriptFile: KtFile,
|
||||
val sourceDependencies: List<KtFile>
|
||||
)
|
||||
}
|
||||
|
||||
// recursively collect dependencies from initial and imported scripts
|
||||
fun collectScriptsCompilationDependencies(
|
||||
configuration: CompilerConfiguration,
|
||||
project: Project,
|
||||
initialSources: Iterable<KtFile>
|
||||
): ScriptsCompilationDependencies {
|
||||
val collectedClassPath = ArrayList<File>()
|
||||
val collectedSources = ArrayList<KtFile>()
|
||||
val collectedSourceDependencies = ArrayList<ScriptsCompilationDependencies.SourceDependencies>()
|
||||
var remainingSources = initialSources
|
||||
val knownSourcePaths = initialSources.mapNotNullTo(HashSet()) { it.virtualFile?.path }
|
||||
val importsProvider = ScriptDependenciesProvider.getInstance(project)
|
||||
if (importsProvider != null) {
|
||||
while (true) {
|
||||
val newRemainingSources = ArrayList<KtFile>()
|
||||
for (source in remainingSources) {
|
||||
val refinedConfiguration = importsProvider.getScriptConfigurationResult(source)?.valueOrNull()
|
||||
if (refinedConfiguration != null) {
|
||||
collectedClassPath.addAll(refinedConfiguration.dependenciesClassPath)
|
||||
|
||||
val sourceDependenciesRoots = refinedConfiguration.importedScripts.map {
|
||||
KotlinSourceRoot(it.path, false)
|
||||
}
|
||||
val sourceDependencies =
|
||||
createSourceFilesFromSourceRoots(
|
||||
configuration, project, sourceDependenciesRoots,
|
||||
// TODO: consider receiving and using precise location from the resolver in the future
|
||||
source.virtualFile?.path?.let { CompilerMessageLocation.create(it) }
|
||||
)
|
||||
if (sourceDependencies.isNotEmpty()) {
|
||||
collectedSourceDependencies.add(
|
||||
ScriptsCompilationDependencies.SourceDependencies(
|
||||
source,
|
||||
sourceDependencies
|
||||
)
|
||||
)
|
||||
|
||||
val newSources = sourceDependencies.filterNot { knownSourcePaths.contains(it.virtualFile.path) }
|
||||
for (newSource in newSources) {
|
||||
collectedSources.add(newSource)
|
||||
newRemainingSources.add(newSource)
|
||||
knownSourcePaths.add(newSource.virtualFile.path)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (newRemainingSources.isEmpty()) break
|
||||
else {
|
||||
remainingSources = newRemainingSources
|
||||
}
|
||||
}
|
||||
}
|
||||
return ScriptsCompilationDependencies(
|
||||
collectedClassPath.distinctBy { it.absolutePath },
|
||||
collectedSources,
|
||||
collectedSourceDependencies
|
||||
)
|
||||
}
|
||||
-45
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.scripting.extensions
|
||||
|
||||
import com.intellij.core.JavaCoreProjectEnvironment
|
||||
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
||||
import org.jetbrains.kotlin.cli.common.extensions.ReplFactoryExtension
|
||||
import org.jetbrains.kotlin.cli.common.repl.ReplCompiler
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition
|
||||
import org.jetbrains.kotlin.scripting.repl.GenericReplCompiler
|
||||
import org.jetbrains.kotlin.scripting.resolve.KotlinScriptDefinitionFromAnnotatedTemplate
|
||||
import java.io.File
|
||||
import java.net.URLClassLoader
|
||||
|
||||
class JvmStandardReplFactoryExtension : ReplFactoryExtension {
|
||||
|
||||
override fun makeReplCompiler(
|
||||
templateClassName: String,
|
||||
templateClasspath: List<File>,
|
||||
baseClassLoader: ClassLoader?,
|
||||
configuration: CompilerConfiguration,
|
||||
projectEnvironment: JavaCoreProjectEnvironment
|
||||
): ReplCompiler = GenericReplCompiler(
|
||||
projectEnvironment.parentDisposable,
|
||||
makeScriptDefinition(templateClasspath, templateClassName, baseClassLoader),
|
||||
configuration,
|
||||
configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
|
||||
)
|
||||
|
||||
private fun makeScriptDefinition(
|
||||
templateClasspath: List<File>, templateClassName: String, baseClassLoader: ClassLoader?
|
||||
): KotlinScriptDefinition = try {
|
||||
val classloader = URLClassLoader(templateClasspath.map { it.toURI().toURL() }.toTypedArray(), baseClassLoader)
|
||||
val cls = classloader.loadClass(templateClassName)
|
||||
KotlinScriptDefinitionFromAnnotatedTemplate(cls.kotlin, emptyMap())
|
||||
} catch (ex: ClassNotFoundException) {
|
||||
throw IllegalStateException("Cannot find script definition template class $templateClassName", ex)
|
||||
} catch (ex: Exception) {
|
||||
throw IllegalStateException("Error loading script definition template $templateClassName", ex)
|
||||
}
|
||||
}
|
||||
-30
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.scripting.extensions
|
||||
|
||||
import com.intellij.mock.MockProject
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.extensions.CollectAdditionalSourcesExtension
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.scripting.dependencies.collectScriptsCompilationDependencies
|
||||
|
||||
class ScriptingCollectAdditionalSourcesExtension(val project: MockProject) : CollectAdditionalSourcesExtension {
|
||||
override fun collectAdditionalSourcesAndUpdateConfiguration(
|
||||
knownSources: Collection<KtFile>,
|
||||
configuration: CompilerConfiguration,
|
||||
project: Project
|
||||
): Collection<KtFile> {
|
||||
val (newSourcesClasspath, newSources, _) = collectScriptsCompilationDependencies(
|
||||
configuration,
|
||||
project,
|
||||
knownSources
|
||||
)
|
||||
configuration.addJvmClasspathRoots(newSourcesClasspath)
|
||||
return newSources
|
||||
}
|
||||
}
|
||||
-54
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* 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.scripting.repl
|
||||
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.KtDestructuringDeclarationEntry
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.resolve.lazy.data.KtScriptInfo
|
||||
import org.jetbrains.kotlin.resolve.lazy.declarations.PackageMemberDeclarationProvider
|
||||
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
|
||||
|
||||
open class DelegatePackageMemberDeclarationProvider(var delegate: PackageMemberDeclarationProvider) : PackageMemberDeclarationProvider {
|
||||
// Can't use Kotlin delegate feature because of inability to change delegate object in runtime (KT-5870)
|
||||
|
||||
override fun getAllDeclaredSubPackages(nameFilter: (Name) -> Boolean) = delegate.getAllDeclaredSubPackages(nameFilter)
|
||||
|
||||
override fun getPackageFiles() = delegate.getPackageFiles()
|
||||
|
||||
override fun containsFile(file: KtFile) = delegate.containsFile(file)
|
||||
|
||||
override fun getDeclarations(
|
||||
kindFilter: DescriptorKindFilter,
|
||||
nameFilter: (Name) -> Boolean
|
||||
) = delegate.getDeclarations(kindFilter, nameFilter)
|
||||
|
||||
override fun getFunctionDeclarations(name: Name) = delegate.getFunctionDeclarations(name)
|
||||
|
||||
override fun getPropertyDeclarations(name: Name) = delegate.getPropertyDeclarations(name)
|
||||
|
||||
override fun getDestructuringDeclarationsEntries(name: Name): Collection<KtDestructuringDeclarationEntry> =
|
||||
delegate.getDestructuringDeclarationsEntries(name)
|
||||
|
||||
override fun getClassOrObjectDeclarations(name: Name) = delegate.getClassOrObjectDeclarations(name)
|
||||
|
||||
override fun getScriptDeclarations(name: Name): Collection<KtScriptInfo> = delegate.getScriptDeclarations(name)
|
||||
|
||||
override fun getTypeAliasDeclarations(name: Name) = delegate.getTypeAliasDeclarations(name)
|
||||
|
||||
override fun getDeclarationNames() = delegate.getDeclarationNames()
|
||||
}
|
||||
-65
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.scripting.repl
|
||||
|
||||
import org.jetbrains.kotlin.cli.common.repl.*
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||
import org.jetbrains.kotlin.descriptors.ScriptDescriptor
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.scripting.repl.messages.DiagnosticMessageHolder
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||
import kotlin.script.experimental.dependencies.ScriptDependencies
|
||||
|
||||
class ReplCompilerStageHistory(private val state: org.jetbrains.kotlin.scripting.repl.GenericReplCompilerState) : BasicReplStageHistory<ScriptDescriptor>(state.lock) {
|
||||
|
||||
override fun reset(): Iterable<ILineId> {
|
||||
val removedCompiledLines = super.reset()
|
||||
val removedAnalyzedLines = state.analyzerEngine.reset()
|
||||
|
||||
checkConsistent(removedCompiledLines, removedAnalyzedLines)
|
||||
return removedCompiledLines
|
||||
}
|
||||
|
||||
override fun resetTo(id: ILineId): Iterable<ILineId> {
|
||||
val removedCompiledLines = super.resetTo(id)
|
||||
val removedAnalyzedLines = state.analyzerEngine.resetToLine(id)
|
||||
|
||||
checkConsistent(removedCompiledLines, removedAnalyzedLines)
|
||||
return removedCompiledLines
|
||||
}
|
||||
|
||||
private fun checkConsistent(removedCompiledLines: Iterable<ILineId>, removedAnalyzedLines: List<ReplCodeLine>) {
|
||||
removedCompiledLines.zip(removedAnalyzedLines).forEach { (removedCompiledLine, removedAnalyzedLine) ->
|
||||
if (removedCompiledLine != LineId(removedAnalyzedLine)) {
|
||||
throw IllegalStateException("History mismatch when resetting lines: ${removedCompiledLine.no} != $removedAnalyzedLine")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract class GenericReplCheckerState : IReplStageState<ScriptDescriptor> {
|
||||
|
||||
// "line" - is the unit of evaluation here, could in fact consists of several character lines
|
||||
class LineState(
|
||||
val codeLine: ReplCodeLine,
|
||||
val psiFile: KtFile,
|
||||
val errorHolder: DiagnosticMessageHolder
|
||||
)
|
||||
|
||||
var lastLineState: org.jetbrains.kotlin.scripting.repl.GenericReplCheckerState.LineState? = null // for transferring state to the compiler in most typical case
|
||||
}
|
||||
|
||||
class GenericReplCompilerState(environment: KotlinCoreEnvironment, override val lock: ReentrantReadWriteLock = ReentrantReadWriteLock()) :
|
||||
IReplStageState<ScriptDescriptor>, org.jetbrains.kotlin.scripting.repl.GenericReplCheckerState() {
|
||||
|
||||
override val history = org.jetbrains.kotlin.scripting.repl.ReplCompilerStageHistory(this)
|
||||
|
||||
override val currentGeneration: Int get() = (history as BasicReplStageHistory<*>).currentGeneration.get()
|
||||
|
||||
val analyzerEngine = org.jetbrains.kotlin.scripting.repl.ReplCodeAnalyzer(environment)
|
||||
|
||||
var lastDependencies: ScriptDependencies? = null
|
||||
}
|
||||
-100
@@ -1,100 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.scripting.repl
|
||||
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
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.jvmClasspathRoots
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.config.JVMConfigurationKeys
|
||||
import org.jetbrains.kotlin.config.JvmTarget
|
||||
import org.jetbrains.kotlin.idea.KotlinLanguage
|
||||
import org.jetbrains.kotlin.parsing.KotlinParserDefinition
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys
|
||||
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition
|
||||
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
|
||||
import org.jetbrains.kotlin.scripting.repl.messages.ConsoleDiagnosticMessageHolder
|
||||
import kotlin.concurrent.write
|
||||
import kotlin.script.experimental.host.ScriptingHostConfiguration
|
||||
import kotlin.script.experimental.host.configurationDependencies
|
||||
import kotlin.script.experimental.jvm.JvmDependency
|
||||
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
|
||||
|
||||
const val KOTLIN_REPL_JVM_TARGET_PROPERTY = "kotlin.repl.jvm.target"
|
||||
|
||||
open class GenericReplChecker(
|
||||
disposable: Disposable,
|
||||
private val scriptDefinition: KotlinScriptDefinition,
|
||||
private val compilerConfiguration: CompilerConfiguration,
|
||||
messageCollector: MessageCollector
|
||||
) : ReplCheckAction {
|
||||
|
||||
internal val environment = run {
|
||||
compilerConfiguration.apply {
|
||||
val hostConfiguration = ScriptingHostConfiguration(defaultJvmScriptingHostConfiguration) {
|
||||
configurationDependencies(JvmDependency(jvmClasspathRoots))
|
||||
}
|
||||
add(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS, ScriptDefinition.FromLegacy(hostConfiguration, scriptDefinition))
|
||||
put<MessageCollector>(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, messageCollector)
|
||||
put(JVMConfigurationKeys.RETAIN_OUTPUT_IN_MEMORY, true)
|
||||
|
||||
if (get(JVMConfigurationKeys.JVM_TARGET) == null) {
|
||||
put(JVMConfigurationKeys.JVM_TARGET,
|
||||
System.getProperty(KOTLIN_REPL_JVM_TARGET_PROPERTY)?.let { JvmTarget.fromString(it) }
|
||||
?: System.getProperty("java.specification.version")?.let { JvmTarget.fromString(it) }
|
||||
?: JvmTarget.DEFAULT)
|
||||
}
|
||||
}
|
||||
KotlinCoreEnvironment.createForProduction(disposable, compilerConfiguration, EnvironmentConfigFiles.JVM_CONFIG_FILES)
|
||||
}
|
||||
|
||||
private val psiFileFactory: PsiFileFactoryImpl = PsiFileFactory.getInstance(environment.project) as PsiFileFactoryImpl
|
||||
|
||||
private fun createDiagnosticHolder() = ConsoleDiagnosticMessageHolder()
|
||||
|
||||
override fun check(state: IReplStageState<*>, codeLine: ReplCodeLine): ReplCheckResult {
|
||||
state.lock.write {
|
||||
val checkerState = state.asState(org.jetbrains.kotlin.scripting.repl.GenericReplCheckerState::class.java)
|
||||
val scriptFileName = makeScriptBaseName(codeLine)
|
||||
val virtualFile =
|
||||
LightVirtualFile(
|
||||
"$scriptFileName${KotlinParserDefinition.STD_SCRIPT_EXT}",
|
||||
KotlinLanguage.INSTANCE,
|
||||
StringUtil.convertLineSeparators(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.reportSyntaxErrors(psiFile, errorHolder)
|
||||
|
||||
if (!syntaxErrorReport.isHasErrors) {
|
||||
checkerState.lastLineState =
|
||||
org.jetbrains.kotlin.scripting.repl.GenericReplCheckerState.LineState(codeLine, psiFile, errorHolder)
|
||||
}
|
||||
|
||||
return when {
|
||||
syntaxErrorReport.isHasErrors && syntaxErrorReport.isAllErrorsAtEof -> ReplCheckResult.Incomplete()
|
||||
syntaxErrorReport.isHasErrors -> ReplCheckResult.Error(errorHolder.renderMessage())
|
||||
else -> ReplCheckResult.Ok()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-131
@@ -1,131 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.scripting.repl
|
||||
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.util.Disposer
|
||||
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.CompilationErrorHandler
|
||||
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.renderer.DescriptorRenderer
|
||||
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition
|
||||
import org.jetbrains.kotlin.scripting.definitions.ScriptDependenciesProvider
|
||||
import java.io.File
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||
import kotlin.concurrent.write
|
||||
import kotlin.script.experimental.api.valueOrNull
|
||||
|
||||
// WARNING: not thread safe, assuming external synchronization
|
||||
|
||||
open class GenericReplCompiler(
|
||||
disposable: Disposable,
|
||||
scriptDefinition: KotlinScriptDefinition,
|
||||
private val compilerConfiguration: CompilerConfiguration,
|
||||
messageCollector: MessageCollector
|
||||
) : ReplCompiler {
|
||||
|
||||
constructor(
|
||||
scriptDefinition: KotlinScriptDefinition,
|
||||
compilerConfiguration: CompilerConfiguration,
|
||||
messageCollector: MessageCollector
|
||||
) : this(Disposer.newDisposable(), scriptDefinition, compilerConfiguration, messageCollector)
|
||||
|
||||
private val checker =
|
||||
GenericReplChecker(disposable, scriptDefinition, compilerConfiguration, messageCollector)
|
||||
|
||||
override fun createState(lock: ReentrantReadWriteLock): IReplStageState<*> =
|
||||
org.jetbrains.kotlin.scripting.repl.GenericReplCompilerState(checker.environment, lock)
|
||||
|
||||
override fun check(state: IReplStageState<*>, codeLine: ReplCodeLine): ReplCheckResult = checker.check(state, codeLine)
|
||||
|
||||
override fun compile(state: IReplStageState<*>, codeLine: ReplCodeLine): ReplCompileResult {
|
||||
state.lock.write {
|
||||
val compilerState = state.asState(org.jetbrains.kotlin.scripting.repl.GenericReplCompilerState::class.java)
|
||||
|
||||
val (psiFile, errorHolder) = run {
|
||||
if (compilerState.lastLineState == null || compilerState.lastLineState!!.codeLine != codeLine) {
|
||||
val res = checker.check(state, codeLine)
|
||||
when (res) {
|
||||
is ReplCheckResult.Incomplete -> return@compile ReplCompileResult.Incomplete()
|
||||
is ReplCheckResult.Error -> return@compile ReplCompileResult.Error(res.message, res.location)
|
||||
is ReplCheckResult.Ok -> {
|
||||
} // continue
|
||||
}
|
||||
}
|
||||
Pair(compilerState.lastLineState!!.psiFile, compilerState.lastLineState!!.errorHolder)
|
||||
}
|
||||
|
||||
val newDependencies =
|
||||
ScriptDependenciesProvider.getInstance(checker.environment.project)?.getScriptConfigurationResult(psiFile)?.valueOrNull()
|
||||
?.legacyDependencies
|
||||
var classpathAddendum: List<File>? = null
|
||||
if (compilerState.lastDependencies != newDependencies) {
|
||||
compilerState.lastDependencies = newDependencies
|
||||
classpathAddendum = newDependencies?.let { checker.environment.updateClasspath(it.classpath.map(::JvmClasspathRoot)) }
|
||||
}
|
||||
|
||||
val analysisResult = compilerState.analyzerEngine.analyzeReplLine(psiFile, codeLine)
|
||||
AnalyzerWithCompilerReport.reportDiagnostics(analysisResult.diagnostics, errorHolder)
|
||||
val scriptDescriptor = when (analysisResult) {
|
||||
is ReplCodeAnalyzer.ReplLineAnalysisResult.WithErrors -> {
|
||||
return ReplCompileResult.Error(errorHolder.renderMessage())
|
||||
}
|
||||
is ReplCodeAnalyzer.ReplLineAnalysisResult.Successful -> {
|
||||
(analysisResult.scriptDescriptor as? ScriptDescriptor)
|
||||
?: error("Unexpected script descriptor type ${analysisResult.scriptDescriptor::class}")
|
||||
}
|
||||
else -> error("Unexpected result ${analysisResult::class.java}")
|
||||
}
|
||||
|
||||
val generationState = GenerationState.Builder(
|
||||
psiFile.project,
|
||||
ClassBuilderFactories.BINARIES,
|
||||
compilerState.analyzerEngine.module,
|
||||
compilerState.analyzerEngine.trace.bindingContext,
|
||||
listOf(psiFile),
|
||||
compilerConfiguration
|
||||
).build()
|
||||
|
||||
generationState.scriptSpecific.earlierScriptsForReplInterpreter = compilerState.history.map { it.item }
|
||||
generationState.beforeCompile()
|
||||
KotlinCodegenFacade.generatePackage(
|
||||
generationState,
|
||||
psiFile.script!!.containingKtFile.packageFqName,
|
||||
setOf(psiFile.script!!.containingKtFile),
|
||||
CompilationErrorHandler.THROW_EXCEPTION
|
||||
)
|
||||
|
||||
val generatedClassname = makeScriptBaseName(codeLine)
|
||||
compilerState.history.push(LineId(codeLine), scriptDescriptor)
|
||||
|
||||
val classes = generationState.factory.asList().map { CompiledClassData(it.relativePath, it.asByteArray()) }
|
||||
|
||||
return ReplCompileResult.CompiledClasses(
|
||||
LineId(codeLine),
|
||||
compilerState.history.map { it.id },
|
||||
generatedClassname,
|
||||
classes,
|
||||
generationState.scriptSpecific.resultFieldName != null,
|
||||
classpathAddendum ?: emptyList(),
|
||||
generationState.scriptSpecific.resultType?.let {
|
||||
DescriptorRenderer.FQ_NAMES_IN_TYPES.renderType(it)
|
||||
},
|
||||
null
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val SCRIPT_RESULT_FIELD_NAME = "\$\$result"
|
||||
}
|
||||
}
|
||||
-242
@@ -1,242 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
@file:Suppress("UNUSED_PARAMETER")
|
||||
|
||||
package org.jetbrains.kotlin.scripting.repl
|
||||
|
||||
import org.jetbrains.kotlin.cli.common.repl.CompiledReplCodeLine
|
||||
import org.jetbrains.kotlin.cli.common.repl.ILineId
|
||||
import org.jetbrains.kotlin.cli.common.repl.ReplCodeLine
|
||||
import org.jetbrains.kotlin.cli.common.repl.ReplHistory
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.NoScopeRecordCliBindingTrace
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM
|
||||
import org.jetbrains.kotlin.container.get
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptorWithResolutionScopes
|
||||
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
|
||||
import org.jetbrains.kotlin.diagnostics.Severity
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.resolve.BindingTraceContext
|
||||
import org.jetbrains.kotlin.resolve.LazyTopDownAnalyzer
|
||||
import org.jetbrains.kotlin.resolve.TopDownAnalysisContext
|
||||
import org.jetbrains.kotlin.resolve.TopDownAnalysisMode
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfoFactory
|
||||
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
|
||||
import org.jetbrains.kotlin.resolve.lazy.*
|
||||
import org.jetbrains.kotlin.resolve.lazy.data.KtClassLikeInfo
|
||||
import org.jetbrains.kotlin.resolve.lazy.declarations.*
|
||||
import org.jetbrains.kotlin.resolve.scopes.ImportingScope
|
||||
import org.jetbrains.kotlin.resolve.scopes.utils.parentsWithSelf
|
||||
import org.jetbrains.kotlin.resolve.scopes.utils.replaceImportingScopes
|
||||
import org.jetbrains.kotlin.scripting.definitions.ScriptPriorities
|
||||
|
||||
class ReplCodeAnalyzer(environment: KotlinCoreEnvironment) {
|
||||
private val topDownAnalysisContext: TopDownAnalysisContext
|
||||
private val topDownAnalyzer: LazyTopDownAnalyzer
|
||||
private val resolveSession: ResolveSession
|
||||
private val scriptDeclarationFactory: ScriptMutableDeclarationProviderFactory
|
||||
private val replState = ResettableAnalyzerState()
|
||||
|
||||
val module: ModuleDescriptorImpl
|
||||
|
||||
val trace: BindingTraceContext = NoScopeRecordCliBindingTrace()
|
||||
|
||||
init {
|
||||
// Module source scope is empty because all binary classes are in the dependency module, and all source classes are guaranteed
|
||||
// to be found via ResolveSession. The latter is true as long as light classes are not needed in REPL (which is currently true
|
||||
// because no symbol declared in the REPL session can be used from Java)
|
||||
val container = TopDownAnalyzerFacadeForJVM.createContainer(
|
||||
environment.project,
|
||||
emptyList(),
|
||||
trace,
|
||||
environment.configuration,
|
||||
environment::createPackagePartProvider,
|
||||
{ _, _ -> ScriptMutableDeclarationProviderFactory() }
|
||||
)
|
||||
|
||||
this.module = container.get()
|
||||
this.scriptDeclarationFactory = container.get()
|
||||
this.resolveSession = container.get()
|
||||
this.topDownAnalysisContext = TopDownAnalysisContext(
|
||||
TopDownAnalysisMode.TopLevelDeclarations, DataFlowInfoFactory.EMPTY, resolveSession.declarationScopeProvider
|
||||
)
|
||||
this.topDownAnalyzer = container.get()
|
||||
}
|
||||
|
||||
interface ReplLineAnalysisResult {
|
||||
val scriptDescriptor: ClassDescriptorWithResolutionScopes?
|
||||
val diagnostics: Diagnostics
|
||||
|
||||
data class Successful(override val scriptDescriptor: ClassDescriptorWithResolutionScopes, override val diagnostics: Diagnostics) :
|
||||
ReplLineAnalysisResult
|
||||
|
||||
data class WithErrors(override val diagnostics: Diagnostics) :
|
||||
ReplLineAnalysisResult {
|
||||
override val scriptDescriptor: ClassDescriptorWithResolutionScopes? get() = null
|
||||
}
|
||||
}
|
||||
|
||||
fun resetToLine(lineId: ILineId): List<ReplCodeLine> = replState.resetToLine(lineId)
|
||||
|
||||
fun reset(): List<ReplCodeLine> = replState.reset()
|
||||
|
||||
fun analyzeReplLine(psiFile: KtFile, codeLine: ReplCodeLine): ReplLineAnalysisResult {
|
||||
topDownAnalysisContext.scripts.clear()
|
||||
trace.clearDiagnostics()
|
||||
|
||||
psiFile.script!!.putUserData(ScriptPriorities.PRIORITY_KEY, codeLine.no)
|
||||
|
||||
return doAnalyze(psiFile, emptyList(), codeLine)
|
||||
}
|
||||
|
||||
fun analyzeReplLineWithImportedScripts(psiFile: KtFile, importedScripts: List<KtFile>, codeLine: ReplCodeLine): ReplLineAnalysisResult {
|
||||
topDownAnalysisContext.scripts.clear()
|
||||
trace.clearDiagnostics()
|
||||
|
||||
psiFile.script!!.putUserData(ScriptPriorities.PRIORITY_KEY, codeLine.no)
|
||||
|
||||
return doAnalyze(psiFile, importedScripts, codeLine)
|
||||
}
|
||||
|
||||
private fun doAnalyze(linePsi: KtFile, importedScripts: List<KtFile>, codeLine: ReplCodeLine): ReplLineAnalysisResult {
|
||||
scriptDeclarationFactory.setDelegateFactory(FileBasedDeclarationProviderFactory(resolveSession.storageManager, listOf(linePsi)))
|
||||
replState.submitLine(linePsi, codeLine)
|
||||
|
||||
val context = topDownAnalyzer.analyzeDeclarations(topDownAnalysisContext.topDownAnalysisMode, listOf(linePsi) + importedScripts)
|
||||
|
||||
val diagnostics = trace.bindingContext.diagnostics
|
||||
val hasErrors = diagnostics.any { it.severity == Severity.ERROR }
|
||||
return if (hasErrors) {
|
||||
replState.lineFailure(linePsi, codeLine)
|
||||
ReplLineAnalysisResult.WithErrors(diagnostics)
|
||||
} else {
|
||||
val scriptDescriptor = context.scripts[linePsi.script]!!
|
||||
replState.lineSuccess(linePsi, codeLine, scriptDescriptor)
|
||||
ReplLineAnalysisResult.Successful(scriptDescriptor, diagnostics)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class ScriptMutableDeclarationProviderFactory : DeclarationProviderFactory {
|
||||
private lateinit var delegateFactory: DeclarationProviderFactory
|
||||
private lateinit var rootPackageProvider: AdaptablePackageMemberDeclarationProvider
|
||||
|
||||
fun setDelegateFactory(delegateFactory: DeclarationProviderFactory) {
|
||||
this.delegateFactory = delegateFactory
|
||||
|
||||
val provider = delegateFactory.getPackageMemberDeclarationProvider(FqName.ROOT)!!
|
||||
try {
|
||||
rootPackageProvider.addDelegateProvider(provider)
|
||||
} catch (e: UninitializedPropertyAccessException) {
|
||||
rootPackageProvider =
|
||||
AdaptablePackageMemberDeclarationProvider(
|
||||
provider
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getClassMemberDeclarationProvider(classLikeInfo: KtClassLikeInfo): ClassMemberDeclarationProvider {
|
||||
return delegateFactory.getClassMemberDeclarationProvider(classLikeInfo)
|
||||
}
|
||||
|
||||
override fun getPackageMemberDeclarationProvider(packageFqName: FqName): PackageMemberDeclarationProvider? {
|
||||
if (packageFqName.isRoot) {
|
||||
return rootPackageProvider
|
||||
}
|
||||
|
||||
return delegateFactory.getPackageMemberDeclarationProvider(packageFqName)
|
||||
}
|
||||
|
||||
override fun diagnoseMissingPackageFragment(fqName: FqName, file: KtFile?) {
|
||||
delegateFactory.diagnoseMissingPackageFragment(fqName, file)
|
||||
}
|
||||
|
||||
class AdaptablePackageMemberDeclarationProvider(
|
||||
private var delegateProvider: PackageMemberDeclarationProvider
|
||||
) : org.jetbrains.kotlin.scripting.repl.DelegatePackageMemberDeclarationProvider(delegateProvider) {
|
||||
fun addDelegateProvider(provider: PackageMemberDeclarationProvider) {
|
||||
delegateProvider = CombinedPackageMemberDeclarationProvider(listOf(provider, delegateProvider))
|
||||
|
||||
delegate = delegateProvider
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: merge with org.jetbrains.kotlin.resolve.repl.ReplState when switching to new REPL infrastructure everywhere
|
||||
// TODO: review its place in the extracted state infrastructure (now the analyzer itself is a part of the state)
|
||||
class ResettableAnalyzerState {
|
||||
private val successfulLines = ReplHistory<LineInfo.SuccessfulLine>()
|
||||
private val submittedLines = hashMapOf<KtFile, LineInfo>()
|
||||
|
||||
fun resetToLine(lineId: ILineId): List<ReplCodeLine> {
|
||||
val removed = successfulLines.resetToLine(lineId.no)
|
||||
removed.forEach { submittedLines.remove(it.second.linePsi) }
|
||||
return removed.map { it.first }
|
||||
}
|
||||
|
||||
fun reset(): List<ReplCodeLine> {
|
||||
submittedLines.clear()
|
||||
return successfulLines.reset().map { it.first }
|
||||
}
|
||||
|
||||
fun submitLine(ktFile: KtFile, codeLine: ReplCodeLine) {
|
||||
val line = LineInfo.SubmittedLine(
|
||||
ktFile,
|
||||
successfulLines.lastValue()
|
||||
)
|
||||
submittedLines[ktFile] = line
|
||||
ktFile.fileScopesCustomizer = object : FileScopesCustomizer {
|
||||
override fun createFileScopes(fileScopeFactory: FileScopeFactory): FileScopes {
|
||||
return lineInfo(ktFile)?.let { computeFileScopes(it, fileScopeFactory) } ?: fileScopeFactory.createScopesForFile(ktFile)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun lineSuccess(ktFile: KtFile, codeLine: ReplCodeLine, scriptDescriptor: ClassDescriptorWithResolutionScopes) {
|
||||
val successfulLine = LineInfo.SuccessfulLine(
|
||||
ktFile,
|
||||
successfulLines.lastValue(),
|
||||
scriptDescriptor
|
||||
)
|
||||
submittedLines[ktFile] = successfulLine
|
||||
successfulLines.add(CompiledReplCodeLine(ktFile.name, codeLine), successfulLine)
|
||||
}
|
||||
|
||||
fun lineFailure(ktFile: KtFile, codeLine: ReplCodeLine) {
|
||||
submittedLines[ktFile] = LineInfo.FailedLine(
|
||||
ktFile,
|
||||
successfulLines.lastValue()
|
||||
)
|
||||
}
|
||||
|
||||
private fun lineInfo(ktFile: KtFile) = submittedLines[ktFile]
|
||||
|
||||
// use sealed?
|
||||
private sealed class LineInfo {
|
||||
abstract val linePsi: KtFile
|
||||
abstract val parentLine: SuccessfulLine?
|
||||
|
||||
class SubmittedLine(override val linePsi: KtFile, override val parentLine: SuccessfulLine?) : LineInfo()
|
||||
class SuccessfulLine(
|
||||
override val linePsi: KtFile,
|
||||
override val parentLine: SuccessfulLine?,
|
||||
val lineDescriptor: ClassDescriptorWithResolutionScopes
|
||||
) : LineInfo()
|
||||
|
||||
class FailedLine(override val linePsi: KtFile, override val parentLine: SuccessfulLine?) : LineInfo()
|
||||
}
|
||||
|
||||
private fun computeFileScopes(lineInfo: LineInfo, fileScopeFactory: FileScopeFactory): FileScopes? {
|
||||
// create scope that wraps previous line lexical scope and adds imports from this line
|
||||
val lexicalScopeAfterLastLine = lineInfo.parentLine?.lineDescriptor?.scopeForInitializerResolution ?: return null
|
||||
val lastLineImports = lexicalScopeAfterLastLine.parentsWithSelf.first { it is ImportingScope } as ImportingScope
|
||||
val scopesForThisLine = fileScopeFactory.createScopesForFile(lineInfo.linePsi, lastLineImports)
|
||||
val combinedLexicalScopes = lexicalScopeAfterLastLine.replaceImportingScopes(scopesForThisLine.importingScope)
|
||||
return FileScopes(combinedLexicalScopes, scopesForThisLine.importingScope, scopesForThisLine.importForceResolver)
|
||||
}
|
||||
}
|
||||
}
|
||||
-31
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.scripting.repl
|
||||
|
||||
import org.jetbrains.kotlin.scripting.repl.writer.ReplWriter
|
||||
import java.io.PrintWriter
|
||||
import java.io.StringWriter
|
||||
|
||||
interface ReplExceptionReporter {
|
||||
fun report(e: Throwable)
|
||||
|
||||
companion object DoNothing : ReplExceptionReporter {
|
||||
override fun report(e: Throwable) {}
|
||||
}
|
||||
}
|
||||
|
||||
class IdeReplExceptionReporter(private val replWriter: ReplWriter) : ReplExceptionReporter {
|
||||
override fun report(e: Throwable) {
|
||||
val stringWriter = StringWriter()
|
||||
val printWriter = PrintWriter(stringWriter)
|
||||
e.printStackTrace(printWriter)
|
||||
|
||||
val writerString = stringWriter.toString()
|
||||
val internalErrorText = if (writerString.isEmpty()) "Unknown error" else writerString
|
||||
|
||||
replWriter.sendInternalErrorReport(internalErrorText)
|
||||
}
|
||||
}
|
||||
-167
@@ -1,167 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.scripting.repl
|
||||
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
||||
import org.jetbrains.kotlin.cli.common.messages.GroupingMessageCollector
|
||||
import org.jetbrains.kotlin.cli.common.repl.ReplEvalResult
|
||||
import org.jetbrains.kotlin.cli.common.repl.replUnescapeLineBreaks
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.config.KotlinCompilerVersion
|
||||
import org.jetbrains.kotlin.scripting.repl.configuration.ConsoleReplConfiguration
|
||||
import org.jetbrains.kotlin.scripting.repl.configuration.IdeReplConfiguration
|
||||
import org.jetbrains.kotlin.scripting.repl.configuration.ReplConfiguration
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.io.PrintWriter
|
||||
import java.util.*
|
||||
import java.util.concurrent.Callable
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.concurrent.Future
|
||||
|
||||
class ReplFromTerminal(
|
||||
disposable: Disposable,
|
||||
compilerConfiguration: CompilerConfiguration,
|
||||
private val replConfiguration: ReplConfiguration
|
||||
) {
|
||||
private val replInitializer: Future<ReplInterpreter> = Executors.newSingleThreadExecutor().submit(Callable {
|
||||
ReplInterpreter(disposable, compilerConfiguration, replConfiguration)
|
||||
})
|
||||
|
||||
private val replInterpreter: ReplInterpreter
|
||||
get() = replInitializer.get()
|
||||
|
||||
private val writer get() = replConfiguration.writer
|
||||
|
||||
private val messageCollector = compilerConfiguration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
|
||||
|
||||
private fun doRun() {
|
||||
try {
|
||||
with(writer) {
|
||||
printlnWelcomeMessage(
|
||||
"Welcome to Kotlin version ${KotlinCompilerVersion.VERSION} " +
|
||||
"(JRE ${System.getProperty("java.runtime.version")})"
|
||||
)
|
||||
printlnWelcomeMessage("Type :help for help, :quit for quit")
|
||||
}
|
||||
|
||||
// Display compiler messages related to configuration and CLI arguments, quit if there are errors
|
||||
val hasErrors = messageCollector.hasErrors()
|
||||
(messageCollector as? GroupingMessageCollector)?.flush()
|
||||
if (hasErrors) return
|
||||
|
||||
var next = WhatNextAfterOneLine.READ_LINE
|
||||
while (true) {
|
||||
next = one(next)
|
||||
if (next == WhatNextAfterOneLine.QUIT) {
|
||||
break
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
replConfiguration.exceptionReporter.report(e)
|
||||
throw e
|
||||
} finally {
|
||||
try {
|
||||
replConfiguration.commandReader.flushHistory()
|
||||
} catch (e: Exception) {
|
||||
replConfiguration.exceptionReporter.report(e)
|
||||
throw e
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
enum class WhatNextAfterOneLine {
|
||||
READ_LINE,
|
||||
INCOMPLETE,
|
||||
QUIT
|
||||
}
|
||||
|
||||
private fun one(next: WhatNextAfterOneLine): WhatNextAfterOneLine {
|
||||
var line = replConfiguration.commandReader.readLine(next) ?: return WhatNextAfterOneLine.QUIT
|
||||
|
||||
line = line.replUnescapeLineBreaks()
|
||||
|
||||
if (line.startsWith(":") && (line.length == 1 || line[1] != ':')) {
|
||||
val notQuit = oneCommand(line.substring(1))
|
||||
return if (notQuit) WhatNextAfterOneLine.READ_LINE else WhatNextAfterOneLine.QUIT
|
||||
}
|
||||
|
||||
val lineResult = eval(line)
|
||||
return if (lineResult is ReplEvalResult.Incomplete) {
|
||||
WhatNextAfterOneLine.INCOMPLETE
|
||||
} else {
|
||||
WhatNextAfterOneLine.READ_LINE
|
||||
}
|
||||
}
|
||||
|
||||
private fun eval(line: String): ReplEvalResult {
|
||||
val evalResult = replInterpreter.eval(line)
|
||||
when (evalResult) {
|
||||
is ReplEvalResult.ValueResult, is ReplEvalResult.UnitResult -> {
|
||||
writer.notifyCommandSuccess()
|
||||
if (evalResult is ReplEvalResult.ValueResult) {
|
||||
writer.outputCommandResult(evalResult.toString())
|
||||
}
|
||||
}
|
||||
is ReplEvalResult.Error.Runtime -> writer.outputRuntimeError(evalResult.message)
|
||||
is ReplEvalResult.Error.CompileTime -> writer.outputRuntimeError(evalResult.message)
|
||||
is ReplEvalResult.Incomplete -> writer.notifyIncomplete()
|
||||
}
|
||||
return evalResult
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
private fun oneCommand(command: String): Boolean {
|
||||
val split = splitCommand(command)
|
||||
if (split.isNotEmpty() && command == "help") {
|
||||
writer.printlnHelpMessage(
|
||||
"Available commands:\n" +
|
||||
":help show this help\n" +
|
||||
":quit exit the interpreter\n" +
|
||||
":dump bytecode dump classes to terminal\n" +
|
||||
":load <file> load script from specified file"
|
||||
)
|
||||
return true
|
||||
} else if (split.size >= 2 && split[0] == "dump" && split[1] == "bytecode") {
|
||||
replInterpreter.dumpClasses(PrintWriter(System.out))
|
||||
return true
|
||||
} else if (split.isNotEmpty() && split[0] == "quit") {
|
||||
return false
|
||||
} else if (split.size >= 2 && split[0] == "load") {
|
||||
val fileName = split[1]
|
||||
try {
|
||||
val scriptText = FileUtil.loadFile(File(fileName))
|
||||
eval(scriptText)
|
||||
} catch (e: IOException) {
|
||||
writer.outputCompileError("Can not load script: ${e.message}")
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
writer.printlnHelpMessage("Unknown command\n" + "Type :help for help")
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private fun splitCommand(command: String): List<String> {
|
||||
return listOf(*command.split(" ".toRegex()).dropLastWhile(String::isEmpty).toTypedArray())
|
||||
}
|
||||
|
||||
fun run(disposable: Disposable, configuration: CompilerConfiguration) {
|
||||
val replIdeMode = System.getProperty("kotlin.repl.ideMode") == "true"
|
||||
val replConfiguration = if (replIdeMode) IdeReplConfiguration() else ConsoleReplConfiguration()
|
||||
return try {
|
||||
ReplFromTerminal(disposable, configuration, replConfiguration).doRun()
|
||||
} catch (e: Exception) {
|
||||
replConfiguration.exceptionReporter.report(e)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-122
@@ -1,122 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.scripting.repl
|
||||
|
||||
import com.intellij.openapi.Disposable
|
||||
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
|
||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
||||
import org.jetbrains.kotlin.cli.common.messages.MessageRenderer
|
||||
import org.jetbrains.kotlin.cli.common.repl.*
|
||||
import org.jetbrains.kotlin.cli.jvm.config.JvmClasspathRoot
|
||||
import org.jetbrains.kotlin.cli.jvm.config.JvmModulePathRoot
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition
|
||||
import org.jetbrains.kotlin.scripting.repl.configuration.ReplConfiguration
|
||||
import java.io.PrintWriter
|
||||
import java.net.URLClassLoader
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
class ReplInterpreter(
|
||||
disposable: Disposable,
|
||||
private val configuration: CompilerConfiguration,
|
||||
private val replConfiguration: ReplConfiguration
|
||||
) {
|
||||
private val lineNumber = AtomicInteger()
|
||||
|
||||
private val previousIncompleteLines = arrayListOf<String>()
|
||||
|
||||
private val classpathRoots = configuration.getList(CLIConfigurationKeys.CONTENT_ROOTS).mapNotNull { root ->
|
||||
when (root) {
|
||||
is JvmModulePathRoot -> root.file // TODO: only add required modules
|
||||
is JvmClasspathRoot -> root.file
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
private val classLoader = ReplClassLoader(URLClassLoader(classpathRoots.map { it.toURI().toURL() }.toTypedArray(), null))
|
||||
|
||||
private val messageCollector = object : MessageCollector {
|
||||
private var hasErrors = false
|
||||
private val messageRenderer = MessageRenderer.WITHOUT_PATHS
|
||||
|
||||
override fun clear() {
|
||||
hasErrors = false
|
||||
}
|
||||
|
||||
override fun report(severity: CompilerMessageSeverity, message: String, location: CompilerMessageLocation?) {
|
||||
val msg = messageRenderer.render(severity, message, location).trimEnd()
|
||||
with(replConfiguration.writer) {
|
||||
when (severity) {
|
||||
CompilerMessageSeverity.EXCEPTION -> sendInternalErrorReport(msg)
|
||||
CompilerMessageSeverity.ERROR -> outputCompileError(msg)
|
||||
CompilerMessageSeverity.STRONG_WARNING -> {
|
||||
} // TODO consider reporting this and two below
|
||||
CompilerMessageSeverity.WARNING -> {
|
||||
}
|
||||
CompilerMessageSeverity.INFO -> {
|
||||
}
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun hasErrors(): Boolean = hasErrors
|
||||
}
|
||||
|
||||
// TODO: add script definition with project-based resolving for IDEA repl
|
||||
private val scriptCompiler: ReplCompiler by lazy {
|
||||
GenericReplCompiler(
|
||||
disposable,
|
||||
REPL_LINE_AS_SCRIPT_DEFINITION,
|
||||
configuration,
|
||||
messageCollector
|
||||
)
|
||||
}
|
||||
private val scriptEvaluator: ReplFullEvaluator by lazy {
|
||||
GenericReplCompilingEvaluator(scriptCompiler, classpathRoots, classLoader, null, ReplRepeatingMode.REPEAT_ANY_PREVIOUS)
|
||||
}
|
||||
|
||||
private val evalState by lazy { scriptEvaluator.createState() }
|
||||
|
||||
fun eval(line: String): ReplEvalResult {
|
||||
val fullText = (previousIncompleteLines + line).joinToString(separator = "\n")
|
||||
|
||||
try {
|
||||
val evalRes = scriptEvaluator.compileAndEval(
|
||||
evalState,
|
||||
ReplCodeLine(lineNumber.getAndIncrement(), 0, fullText),
|
||||
null,
|
||||
object : InvokeWrapper {
|
||||
override fun <T> invoke(body: () -> T): T = replConfiguration.executionInterceptor.execute(body)
|
||||
}
|
||||
)
|
||||
|
||||
when {
|
||||
evalRes !is ReplEvalResult.Incomplete -> previousIncompleteLines.clear()
|
||||
replConfiguration.allowIncompleteLines -> previousIncompleteLines.add(line)
|
||||
else -> return ReplEvalResult.Error.CompileTime("incomplete code")
|
||||
}
|
||||
return evalRes
|
||||
} catch (e: Throwable) {
|
||||
val writer = PrintWriter(System.err)
|
||||
classLoader.dumpClasses(writer)
|
||||
writer.flush()
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
fun dumpClasses(out: PrintWriter) {
|
||||
classLoader.dumpClasses(out)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val REPL_LINE_AS_SCRIPT_DEFINITION = object : KotlinScriptDefinition(Any::class) {
|
||||
override val name = "Kotlin REPL"
|
||||
}
|
||||
}
|
||||
}
|
||||
-28
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.scripting.repl.configuration
|
||||
|
||||
import org.jetbrains.kotlin.scripting.repl.ReplExceptionReporter
|
||||
import org.jetbrains.kotlin.scripting.repl.messages.ConsoleDiagnosticMessageHolder
|
||||
import org.jetbrains.kotlin.scripting.repl.reader.ConsoleReplCommandReader
|
||||
import org.jetbrains.kotlin.scripting.repl.writer.ConsoleReplWriter
|
||||
|
||||
class ConsoleReplConfiguration : ReplConfiguration {
|
||||
override val writer = ConsoleReplWriter()
|
||||
|
||||
override val exceptionReporter
|
||||
get() = ReplExceptionReporter
|
||||
|
||||
override val commandReader = ConsoleReplCommandReader()
|
||||
|
||||
override val allowIncompleteLines: Boolean
|
||||
get() = true
|
||||
|
||||
override val executionInterceptor
|
||||
get() = SnippetExecutionInterceptor
|
||||
|
||||
override fun createDiagnosticHolder() = ConsoleDiagnosticMessageHolder()
|
||||
}
|
||||
-57
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.scripting.repl.configuration
|
||||
|
||||
import org.jetbrains.kotlin.scripting.repl.IdeReplExceptionReporter
|
||||
import org.jetbrains.kotlin.scripting.repl.ReplExceptionReporter
|
||||
import org.jetbrains.kotlin.scripting.repl.messages.IdeDiagnosticMessageHolder
|
||||
import org.jetbrains.kotlin.scripting.repl.reader.IdeReplCommandReader
|
||||
import org.jetbrains.kotlin.scripting.repl.reader.ReplCommandReader
|
||||
import org.jetbrains.kotlin.scripting.repl.reader.ReplSystemInWrapper
|
||||
import org.jetbrains.kotlin.scripting.repl.writer.IdeSystemOutWrapperReplWriter
|
||||
import org.jetbrains.kotlin.scripting.repl.writer.ReplWriter
|
||||
|
||||
class IdeReplConfiguration : ReplConfiguration {
|
||||
override val allowIncompleteLines: Boolean
|
||||
get() = false
|
||||
|
||||
override val executionInterceptor: SnippetExecutionInterceptor = object :
|
||||
SnippetExecutionInterceptor {
|
||||
override fun <T> execute(block: () -> T): T {
|
||||
try {
|
||||
sinWrapper.isReplScriptExecuting = true
|
||||
return block()
|
||||
} finally {
|
||||
sinWrapper.isReplScriptExecuting = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun createDiagnosticHolder() = IdeDiagnosticMessageHolder()
|
||||
|
||||
override val writer: ReplWriter
|
||||
override val exceptionReporter: ReplExceptionReporter
|
||||
override val commandReader: ReplCommandReader
|
||||
|
||||
val sinWrapper: ReplSystemInWrapper
|
||||
|
||||
init {
|
||||
// wrapper for `out` is required to escape every input in [ideMode];
|
||||
// if [ideMode == false] then just redirects all input to [System.out]
|
||||
// if user calls [System.setOut(...)] then undefined behaviour
|
||||
val soutWrapper = IdeSystemOutWrapperReplWriter(System.out)
|
||||
System.setOut(soutWrapper)
|
||||
|
||||
// wrapper for `in` is required to give user possibility of calling
|
||||
// [readLine] from ide-console repl
|
||||
sinWrapper = ReplSystemInWrapper(System.`in`, soutWrapper)
|
||||
System.setIn(sinWrapper)
|
||||
|
||||
writer = soutWrapper
|
||||
exceptionReporter = IdeReplExceptionReporter(writer)
|
||||
commandReader = IdeReplCommandReader()
|
||||
}
|
||||
}
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.scripting.repl.configuration
|
||||
|
||||
import org.jetbrains.kotlin.scripting.repl.ReplExceptionReporter
|
||||
import org.jetbrains.kotlin.scripting.repl.messages.DiagnosticMessageHolder
|
||||
import org.jetbrains.kotlin.scripting.repl.reader.ReplCommandReader
|
||||
import org.jetbrains.kotlin.scripting.repl.writer.ReplWriter
|
||||
|
||||
interface ReplConfiguration {
|
||||
val writer: ReplWriter
|
||||
val exceptionReporter: ReplExceptionReporter
|
||||
val commandReader: ReplCommandReader
|
||||
val allowIncompleteLines: Boolean
|
||||
|
||||
val executionInterceptor: SnippetExecutionInterceptor
|
||||
fun createDiagnosticHolder(): DiagnosticMessageHolder
|
||||
}
|
||||
-14
@@ -1,14 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.scripting.repl.configuration
|
||||
|
||||
interface SnippetExecutionInterceptor {
|
||||
fun <T> execute(block: () -> T): T
|
||||
|
||||
companion object Plain : SnippetExecutionInterceptor {
|
||||
override fun <T> execute(block: () -> T) = block()
|
||||
}
|
||||
}
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.scripting.repl.messages
|
||||
|
||||
import org.jetbrains.kotlin.cli.common.messages.GroupingMessageCollector
|
||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollectorBasedReporter
|
||||
import org.jetbrains.kotlin.cli.common.messages.MessageRenderer
|
||||
import org.jetbrains.kotlin.cli.common.messages.PrintingMessageCollector
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.PrintStream
|
||||
|
||||
class ConsoleDiagnosticMessageHolder : MessageCollectorBasedReporter, DiagnosticMessageHolder {
|
||||
private val outputStream = ByteArrayOutputStream()
|
||||
|
||||
override val messageCollector: GroupingMessageCollector = GroupingMessageCollector(
|
||||
PrintingMessageCollector(PrintStream(outputStream), MessageRenderer.WITHOUT_PATHS, false),
|
||||
false
|
||||
)
|
||||
|
||||
override fun renderMessage(): String {
|
||||
messageCollector.flush()
|
||||
return outputStream.toString("UTF-8")
|
||||
}
|
||||
}
|
||||
-23
@@ -1,23 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.scripting.repl.messages
|
||||
|
||||
import org.jetbrains.kotlin.cli.common.messages.DiagnosticMessageReporter
|
||||
|
||||
interface DiagnosticMessageHolder : DiagnosticMessageReporter {
|
||||
fun renderMessage(): String
|
||||
}
|
||||
-46
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.scripting.repl.messages
|
||||
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import com.intellij.psi.PsiFile
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticUtils
|
||||
import org.w3c.dom.ls.DOMImplementationLS
|
||||
import javax.xml.parsers.DocumentBuilderFactory
|
||||
|
||||
class IdeDiagnosticMessageHolder : DiagnosticMessageHolder {
|
||||
private val diagnostics = arrayListOf<Pair<Diagnostic, String>>()
|
||||
|
||||
override fun report(diagnostic: Diagnostic, file: PsiFile, render: String) {
|
||||
diagnostics.add(Pair(diagnostic, render))
|
||||
}
|
||||
|
||||
override fun renderMessage(): String {
|
||||
val docFactory = DocumentBuilderFactory.newInstance()
|
||||
val docBuilder = docFactory.newDocumentBuilder()
|
||||
val errorReport = docBuilder.newDocument()
|
||||
|
||||
val rootElement = errorReport.createElement("report")
|
||||
errorReport.appendChild(rootElement)
|
||||
|
||||
for ((diagnostic, message) in diagnostics) {
|
||||
val errorRange = DiagnosticUtils.firstRange(diagnostic.textRanges)
|
||||
|
||||
val reportEntry = errorReport.createElement("reportEntry")
|
||||
reportEntry.setAttribute("severity", diagnostic.severity.toString())
|
||||
reportEntry.setAttribute("rangeStart", errorRange.startOffset.toString())
|
||||
reportEntry.setAttribute("rangeEnd", errorRange.endOffset.toString())
|
||||
reportEntry.appendChild(errorReport.createTextNode(StringUtil.escapeXml(message)))
|
||||
|
||||
rootElement.appendChild(reportEntry)
|
||||
}
|
||||
|
||||
val domImplementation = errorReport.implementation as DOMImplementationLS
|
||||
val lsSerializer = domImplementation.createLSSerializer()
|
||||
return lsSerializer.writeToString(errorReport)
|
||||
}
|
||||
}
|
||||
-48
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.scripting.repl.reader
|
||||
|
||||
import org.jetbrains.kotlin.scripting.repl.ReplFromTerminal
|
||||
import org.jline.reader.EndOfFileException
|
||||
import org.jline.reader.LineReader
|
||||
import org.jline.reader.LineReaderBuilder
|
||||
import org.jline.reader.UserInterruptException
|
||||
import org.jline.terminal.TerminalBuilder
|
||||
import java.io.File
|
||||
import java.util.logging.Level
|
||||
import java.util.logging.Logger
|
||||
|
||||
class ConsoleReplCommandReader : ReplCommandReader {
|
||||
private val lineReader = LineReaderBuilder.builder()
|
||||
.appName("kotlin")
|
||||
.terminal(TerminalBuilder.terminal())
|
||||
.variable(LineReader.HISTORY_FILE, File(File(System.getProperty("user.home")), ".kotlinc_history").absolutePath)
|
||||
.build()
|
||||
.apply {
|
||||
setOpt(LineReader.Option.DISABLE_EVENT_EXPANSION)
|
||||
}
|
||||
|
||||
override fun readLine(next: ReplFromTerminal.WhatNextAfterOneLine): String? {
|
||||
val prompt = if (next == ReplFromTerminal.WhatNextAfterOneLine.INCOMPLETE) "... " else ">>> "
|
||||
return try {
|
||||
lineReader.readLine(prompt)
|
||||
} catch (e: UserInterruptException) {
|
||||
println("<interrupted>")
|
||||
System.out.flush()
|
||||
""
|
||||
} catch (e: EndOfFileException) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
override fun flushHistory() = lineReader.history.save()
|
||||
|
||||
private companion object {
|
||||
init {
|
||||
Logger.getLogger("org.jline").level = Level.OFF
|
||||
}
|
||||
}
|
||||
}
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.scripting.repl.reader
|
||||
|
||||
import org.jetbrains.kotlin.scripting.repl.ReplFromTerminal
|
||||
|
||||
class IdeReplCommandReader : ReplCommandReader {
|
||||
override fun readLine(next: ReplFromTerminal.WhatNextAfterOneLine) = readLine()
|
||||
override fun flushHistory() = Unit
|
||||
}
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.scripting.repl.reader
|
||||
|
||||
import org.jetbrains.kotlin.scripting.repl.ReplFromTerminal
|
||||
|
||||
interface ReplCommandReader {
|
||||
fun readLine(next: ReplFromTerminal.WhatNextAfterOneLine): String?
|
||||
fun flushHistory()
|
||||
}
|
||||
-95
@@ -1,95 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.scripting.repl.reader
|
||||
|
||||
import org.jetbrains.kotlin.cli.common.repl.replUnescapeLineBreaks
|
||||
import org.jetbrains.kotlin.scripting.repl.writer.ReplWriter
|
||||
import org.w3c.dom.Element
|
||||
import org.xml.sax.InputSource
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.InputStream
|
||||
import javax.xml.parsers.DocumentBuilderFactory
|
||||
|
||||
class ReplSystemInWrapper(
|
||||
private val stdin: InputStream,
|
||||
private val replWriter: ReplWriter
|
||||
) : InputStream() {
|
||||
private var isXmlIncomplete = true
|
||||
private var isLastByteProcessed = false
|
||||
private var isReadLineStartSent = false
|
||||
private var byteBuilder = ByteArrayOutputStream()
|
||||
private var curBytePos = 0
|
||||
private var inputByteArray = byteArrayOf()
|
||||
|
||||
private val isAtBufferEnd: Boolean
|
||||
get() = curBytePos == inputByteArray.size
|
||||
|
||||
@Volatile
|
||||
var isReplScriptExecuting = false
|
||||
|
||||
override fun read(): Int {
|
||||
if (isLastByteProcessed) {
|
||||
if (isReplScriptExecuting) {
|
||||
isReadLineStartSent = false
|
||||
replWriter.notifyReadLineEnd()
|
||||
}
|
||||
|
||||
isLastByteProcessed = false
|
||||
return -1
|
||||
}
|
||||
|
||||
while (isXmlIncomplete) {
|
||||
if (!isReadLineStartSent && isReplScriptExecuting) {
|
||||
replWriter.notifyReadLineStart()
|
||||
isReadLineStartSent = true
|
||||
}
|
||||
|
||||
byteBuilder.write(stdin.read())
|
||||
|
||||
if (byteBuilder.toString().endsWith('\n')) {
|
||||
isXmlIncomplete = false
|
||||
isLastByteProcessed = false
|
||||
|
||||
inputByteArray = parseInput().toByteArray()
|
||||
}
|
||||
}
|
||||
|
||||
val nextByte = inputByteArray[curBytePos++].toInt()
|
||||
resetBufferIfNeeded()
|
||||
return nextByte
|
||||
}
|
||||
|
||||
private fun parseInput(): String {
|
||||
val xmlInput = byteBuilder.toString()
|
||||
val unescapedXml = parseXml(xmlInput)
|
||||
|
||||
return if (isReplScriptExecuting)
|
||||
unescapedXml.replUnescapeLineBreaks()
|
||||
else
|
||||
unescapedXml
|
||||
}
|
||||
|
||||
private fun resetBufferIfNeeded() {
|
||||
if (isAtBufferEnd) {
|
||||
isXmlIncomplete = true
|
||||
byteBuilder = ByteArrayOutputStream()
|
||||
curBytePos = 0
|
||||
isLastByteProcessed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseXml(inputMessage: String): String {
|
||||
fun strToSource(s: String) = InputSource(ByteArrayInputStream(s.toByteArray()))
|
||||
|
||||
val docFactory = DocumentBuilderFactory.newInstance()
|
||||
val docBuilder = docFactory.newDocumentBuilder()
|
||||
val input = docBuilder.parse(strToSource(inputMessage))
|
||||
|
||||
val root = input.firstChild as Element
|
||||
return root.textContent
|
||||
}
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.scripting.repl.writer
|
||||
|
||||
class ConsoleReplWriter : ReplWriter {
|
||||
override fun printlnWelcomeMessage(x: String) = println(x)
|
||||
override fun printlnHelpMessage(x: String) = println(x)
|
||||
override fun outputCompileError(x: String) = println(x)
|
||||
override fun outputCommandResult(x: String) = println(x)
|
||||
override fun outputRuntimeError(x: String) = println(x)
|
||||
|
||||
override fun notifyReadLineStart() {}
|
||||
override fun notifyReadLineEnd() {}
|
||||
override fun notifyIncomplete() {}
|
||||
override fun notifyCommandSuccess() {}
|
||||
override fun sendInternalErrorReport(x: String) {}
|
||||
}
|
||||
-43
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.scripting.repl.writer
|
||||
|
||||
import org.jetbrains.kotlin.cli.common.repl.replAddLineBreak
|
||||
import org.jetbrains.kotlin.cli.common.repl.replOutputAsXml
|
||||
import org.jetbrains.kotlin.utils.repl.ReplEscapeType
|
||||
import org.jetbrains.kotlin.utils.repl.ReplEscapeType.*
|
||||
import java.io.PrintStream
|
||||
|
||||
class IdeSystemOutWrapperReplWriter(standardOut: PrintStream) : PrintStream(standardOut, true),
|
||||
ReplWriter {
|
||||
override fun print(x: Boolean) = printWithEscaping(x.toString())
|
||||
override fun print(x: Char) = printWithEscaping(x.toString())
|
||||
override fun print(x: Int) = printWithEscaping(x.toString())
|
||||
override fun print(x: Long) = printWithEscaping(x.toString())
|
||||
override fun print(x: Float) = printWithEscaping(x.toString())
|
||||
override fun print(x: Double) = printWithEscaping(x.toString())
|
||||
override fun print(x: String) = printWithEscaping(x)
|
||||
override fun print(x: Any?) = printWithEscaping(x.toString())
|
||||
|
||||
private fun printlnWithEscaping(text: String, escapeType: ReplEscapeType = USER_OUTPUT) {
|
||||
printWithEscaping("$text\n", escapeType)
|
||||
}
|
||||
|
||||
private fun printWithEscaping(text: String, escapeType: ReplEscapeType = USER_OUTPUT) {
|
||||
super.print(text.replOutputAsXml(escapeType).replAddLineBreak())
|
||||
}
|
||||
|
||||
override fun printlnWelcomeMessage(x: String) = printlnWithEscaping(x, INITIAL_PROMPT)
|
||||
override fun printlnHelpMessage(x: String) = printlnWithEscaping(x, HELP_PROMPT)
|
||||
override fun outputCommandResult(x: String) = printlnWithEscaping(x, REPL_RESULT)
|
||||
override fun notifyReadLineStart() = printlnWithEscaping("", READLINE_START)
|
||||
override fun notifyReadLineEnd() = printlnWithEscaping("", READLINE_END)
|
||||
override fun notifyCommandSuccess() = printlnWithEscaping("", SUCCESS)
|
||||
override fun notifyIncomplete() = printlnWithEscaping("", REPL_INCOMPLETE)
|
||||
override fun outputCompileError(x: String) = printlnWithEscaping(x, COMPILE_ERROR)
|
||||
override fun outputRuntimeError(x: String) = printlnWithEscaping(x, RUNTIME_ERROR)
|
||||
override fun sendInternalErrorReport(x: String) = printlnWithEscaping(x, INTERNAL_ERROR)
|
||||
}
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.scripting.repl.writer
|
||||
|
||||
interface ReplWriter {
|
||||
fun printlnWelcomeMessage(x: String)
|
||||
fun printlnHelpMessage(x: String)
|
||||
fun outputCommandResult(x: String)
|
||||
fun notifyReadLineStart()
|
||||
fun notifyReadLineEnd()
|
||||
fun notifyIncomplete()
|
||||
fun notifyCommandSuccess()
|
||||
fun outputCompileError(x: String)
|
||||
fun outputRuntimeError(x: String)
|
||||
fun sendInternalErrorReport(x: String)
|
||||
}
|
||||
Reference in New Issue
Block a user