Extend GeneratorExtensions with previous script, implemt it for JS REPL

also refactor JS REPL for better compatibility with the generic
REPL/scripting infrastructure
This commit is contained in:
Ilya Chernikov
2021-01-07 18:15:05 +01:00
parent 83da5f61fd
commit bac6a7346e
10 changed files with 89 additions and 47 deletions
@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.descriptors.DescriptorVisibility
import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.ir.expressions.IrDelegatingConstructorCall import org.jetbrains.kotlin.ir.expressions.IrDelegatingConstructorCall
import org.jetbrains.kotlin.ir.symbols.IrScriptSymbol
import org.jetbrains.kotlin.ir.util.StubGeneratorExtensions import org.jetbrains.kotlin.ir.util.StubGeneratorExtensions
import org.jetbrains.kotlin.psi.KtPureClassOrObject import org.jetbrains.kotlin.psi.KtPureClassOrObject
import org.jetbrains.kotlin.resolve.scopes.MemberScope import org.jetbrains.kotlin.resolve.scopes.MemberScope
@@ -39,4 +40,6 @@ open class GeneratorExtensions : StubGeneratorExtensions() {
open val shouldPreventDeprecatedIntegerValueTypeLiteralConversion: Boolean open val shouldPreventDeprecatedIntegerValueTypeLiteralConversion: Boolean
get() = false get() = false
open fun getPreviousScripts(): List<IrScriptSymbol> = emptyList()
} }
@@ -39,7 +39,7 @@ class ScriptGenerator(declarationGenerator: DeclarationGenerator) : DeclarationG
fun generateScriptDeclaration(ktScript: KtScript): IrDeclaration? { fun generateScriptDeclaration(ktScript: KtScript): IrDeclaration? {
val descriptor = getOrFail(BindingContext.DECLARATION_TO_DESCRIPTOR, ktScript) as ScriptDescriptor val descriptor = getOrFail(BindingContext.DECLARATION_TO_DESCRIPTOR, ktScript) as ScriptDescriptor
val existedScripts = context.symbolTable.listExistedScripts() val previousScripts = context.extensions.getPreviousScripts()
return context.symbolTable.declareScript(descriptor).buildWithScope { irScript -> return context.symbolTable.declareScript(descriptor).buildWithScope { irScript ->
@@ -54,7 +54,7 @@ class ScriptGenerator(declarationGenerator: DeclarationGenerator) : DeclarationG
// TODO: since script could reference instances of previous one their receivers have to be enlisted in its scope // TODO: since script could reference instances of previous one their receivers have to be enlisted in its scope
// Remove this code once script is no longer represented by Class // Remove this code once script is no longer represented by Class
existedScripts.forEach { previousScripts.forEach {
if (it.owner != irScript && it.descriptor !in importedScripts) { if (it.owner != irScript && it.descriptor !in importedScripts) {
context.symbolTable.introduceValueParameter(it.owner.thisReceiver) context.symbolTable.introduceValueParameter(it.owner.thisReceiver)
} }
@@ -117,7 +117,7 @@ class ScriptGenerator(declarationGenerator: DeclarationGenerator) : DeclarationG
valueParameter to irProperty.symbol valueParameter to irProperty.symbol
} }
irScript.earlierScripts = existedScripts irScript.earlierScripts = previousScripts
for (d in ktScript.declarations) { for (d in ktScript.declarations) {
when (d) { when (d) {
@@ -29,14 +29,14 @@ import kotlin.script.experimental.host.ScriptingHostConfiguration
import kotlin.script.experimental.jvm.JsDependency import kotlin.script.experimental.jvm.JsDependency
abstract class AbstractJsReplTest : Closeable { abstract class AbstractJsReplTest : Closeable {
protected lateinit var compilationState: JsReplCompilationState protected lateinit var compilerState: JsReplCompilerState
protected lateinit var evaluationState: JsEvaluationState protected lateinit var evaluationState: JsEvaluationState
protected abstract fun createCompilationState(): JsReplCompilationState protected abstract fun createCompilationState(): JsReplCompilerState
protected abstract fun createEvaluationState(): JsEvaluationState protected abstract fun createEvaluationState(): JsEvaluationState
fun compile(codeLine: ReplCodeLine): ReplCompileResult { fun compile(codeLine: ReplCodeLine): ReplCompileResult {
return JsReplCompiler(environment).compile(compilationState, codeLine) return JsReplCompiler(environment).compile(compilerState, codeLine)
} }
fun evaluate(compileResult: ReplCompileResult.CompiledClasses): ReplEvalResult { fun evaluate(compileResult: ReplCompileResult.CompiledClasses): ReplEvalResult {
@@ -45,7 +45,7 @@ abstract class AbstractJsReplTest : Closeable {
fun reset() { fun reset() {
collector.clear() collector.clear()
compilationState = createCompilationState() compilerState = createCompilationState()
evaluationState = createEvaluationState() evaluationState = createEvaluationState()
} }
@@ -31,9 +31,9 @@ class JsReplTestAgainstBinaries : AbstractJsReplTest() {
dependencyLoader.saveNames(nameTable) dependencyLoader.saveNames(nameTable)
} }
override fun createCompilationState(): JsReplCompilationState { override fun createCompilationState(): JsReplCompilerState {
val replState = ReplCodeAnalyzerBase.ResettableAnalyzerState() val replState = ReplCodeAnalyzerBase.ResettableAnalyzerState()
return JsReplCompilationState(ReentrantReadWriteLock(), dependencyLoader.loadNames(), dependencies, replState, createSymbolTable()) return JsReplCompilerState(ReentrantReadWriteLock(), dependencyLoader.loadNames(), dependencies, replState, createSymbolTable())
} }
private fun createSymbolTable(): SymbolTable = private fun createSymbolTable(): SymbolTable =
@@ -20,14 +20,14 @@ class JsReplTestAgainstKlib : AbstractJsReplTest() {
private var dependencyCode: String? = null private var dependencyCode: String? = null
override fun createCompilationState(): JsReplCompilationState { override fun createCompilationState(): JsReplCompilerState {
val nameTables = NameTables(emptyList(), mappedNames = mutableMapOf()) val nameTables = NameTables(emptyList(), mappedNames = mutableMapOf())
val symbolTable = SymbolTable(IdSignatureDescriptor(JsManglerDesc), IrFactoryImpl) val symbolTable = SymbolTable(IdSignatureDescriptor(JsManglerDesc), IrFactoryImpl)
val dependencyCompiler = JsScriptDependencyCompiler(environment.configuration, nameTables, symbolTable) val dependencyCompiler = JsScriptDependencyCompiler(environment.configuration, nameTables, symbolTable)
val dependencies = readLibrariesFromConfiguration(environment.configuration) val dependencies = readLibrariesFromConfiguration(environment.configuration)
dependencyCode = dependencyCompiler.compile(dependencies) dependencyCode = dependencyCompiler.compile(dependencies)
return JsReplCompilationState( return JsReplCompilerState(
ReentrantReadWriteLock(), ReentrantReadWriteLock(),
nameTables, nameTables,
dependencies, dependencies,
@@ -5,10 +5,8 @@
package org.jetbrains.kotlin.scripting.repl.js package org.jetbrains.kotlin.scripting.repl.js
import org.jetbrains.kotlin.cli.common.repl.* import org.jetbrains.kotlin.cli.common.repl.IReplStageHistory
import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.cli.common.repl.IReplStageState
import org.jetbrains.kotlin.descriptors.ScriptDescriptor
import org.jetbrains.kotlin.ir.backend.js.utils.NameTables
import org.jetbrains.kotlin.js.engine.ScriptEngineWithTypedResult import org.jetbrains.kotlin.js.engine.ScriptEngineWithTypedResult
import java.util.concurrent.locks.ReentrantReadWriteLock import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.reflect.KClass import kotlin.reflect.KClass
@@ -17,24 +15,17 @@ import kotlin.script.experimental.api.ResultWithDiagnostics
import kotlin.script.experimental.api.ScriptCompilationConfiguration import kotlin.script.experimental.api.ScriptCompilationConfiguration
import kotlin.script.experimental.api.ScriptEvaluationConfiguration import kotlin.script.experimental.api.ScriptEvaluationConfiguration
abstract class JsState(override val lock: ReentrantReadWriteLock) : IReplStageState<ScriptDescriptor> { // NOTE: the state management machinery is not implemented here, since it is unused at the moment in the JS REPL (see JvmReplEvaluatorState for complete implementation, if needed)
override val history: IReplStageHistory<ScriptDescriptor> class JsEvaluationState(override val lock: ReentrantReadWriteLock, val engine: ScriptEngineWithTypedResult) : IReplStageState<Nothing> {
override fun dispose() {
engine.reset()
}
override val history: IReplStageHistory<Nothing>
get() = TODO("not implemented") get() = TODO("not implemented")
override val currentGeneration: Int override val currentGeneration: Int
get() = TODO("not implemented") get() = TODO("not implemented")
}
abstract class JsCompilationState(
lock: ReentrantReadWriteLock,
val nameTables: NameTables,
val dependencies: List<ModuleDescriptor>) : JsState(lock)
class JsEvaluationState(lock: ReentrantReadWriteLock, val engine: ScriptEngineWithTypedResult) : JsState(lock) {
override fun dispose() {
engine.reset()
}
} }
class JsCompiledScript( class JsCompiledScript(
@@ -18,13 +18,17 @@ import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.generateJsCode import org.jetbrains.kotlin.ir.backend.js.generateJsCode
import org.jetbrains.kotlin.ir.backend.js.utils.NameTables import org.jetbrains.kotlin.ir.backend.js.utils.NameTables
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.declarations.IrScript
import org.jetbrains.kotlin.ir.descriptors.IrFunctionFactory import org.jetbrains.kotlin.ir.descriptors.IrFunctionFactory
import org.jetbrains.kotlin.ir.symbols.IrScriptSymbol
import org.jetbrains.kotlin.ir.util.ExternalDependenciesGenerator import org.jetbrains.kotlin.ir.util.ExternalDependenciesGenerator
import org.jetbrains.kotlin.ir.util.SymbolTable import org.jetbrains.kotlin.ir.util.SymbolTable
import org.jetbrains.kotlin.js.config.JSConfigurationKeys import org.jetbrains.kotlin.js.config.JSConfigurationKeys
import org.jetbrains.kotlin.psi2ir.Psi2IrConfiguration import org.jetbrains.kotlin.psi2ir.Psi2IrConfiguration
import org.jetbrains.kotlin.psi2ir.Psi2IrTranslator import org.jetbrains.kotlin.psi2ir.Psi2IrTranslator
import org.jetbrains.kotlin.psi2ir.generators.generateTypicalIrProviderList import org.jetbrains.kotlin.psi2ir.generators.generateTypicalIrProviderList
import org.jetbrains.kotlin.psi2ir.generators.GeneratorExtensions
import org.jetbrains.kotlin.scripting.compiler.plugin.repl.ReplCodeAnalyzerBase import org.jetbrains.kotlin.scripting.compiler.plugin.repl.ReplCodeAnalyzerBase
import org.jetbrains.kotlin.serialization.js.ModuleKind import org.jetbrains.kotlin.serialization.js.ModuleKind
import kotlin.script.experimental.api.valueOr import kotlin.script.experimental.api.valueOr
@@ -35,7 +39,7 @@ class JsCoreScriptingCompiler(
private val nameTables: NameTables, private val nameTables: NameTables,
private val symbolTable: SymbolTable, private val symbolTable: SymbolTable,
private val dependencyDescriptors: List<ModuleDescriptor>, private val dependencyDescriptors: List<ModuleDescriptor>,
private val replState: ReplCodeAnalyzerBase.ResettableAnalyzerState = ReplCodeAnalyzerBase.ResettableAnalyzerState() private val replCompilerState: JsReplCompilerState? = null
) { ) {
fun compile(codeLine: ReplCodeLine): ReplCompileResult { fun compile(codeLine: ReplCodeLine): ReplCompileResult {
val snippet = codeLine.code val snippet = codeLine.code
@@ -51,7 +55,10 @@ class JsCoreScriptingCompiler(
).valueOr { return ReplCompileResult.Error(it.reports.joinToString { r -> r.message }) } ).valueOr { return ReplCompileResult.Error(it.reports.joinToString { r -> r.message }) }
val messageCollector = environment.configuration[CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY] as MessageCollector val messageCollector = environment.configuration[CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY] as MessageCollector
val analyzerEngine = JsReplCodeAnalyzer(environment, dependencyDescriptors, replState)
val analyzerState = replCompilerState?.analyzerState ?: ReplCodeAnalyzerBase.ResettableAnalyzerState()
val analyzerEngine = JsReplCodeAnalyzer(environment, dependencyDescriptors, analyzerState)
val analysisResult = analyzerEngine.analyzeReplLine(snippetKtFile, codeLine).also { val analysisResult = analyzerEngine.analyzeReplLine(snippetKtFile, codeLine).also {
AnalyzerWithCompilerReport.reportDiagnostics(it.bindingContext.diagnostics, messageCollector) AnalyzerWithCompilerReport.reportDiagnostics(it.bindingContext.diagnostics, messageCollector)
if (messageCollector.hasErrors()) return ReplCompileResult.Error("Error while analysis") if (messageCollector.hasErrors()) return ReplCompileResult.Error("Error while analysis")
@@ -60,7 +67,14 @@ class JsCoreScriptingCompiler(
val files = listOf(snippetKtFile) val files = listOf(snippetKtFile)
val (bindingContext, module) = analysisResult val (bindingContext, module) = analysisResult
val psi2ir = Psi2IrTranslator(environment.configuration.languageVersionSettings, Psi2IrConfiguration()) val psi2ir = Psi2IrTranslator(environment.configuration.languageVersionSettings, Psi2IrConfiguration())
val psi2irContext = psi2ir.createGeneratorContext(module, bindingContext, symbolTable)
val generatorExtensions =
if (replCompilerState == null) GeneratorExtensions()
else object : GeneratorExtensions() {
override fun getPreviousScripts() = replCompilerState.history.map { it.item.scriptSymbol }
}
val psi2irContext = psi2ir.createGeneratorContext(module, bindingContext, symbolTable, generatorExtensions)
val providers = generateTypicalIrProviderList(module, psi2irContext.irBuiltIns, psi2irContext.symbolTable) val providers = generateTypicalIrProviderList(module, psi2irContext.irBuiltIns, psi2irContext.symbolTable)
val irModuleFragment = psi2ir.generateModuleFragment(psi2irContext, files, providers, emptyList(), null) // TODO: deserializer val irModuleFragment = psi2ir.generateModuleFragment(psi2irContext, files, providers, emptyList(), null) // TODO: deserializer
@@ -91,6 +105,16 @@ class JsCoreScriptingCompiler(
val code = generateJsCode(context, irModuleFragment, nameTables) val code = generateJsCode(context, irModuleFragment, nameTables)
updateHistory(codeLine, irModuleFragment)
return createCompileResult(LineId(codeLine.no, 0, codeLine.hashCode()), code) return createCompileResult(LineId(codeLine.no, 0, codeLine.hashCode()), code)
} }
private fun updateHistory(codeLine: ReplCodeLine, irModuleFragment: IrModuleFragment) {
if (replCompilerState != null) {
val lineId = LineId(codeLine.no, 0, codeLine.code.hashCode())
val scriptSymbol = irModuleFragment.files.single().declarations.single { it is IrScript }.symbol as IrScriptSymbol
replCompilerState.history.push(lineId, JsReplCompilationHistoryItem(scriptSymbol))
}
}
} }
@@ -19,7 +19,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock
class JsReplCompiler(private val environment: KotlinCoreEnvironment) : ReplCompiler { class JsReplCompiler(private val environment: KotlinCoreEnvironment) : ReplCompiler {
override fun createState(lock: ReentrantReadWriteLock): IReplStageState<*> { override fun createState(lock: ReentrantReadWriteLock): IReplStageState<*> {
return JsReplCompilationState( return JsReplCompilerState(
lock, lock,
NameTables(emptyList(), mappedNames = mutableMapOf()), NameTables(emptyList(), mappedNames = mutableMapOf()),
readLibrariesFromConfiguration(environment.configuration), readLibrariesFromConfiguration(environment.configuration),
@@ -33,13 +33,13 @@ class JsReplCompiler(private val environment: KotlinCoreEnvironment) : ReplCompi
} }
override fun compile(state: IReplStageState<*>, codeLine: ReplCodeLine): ReplCompileResult { override fun compile(state: IReplStageState<*>, codeLine: ReplCodeLine): ReplCompileResult {
val compilationState = state.asState(JsReplCompilationState::class.java) val compilationState = state.asState(JsReplCompilerState::class.java)
return JsCoreScriptingCompiler( return JsCoreScriptingCompiler(
environment, environment,
compilationState.nameTables, compilationState.nameTables,
compilationState.symbolTable, compilationState.symbolTable,
compilationState.dependencies, compilationState.dependencies,
compilationState.replState compilationState
).compile(codeLine) ).compile(codeLine)
} }
} }
@@ -0,0 +1,35 @@
/*
* Copyright 2010-2021 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.js
import org.jetbrains.kotlin.cli.common.repl.BasicReplStageHistory
import org.jetbrains.kotlin.cli.common.repl.IReplStageState
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.ir.backend.js.utils.NameTables
import org.jetbrains.kotlin.ir.symbols.IrScriptSymbol
import org.jetbrains.kotlin.ir.util.SymbolTable
import org.jetbrains.kotlin.scripting.compiler.plugin.repl.ReplCodeAnalyzerBase
import java.util.concurrent.locks.ReentrantReadWriteLock
class JsReplCompilationHistoryItem(
val scriptSymbol: IrScriptSymbol
)
class JsReplCompilerStageHistory(lock: ReentrantReadWriteLock) : BasicReplStageHistory<JsReplCompilationHistoryItem>(lock)
// NOTE: the state management machinery is reduced in this implementation, since it is unused at the moment in the JS REPL (see JvmReplCompilerState for complete implementation, if needed)
class JsReplCompilerState(
override val lock: ReentrantReadWriteLock,
val nameTables: NameTables,
val dependencies: List<ModuleDescriptor>,
val analyzerState: ReplCodeAnalyzerBase.ResettableAnalyzerState,
val symbolTable: SymbolTable
) : IReplStageState<JsReplCompilationHistoryItem> {
override val history = JsReplCompilerStageHistory(lock)
override val currentGeneration: Int get() = (history as BasicReplStageHistory<*>).currentGeneration.get()
}
@@ -9,8 +9,8 @@ import com.intellij.openapi.project.Project
import com.intellij.psi.PsiFileFactory import com.intellij.psi.PsiFileFactory
import com.intellij.psi.impl.PsiFileFactoryImpl import com.intellij.psi.impl.PsiFileFactoryImpl
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSourceLocation
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSourceLocation
import org.jetbrains.kotlin.cli.common.messages.GroupingMessageCollector import org.jetbrains.kotlin.cli.common.messages.GroupingMessageCollector
import org.jetbrains.kotlin.cli.common.messages.MessageCollector import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.cli.common.repl.LineId import org.jetbrains.kotlin.cli.common.repl.LineId
@@ -23,11 +23,9 @@ import org.jetbrains.kotlin.idea.KotlinLanguage
import org.jetbrains.kotlin.ir.backend.js.getModuleDescriptorByLibrary import org.jetbrains.kotlin.ir.backend.js.getModuleDescriptorByLibrary
import org.jetbrains.kotlin.ir.backend.js.jsResolveLibraries import org.jetbrains.kotlin.ir.backend.js.jsResolveLibraries
import org.jetbrains.kotlin.ir.backend.js.utils.NameTables import org.jetbrains.kotlin.ir.backend.js.utils.NameTables
import org.jetbrains.kotlin.ir.util.SymbolTable
import org.jetbrains.kotlin.library.resolver.TopologicalLibraryOrder import org.jetbrains.kotlin.library.resolver.TopologicalLibraryOrder
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtScript import org.jetbrains.kotlin.psi.KtScript
import org.jetbrains.kotlin.scripting.compiler.plugin.repl.ReplCodeAnalyzerBase
import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys
import org.jetbrains.kotlin.scripting.resolve.ScriptLightVirtualFile import org.jetbrains.kotlin.scripting.resolve.ScriptLightVirtualFile
import org.jetbrains.kotlin.util.Logger import org.jetbrains.kotlin.util.Logger
@@ -37,7 +35,6 @@ import java.io.FileOutputStream
import java.io.FileReader import java.io.FileReader
import java.io.InputStreamReader import java.io.InputStreamReader
import java.nio.charset.Charset import java.nio.charset.Charset
import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.script.experimental.api.* import kotlin.script.experimental.api.*
import kotlin.script.experimental.host.FileBasedScriptSource import kotlin.script.experimental.host.FileBasedScriptSource
import kotlin.script.experimental.jvm.JsDependency import kotlin.script.experimental.jvm.JsDependency
@@ -216,11 +213,3 @@ class DependencyLoader {
} }
} }
class JsReplCompilationState(
lock: ReentrantReadWriteLock,
nameTables: NameTables,
dependencies: List<ModuleDescriptor>,
val replState: ReplCodeAnalyzerBase.ResettableAnalyzerState,
val symbolTable: SymbolTable
) : JsCompilationState(lock, nameTables, dependencies)