[JS BE] Drop JS scripting support

JS scripting uses the old IR to JS transformer.
 The new IR to JS transformer can not be used for
 JS scripting out of the box. Patching the new transformer for
 JS scripting is potentially dangerous and requires a lot of effort.
 Dropping JS scripting and the old IR to JS transformer allows to
 refactor and simplify JS BE codebase.
This commit is contained in:
Alexander Korepanov
2022-11-03 14:26:11 +01:00
committed by Space Team
parent d75bbc49e7
commit d5e9e87538
41 changed files with 14 additions and 1860 deletions
@@ -1,32 +0,0 @@
description = "Kotlin Scripting Compiler JS Plugin"
plugins {
kotlin("jvm")
id("jps-compatible")
}
dependencies {
compileOnly(project(":compiler:frontend"))
compileOnly(project(":compiler:psi"))
compileOnly(project(":compiler:plugin-api"))
compileOnly(project(":compiler:cli"))
compileOnly(project(":compiler:backend.js"))
compileOnly(project(":core:descriptors.runtime"))
compileOnly(project(":compiler:ir.tree"))
api(project(":kotlin-scripting-common"))
api(project(":kotlin-scripting-js"))
api(project(":kotlin-util-klib"))
api(project(":kotlin-scripting-compiler"))
api(kotlinStdlib())
compileOnly(intellijCore())
}
sourceSets {
"main" { projectDefault() }
}
tasks.withType<org.jetbrains.kotlin.gradle.dsl.KotlinCompile<*>> {
kotlinOptions {
freeCompilerArgs = freeCompilerArgs - "-progressive" + "-Xskip-metadata-version-check"
}
}
@@ -1,87 +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.js
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.cli.jvm.compiler.NoScopeRecordCliBindingTrace
import org.jetbrains.kotlin.config.languageVersionSettings
import org.jetbrains.kotlin.context.ContextForNewModule
import org.jetbrains.kotlin.context.ProjectContext
import org.jetbrains.kotlin.descriptors.ClassDescriptorWithResolutionScopes
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.PackageFragmentProvider
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
import org.jetbrains.kotlin.diagnostics.Severity
import org.jetbrains.kotlin.frontend.js.di.createTopDownAnalyzerForJs
import org.jetbrains.kotlin.incremental.components.EnumWhenTracker
import org.jetbrains.kotlin.incremental.components.ExpectActualTracker
import org.jetbrains.kotlin.incremental.components.InlineConstTracker
import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.BindingTraceContext
import org.jetbrains.kotlin.resolve.CompilerEnvironment
import org.jetbrains.kotlin.resolve.TopDownAnalysisMode
import org.jetbrains.kotlin.resolve.lazy.declarations.FileBasedDeclarationProviderFactory
abstract class AbstractJsScriptlikeCodeAnalyser(
private val environment: KotlinCoreEnvironment,
private val dependencies: List<ModuleDescriptor>
) {
protected class JsScriptAnalysisResult(
val moduleDescriptor: ModuleDescriptor,
private val scriptDescriptor: ClassDescriptorWithResolutionScopes?,
val bindingContext: BindingContext
) {
val isSuccess: Boolean get() = scriptDescriptor != null
val script: ClassDescriptorWithResolutionScopes get() = scriptDescriptor ?: error("Error occurred")
}
protected fun analysisImpl(psi: KtFile): JsScriptAnalysisResult {
val trace: BindingTraceContext = NoScopeRecordCliBindingTrace()
val project = environment.project
val builtIns: KotlinBuiltIns = dependencies.single { it.allDependencyModules.isEmpty() }.builtIns
val moduleContext = ContextForNewModule(
ProjectContext(project, "TopDownAnalyzer for JS Script"),
Name.special("<script>"),
builtIns,
platform = null
)
val languageVersionSettings = environment.configuration.languageVersionSettings
val lookupTracker = LookupTracker.DO_NOTHING
val expectActualTracker = ExpectActualTracker.DoNothing
val inlineConstTracker = InlineConstTracker.DoNothing
val enumWhenTracker = EnumWhenTracker.DoNothing
val additionalPackages = emptyList<PackageFragmentProvider>()
val moduleDescriptor = moduleContext.module
moduleDescriptor.setDependencies(dependencies.map { it as ModuleDescriptorImpl } + moduleDescriptor)
val analyzer = createTopDownAnalyzerForJs(
moduleContext, trace,
FileBasedDeclarationProviderFactory(moduleContext.storageManager, listOf(psi)),
languageVersionSettings,
lookupTracker,
expectActualTracker,
inlineConstTracker,
enumWhenTracker,
additionalPackages,
CompilerEnvironment,
)
val analyzerContext = analyzer.analyzeDeclarations(TopDownAnalysisMode.TopLevelDeclarations, listOf(psi))
val diagnostics = trace.bindingContext.diagnostics
val hasErrors = diagnostics.any { it.severity == Severity.ERROR }
val scriptDescriptor = analyzerContext.scripts[psi.script]
assert(scriptDescriptor != null || hasErrors) { "If no errors occurred script descriptor has to be existed" }
return JsScriptAnalysisResult(moduleDescriptor, scriptDescriptor, trace.bindingContext)
}
}
@@ -1,127 +0,0 @@
/*
* 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.js
import org.jetbrains.kotlin.backend.common.serialization.DescriptorByIdSignatureFinderImpl
import org.jetbrains.kotlin.backend.common.serialization.linkerissues.checkNoUnboundSymbols
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.common.environment.setIdeaIoUseFallback
import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.cli.common.repl.LineId
import org.jetbrains.kotlin.cli.common.repl.ReplCodeLine
import org.jetbrains.kotlin.cli.common.repl.ReplCompileResult
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.config.languageVersionSettings
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.generateJsCode
import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsManglerDesc
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.symbols.IrScriptSymbol
import org.jetbrains.kotlin.ir.util.ExternalDependenciesGenerator
import org.jetbrains.kotlin.ir.util.SymbolTable
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
import org.jetbrains.kotlin.psi2ir.Psi2IrConfiguration
import org.jetbrains.kotlin.psi2ir.Psi2IrTranslator
import org.jetbrains.kotlin.psi2ir.generators.GeneratorExtensions
import org.jetbrains.kotlin.psi2ir.generators.generateTypicalIrProviderList
import org.jetbrains.kotlin.scripting.compiler.plugin.repl.ReplCodeAnalyzerBase
import org.jetbrains.kotlin.serialization.js.ModuleKind
import kotlin.script.experimental.api.valueOr
import kotlin.script.experimental.host.StringScriptSource
class JsCoreScriptingCompiler(
private val environment: KotlinCoreEnvironment,
private val nameTables: NameTables,
private val symbolTable: SymbolTable,
private val dependencyDescriptors: List<ModuleDescriptor>,
private val replCompilerState: JsReplCompilerState? = null
) {
fun compile(codeLine: ReplCodeLine): ReplCompileResult {
val snippet = codeLine.code
val snippetId = codeLine.no
setIdeaIoUseFallback()
val sourceCode = StringScriptSource(snippet, "line-$snippetId.kts")
val snippetKtFile = getScriptKtFile(
sourceCode,
snippet,
environment.project
).valueOr { return ReplCompileResult.Error(it.reports.joinToString { r -> r.message }) }
val messageCollector = environment.configuration[CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY] as MessageCollector
val analyzerState = replCompilerState?.analyzerState ?: ReplCodeAnalyzerBase.ResettableAnalyzerState()
val analyzerEngine = JsReplCodeAnalyzer(environment, dependencyDescriptors, analyzerState)
val analysisResult = analyzerEngine.analyzeReplLine(snippetKtFile, codeLine).also {
AnalyzerWithCompilerReport.reportDiagnostics(it.bindingContext.diagnostics, messageCollector, renderInternalDiagnosticName = false)
if (messageCollector.hasErrors()) return ReplCompileResult.Error("Error while analysis")
}
val files = listOf(snippetKtFile)
val (bindingContext, module) = analysisResult
val psi2ir = Psi2IrTranslator(
environment.configuration.languageVersionSettings,
Psi2IrConfiguration(),
environment.configuration::checkNoUnboundSymbols
)
val generatorExtensions =
if (replCompilerState == null) GeneratorExtensions()
else object : GeneratorExtensions() {
override fun getPreviousScripts() = replCompilerState.history.map { it.item.scriptSymbol }
override val lowerScriptToClass: Boolean = false
}
val psi2irContext = psi2ir.createGeneratorContext(module, bindingContext, symbolTable, generatorExtensions)
val providers = generateTypicalIrProviderList(
module, psi2irContext.irBuiltIns, psi2irContext.symbolTable, DescriptorByIdSignatureFinderImpl(module, JsManglerDesc)
)
val irModuleFragment = psi2ir.generateModuleFragment(psi2irContext, files, providers, emptyList(), null) // TODO: deserializer
val context = JsIrBackendContext(
irModuleFragment.descriptor,
psi2irContext.irBuiltIns,
psi2irContext.symbolTable,
irModuleFragment,
additionalExportedDeclarationNames = emptySet(),
keep = emptySet(),
environment.configuration,
true
)
ExternalDependenciesGenerator(
psi2irContext.symbolTable,
generateTypicalIrProviderList(
irModuleFragment.descriptor,
psi2irContext.irBuiltIns,
psi2irContext.symbolTable,
DescriptorByIdSignatureFinderImpl(irModuleFragment.descriptor, JsManglerDesc)
)
).generateUnboundSymbolsAsDependencies()
environment.configuration.put(JSConfigurationKeys.MODULE_KIND, ModuleKind.PLAIN)
val code = generateJsCode(context, irModuleFragment, nameTables)
updateHistory(codeLine, irModuleFragment)
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))
}
}
}
@@ -1,37 +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.js
import org.jetbrains.kotlin.analyzer.AnalysisResult
import org.jetbrains.kotlin.cli.common.repl.ReplCodeLine
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.scripting.compiler.plugin.repl.ReplCodeAnalyzerBase
import org.jetbrains.kotlin.scripting.compiler.plugin.repl.toSourceCode
import org.jetbrains.kotlin.scripting.definitions.ScriptPriorities
class JsReplCodeAnalyzer(
environment: KotlinCoreEnvironment,
dependencies: List<ModuleDescriptor>,
private val replState: ReplCodeAnalyzerBase.ResettableAnalyzerState
) : AbstractJsScriptlikeCodeAnalyser(environment, dependencies) {
fun analyzeReplLine(linePsi: KtFile, codeLine: ReplCodeLine): AnalysisResult {
linePsi.script!!.putUserData(ScriptPriorities.PRIORITY_KEY, codeLine.no)
replState.submitLine(linePsi)
val result = analysisImpl(linePsi)
return if (result.isSuccess) {
replState.lineSuccess(linePsi, codeLine.toSourceCode(), result.script)
AnalysisResult.success(result.bindingContext, result.moduleDescriptor)
} else {
replState.lineFailure(linePsi)
AnalysisResult.compilationError(result.bindingContext)
}
}
}
@@ -1,45 +0,0 @@
/*
* 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.js
import org.jetbrains.kotlin.backend.common.serialization.signature.IdSignatureDescriptor
import org.jetbrains.kotlin.cli.common.repl.*
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsManglerDesc
import org.jetbrains.kotlin.ir.backend.js.utils.NameTables
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
import org.jetbrains.kotlin.ir.util.SymbolTable
import org.jetbrains.kotlin.scripting.compiler.plugin.repl.ReplCodeAnalyzerBase
import java.util.concurrent.locks.ReentrantReadWriteLock
// Used to compile REPL code lines
class JsReplCompiler(private val environment: KotlinCoreEnvironment) : ReplCompiler {
override fun createState(lock: ReentrantReadWriteLock): IReplStageState<*> {
return JsReplCompilerState(
lock,
NameTables(emptyList(), mappedNames = mutableMapOf()),
readLibrariesFromConfiguration(environment.configuration),
ReplCodeAnalyzerBase.ResettableAnalyzerState(),
SymbolTable(IdSignatureDescriptor(JsManglerDesc), IrFactoryImpl)
)
}
override fun check(state: IReplStageState<*>, codeLine: ReplCodeLine): ReplCheckResult {
return ReplCheckResult.Ok()
}
override fun compile(state: IReplStageState<*>, codeLine: ReplCodeLine): ReplCompileResult {
val compilationState = state.asState(JsReplCompilerState::class.java)
return JsCoreScriptingCompiler(
environment,
compilationState.nameTables,
compilationState.symbolTable,
compilationState.dependencies,
compilationState
).compile(codeLine)
}
}
@@ -1,35 +0,0 @@
/*
* 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.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()
}
@@ -1,215 +0,0 @@
/*
* 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.js
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiFileFactory
import com.intellij.psi.impl.PsiFileFactoryImpl
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
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.MessageCollector
import org.jetbrains.kotlin.cli.common.repl.LineId
import org.jetbrains.kotlin.cli.common.repl.ReplCodeLine
import org.jetbrains.kotlin.cli.common.repl.ReplCompileResult
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
import org.jetbrains.kotlin.idea.KotlinLanguage
import org.jetbrains.kotlin.ir.backend.js.getModuleDescriptorByLibrary
import org.jetbrains.kotlin.ir.backend.js.jsResolveLibraries
import org.jetbrains.kotlin.ir.backend.js.utils.NameTables
import org.jetbrains.kotlin.library.resolver.TopologicalLibraryOrder
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtScript
import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys
import org.jetbrains.kotlin.scripting.resolve.ScriptLightVirtualFile
import org.jetbrains.kotlin.util.Logger
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import java.io.BufferedReader
import java.io.FileOutputStream
import java.io.FileReader
import java.io.InputStreamReader
import java.nio.charset.Charset
import kotlin.script.experimental.api.*
import kotlin.script.experimental.host.FileBasedScriptSource
import kotlin.script.experimental.jvm.JsDependency
fun getScriptKtFile(
script: SourceCode,
scriptText: String,
project: Project
): ResultWithDiagnostics<KtFile> {
val psiFileFactory: PsiFileFactoryImpl = PsiFileFactory.getInstance(project) as PsiFileFactoryImpl
val virtualFile = ScriptLightVirtualFile(
script.name!!,
(script as? FileBasedScriptSource)?.file?.path,
scriptText
)
val ktFile = psiFileFactory.trySetupPsiForFile(virtualFile, KotlinLanguage.INSTANCE, true, false) as KtFile?
return when {
ktFile == null -> ResultWithDiagnostics.Failure(
ScriptDiagnostic(
ScriptDiagnostic.unspecifiedError,
message = "Cannot create PSI",
severity = ScriptDiagnostic.Severity.ERROR
)
)
ktFile.declarations.firstIsInstanceOrNull<KtScript>() == null -> ResultWithDiagnostics.Failure(
ScriptDiagnostic(
ScriptDiagnostic.unspecifiedError,
message = "There is not Script",
severity = ScriptDiagnostic.Severity.ERROR
)
)
else -> ktFile.asSuccess()
}
}
fun makeReplCodeLine(no: Int, code: String): ReplCodeLine = ReplCodeLine(no, 0, code)
//TODO: remove and use collector from kotlin-scripting-compiler
class ReplMessageCollector : MessageCollector {
private var hasErrors = false
private var messages = mutableListOf<Pair<CompilerMessageSeverity, String>>()
override fun clear() {
hasErrors = false
messages.clear()
}
override fun report(severity: CompilerMessageSeverity, message: String, location: CompilerMessageSourceLocation?) {
if (severity == CompilerMessageSeverity.ERROR) hasErrors = true
messages.add(Pair(severity, message))
}
override fun hasErrors(): Boolean {
return hasErrors
}
fun hasNotErrors(): Boolean {
return !hasErrors
}
fun getMessage(): String {
val resultMessage = StringBuilder("Found ${messages.size} problems:\n")
for (m in messages) {
resultMessage.append(m.first.toString() + " : " + m.second + "\n")
}
return resultMessage.toString()
}
}
fun readLibrariesFromConfiguration(configuration: CompilerConfiguration): List<ModuleDescriptor> {
// TODO: Reimplement this code once we get proper klib dependency resolver
val scriptConfig = configuration[ScriptingConfigurationKeys.SCRIPT_DEFINITIONS]!!
val scriptCompilationConfig = scriptConfig.find { (it).platform == "JS" }!!.compilationConfiguration
val scriptDependencies = scriptCompilationConfig[ScriptCompilationConfiguration.dependencies]!!
val descriptorMap = mutableMapOf<String, ModuleDescriptorImpl>()
val libraries = scriptDependencies.map { (it as JsDependency).path }
val resolvedLibraries = jsResolveLibraries(
libraries,
emptyList(),
object : Logger {
private val collector = configuration[CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY] ?: MessageCollector.NONE
override fun warning(message: String) = collector.report(CompilerMessageSeverity.STRONG_WARNING, message)
override fun error(message: String) = collector.report(CompilerMessageSeverity.ERROR, message)
override fun log(message: String) = collector.report(CompilerMessageSeverity.LOGGING, message)
override fun fatal(message: String): Nothing {
collector.report(CompilerMessageSeverity.ERROR, message)
(collector as? GroupingMessageCollector)?.flush()
kotlin.error(message)
}
}
)
return resolvedLibraries.getFullList(TopologicalLibraryOrder)
.map { descriptorMap.getOrPut(it.libraryName) { getModuleDescriptorByLibrary(it, descriptorMap) } }
}
fun createCompileResult(code: String) = createCompileResult(LineId(0, 0, 0), code)
fun createCompileResult(lineId: LineId, code: String): ReplCompileResult.CompiledClasses {
return ReplCompileResult.CompiledClasses(
lineId,
emptyList(),
"",
emptyList(),
false,
emptyList(),
"Any?",
code
)
}
class DependencyLoader {
// TODO: this should be taken from CompilerConfiguration
private val commonPath = "libraries/stdlib/js-ir/build/classes/kotlin/js/main/"
private val mappedNamesPath = "$commonPath/mappedNames.txt"
private val scriptDependencyBinaryPath = "$commonPath/scriptDependencyBinary.js"
fun saveNames(nameTables: NameTables, path: String = mappedNamesPath) {
writeDataByPath(writeNames(nameTables), path)
}
fun loadNames(path: String = mappedNamesPath): NameTables {
return readNames(readDataByPath(path))
}
fun saveScriptDependencyBinary(stdlibCompiledResult: String, path: String = scriptDependencyBinaryPath) {
writeDataByPath(writeScriptDependencyBinary(stdlibCompiledResult), path)
}
fun loadScriptDependencyBinary(path: String = scriptDependencyBinaryPath): String {
return readScriptDependencyBinary(readDataByPath(path))
}
fun writeNames(nameTables: NameTables): ByteArray {
val result = StringBuilder()
for (entry in nameTables.mappedNames.orEmpty()) {
result.append("${entry.key} ${entry.value}" + System.lineSeparator())
}
return result.toString().toByteArray(Charset.defaultCharset())
}
fun readNames(data: ByteArray): NameTables {
val mappedNames = mutableMapOf<String, String>()
val reserved = mutableSetOf<String>()
BufferedReader(InputStreamReader(data.inputStream())).use { reader ->
for (line in reader.readLines()) {
val (key, value) = line.split(" ")
mappedNames[key] = value
reserved += value
}
}
return NameTables(emptyList(), mappedNames = mappedNames, reservedForGlobal = reserved)
}
fun writeScriptDependencyBinary(stdlibCompiledResult: String): ByteArray {
return stdlibCompiledResult.toByteArray(Charset.defaultCharset())
}
fun readScriptDependencyBinary(data: ByteArray): String {
return data.toString(Charset.defaultCharset())
}
fun readDataByPath(path: String): ByteArray {
FileReader(path).use { reader ->
val stdlibCompiledResult = reader.readText()
return stdlibCompiledResult.toByteArray(Charset.defaultCharset())
}
}
fun writeDataByPath(data: ByteArray, path: String) {
FileOutputStream(path).use {
it.write(data)
}
}
}
@@ -1,82 +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.js
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.PackageFragmentProvider
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
import org.jetbrains.kotlin.descriptors.konan.kotlinLibrary
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.generateJsCode
import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsIrLinker
import org.jetbrains.kotlin.ir.backend.js.utils.NameTables
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi2ir.descriptors.IrBuiltInsOverDescriptors
import org.jetbrains.kotlin.psi2ir.generators.TypeTranslatorImpl
import org.jetbrains.kotlin.serialization.js.ModuleKind
import org.jetbrains.kotlin.storage.LockBasedStorageManager
// Transforms klib into js code in script-friendly way
@OptIn(ObsoleteDescriptorBasedAPI::class)
class JsScriptDependencyCompiler(
private val configuration: CompilerConfiguration,
private val nameTables: NameTables,
private val symbolTable: SymbolTable
) {
fun compile(dependencies: List<ModuleDescriptor>): String {
val builtIns: KotlinBuiltIns = dependencies.single { it.allDependencyModules.isEmpty() }.builtIns
val languageVersionSettings = LanguageVersionSettingsImpl.DEFAULT
val messageLogger = configuration.irMessageLogger
val moduleName = Name.special("<script-dependencies>")
val storageManager = LockBasedStorageManager.NO_LOCKS
val moduleDescriptor = ModuleDescriptorImpl(moduleName, storageManager, builtIns, null).also {
it.setDependencies(dependencies.map { d -> d as ModuleDescriptorImpl } + it)
it.initialize(PackageFragmentProvider.Empty)
}
val typeTranslator = TypeTranslatorImpl(symbolTable, languageVersionSettings, moduleDescriptor)
val irBuiltIns = IrBuiltInsOverDescriptors(builtIns, typeTranslator, symbolTable)
val jsLinker = JsIrLinker(null, messageLogger, irBuiltIns, symbolTable, partialLinkageEnabled = false, null)
val irDependencies = dependencies.map { jsLinker.deserializeFullModule(it, it.kotlinLibrary) }
val moduleFragment = irDependencies.last()
val irProviders = listOf(jsLinker)
jsLinker.init(null, emptyList())
ExternalDependenciesGenerator(symbolTable, irProviders)
.generateUnboundSymbolsAsDependencies()
moduleFragment.patchDeclarationParents()
val backendContext = JsIrBackendContext(
moduleDescriptor,
irBuiltIns,
symbolTable,
moduleFragment,
additionalExportedDeclarationNames = emptySet(),
keep = emptySet(),
configuration,
true
)
ExternalDependenciesGenerator(symbolTable, irProviders)
.generateUnboundSymbolsAsDependencies()
moduleFragment.patchDeclarationParents()
jsLinker.postProcess()
moduleFragment.files += irDependencies.filter { it !== moduleFragment }.flatMap { it.files }
configuration.put(JSConfigurationKeys.MODULE_KIND, ModuleKind.PLAIN)
return generateJsCode(backendContext, moduleFragment, nameTables)
}
}
@@ -1,69 +0,0 @@
/*
* 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.js
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
import org.jetbrains.kotlin.cli.common.arguments.K2JSCompilerArguments
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.scripting.compiler.plugin.AbstractScriptEvaluationExtension
import org.jetbrains.kotlin.scripting.compiler.plugin.ScriptCompilerProxy
import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.platform
import org.jetbrains.kotlin.scripting.repl.js.JsScriptEvaluator
import kotlin.script.experimental.api.*
import kotlin.script.experimental.host.ScriptingHostConfiguration
import kotlin.script.experimental.jvm.JsDependency
// TODO: the code below has to be considered as temporary hack and removed ASAP.
// Actual ScriptCompilationConfiguration should be set up from CompilerConfiguration.
fun loadScriptConfiguration(configuration: CompilerConfiguration) {
val scriptConfiguration = ScriptCompilationConfiguration {
baseClass("kotlin.Any")
dependencies.append(JsDependency("libraries/stdlib/js-ir/build/classes/kotlin/js/main/"))
platform.put("JS")
}
configuration.add(
ScriptingConfigurationKeys.SCRIPT_DEFINITIONS,
ScriptDefinition.FromConfigurations(ScriptingHostConfiguration(), scriptConfiguration, null)
)
}
class JsScriptEvaluationExtension : AbstractScriptEvaluationExtension() {
override fun setupScriptConfiguration(configuration: CompilerConfiguration) {
loadScriptConfiguration(configuration)
}
override fun createEnvironment(
projectEnvironment: KotlinCoreEnvironment.ProjectEnvironment,
configuration: CompilerConfiguration
): KotlinCoreEnvironment {
return KotlinCoreEnvironment.createForProduction(
projectEnvironment,
configuration,
EnvironmentConfigFiles.JS_CONFIG_FILES
)
}
override fun createScriptEvaluator(): ScriptEvaluator {
return JsScriptEvaluator()
}
private var scriptCompilerProxy: ScriptCompilerProxy? = null
override fun createScriptCompiler(environment: KotlinCoreEnvironment): ScriptCompilerProxy {
return scriptCompilerProxy ?: JsScriptCompilerWithDependenciesProxy(environment).also { scriptCompilerProxy = it }
}
override fun ScriptEvaluationConfiguration.Builder.platformEvaluationConfiguration() {}
override fun isAccepted(arguments: CommonCompilerArguments): Boolean {
return arguments is K2JSCompilerArguments
}
}
@@ -1,63 +0,0 @@
/*
* 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.js
import org.jetbrains.kotlin.backend.common.serialization.signature.IdSignatureDescriptor
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.common.repl.ReplCompileResult
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsManglerDesc
import org.jetbrains.kotlin.ir.backend.js.utils.NameTables
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
import org.jetbrains.kotlin.ir.util.SymbolTable
import org.jetbrains.kotlin.scripting.compiler.plugin.ScriptCompilerProxy
import org.jetbrains.kotlin.scripting.compiler.plugin.impl.withMessageCollector
import org.jetbrains.kotlin.scripting.repl.js.JsCompiledScript
import kotlin.script.experimental.api.*
class JsScriptCompilerWithDependenciesProxy(private val environment: KotlinCoreEnvironment) : ScriptCompilerProxy {
private val nameTables = NameTables(emptyList(), mappedNames = mutableMapOf())
private val symbolTable = SymbolTable(IdSignatureDescriptor(JsManglerDesc), IrFactoryImpl)
private val dependencies: List<ModuleDescriptor> = readLibrariesFromConfiguration(environment.configuration)
private val compiler = JsCoreScriptingCompiler(environment, nameTables, symbolTable, dependencies)
private var scriptDependencyCompiler: JsScriptDependencyCompiler? =
JsScriptDependencyCompiler(environment.configuration, nameTables, symbolTable)
override fun compile(
script: SourceCode,
scriptCompilationConfiguration: ScriptCompilationConfiguration
): ResultWithDiagnostics<CompiledScript> {
val parentMessageCollector = environment.configuration[CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY]
return withMessageCollector(script = script, parentMessageCollector = parentMessageCollector) { messageCollector ->
environment.configuration.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, messageCollector)
try {
val dependenciesCode = scriptDependencyCompiler?.let { scriptDependencyCompiler = null; it.compile(dependencies) } ?: ""
when (val compileResult = compiler.compile(makeReplCodeLine(0, script.text))) {
is ReplCompileResult.CompiledClasses -> {
val compileJsCode = compileResult.data as String
ResultWithDiagnostics.Success(
JsCompiledScript(dependenciesCode + "\n" + compileJsCode, scriptCompilationConfiguration)
)
}
is ReplCompileResult.Incomplete -> ResultWithDiagnostics.Failure(
ScriptDiagnostic(ScriptDiagnostic.incompleteCode, "Incomplete code")
)
is ReplCompileResult.Error -> ResultWithDiagnostics.Failure(
ScriptDiagnostic(
ScriptDiagnostic.unspecifiedError,
message = compileResult.message,
severity = ScriptDiagnostic.Severity.ERROR
)
)
}
} finally {
if (parentMessageCollector != null)
environment.configuration.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, parentMessageCollector)
}
}
}
}