diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compiler.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compiler.kt index b5dc688b35a..629ae7a824b 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compiler.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compiler.kt @@ -13,11 +13,23 @@ import org.jetbrains.kotlin.config.CompilerConfiguration import org.jetbrains.kotlin.ir.backend.js.lower.moveBodilessDeclarationsToSeparatePlace import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.IrModuleToJsTransformer import org.jetbrains.kotlin.ir.backend.js.utils.JsMainFunctionDetector +import org.jetbrains.kotlin.ir.backend.js.utils.NameTables +import org.jetbrains.kotlin.ir.declarations.IrModuleFragment import org.jetbrains.kotlin.ir.util.ExternalDependenciesGenerator import org.jetbrains.kotlin.ir.util.patchDeclarationParents import org.jetbrains.kotlin.library.KotlinLibrary import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.utils.DFS + +fun sortDependencies(dependencies: Collection): Collection { + val mapping = dependencies.map { it.descriptor to it }.toMap() + + return DFS.topologicalOrder(dependencies) { m -> + val descriptor = m.descriptor + descriptor.allDependencyModules.filter { it != descriptor }.map { mapping[it] } + }.reversed() +} class CompilerResult( val jsCode: String, @@ -72,4 +84,16 @@ fun compile( val transformer = IrModuleToJsTransformer(context, mainFunction, mainArguments) return transformer.generateModule(moduleFragment) -} \ No newline at end of file +} + +fun compileForRepl( + context: JsIrBackendContext, + moduleFragment: IrModuleFragment, + nameTables: NameTables +): String { + moveBodilessDeclarationsToSeparatePlace(context, moduleFragment) + jsPhases.invokeToplevel(PhaseConfig(jsPhases), context, moduleFragment) + + val transformer = IrModuleToJsTransformer(context, null, null, true, nameTables) + return transformer.generateModule(moduleFragment).jsCode +} diff --git a/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/klib.kt b/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/klib.kt index 498f4236921..5706d8afe75 100644 --- a/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/klib.kt +++ b/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/klib.kt @@ -222,13 +222,31 @@ private fun runAnalysisAndPreparePsi2Ir(depsDescriptors: ModulesStructure): Gene ) } -private fun GeneratorContext.generateModuleFragment(files: List, deserializer: JsIrLinker? = null) = +fun GeneratorContext.generateModuleFragment(files: List, deserializer: IrDeserializer? = null) = Psi2IrTranslator(languageVersionSettings, configuration).generateModuleFragment(this, files, deserializer) private fun createBuiltIns(storageManager: StorageManager) = object : KotlinBuiltIns(storageManager) {} val JsFactories = KlibMetadataFactories(::createBuiltIns, DynamicTypeDeserializer) +fun getModuleDescriptorByLibrary( + current: KotlinLibrary +): ModuleDescriptorImpl { + val parts = loadKlibMetadataParts(current) + val isBuiltIns = parts.importedModules.isEmpty() + return loadKlibMetadata( + parts, + current, + isBuiltIns, + LookupTracker.DO_NOTHING, + LockBasedStorageManager("ModulesStructure"), + JsKlibMetadataVersion.INSTANCE, + LanguageVersionSettingsImpl.DEFAULT, + null, + emptyList() + ) +} + private class ModulesStructure( private val project: Project, private val files: List, @@ -298,7 +316,6 @@ private class ModulesStructure( getModuleDescriptor(builtInsDep) else null // null in case compiling builtInModule itself - } private fun getDescriptorForElement( diff --git a/libraries/scripting/jvm/src/kotlin/script/experimental/jvm/jvmScriptCompilation.kt b/libraries/scripting/jvm/src/kotlin/script/experimental/jvm/jvmScriptCompilation.kt index 01c2ab10aae..8c35113d162 100644 --- a/libraries/scripting/jvm/src/kotlin/script/experimental/jvm/jvmScriptCompilation.kt +++ b/libraries/scripting/jvm/src/kotlin/script/experimental/jvm/jvmScriptCompilation.kt @@ -25,6 +25,8 @@ class JvmDependencyFromClassLoader(val classLoaderGetter: ClassLoaderByConfigura fun getClassLoader(configuration: ScriptCompilationConfiguration): ClassLoader = classLoaderGetter(configuration) } +data class JsDependency(val path: String) : ScriptDependency + interface JvmScriptCompilationConfigurationKeys open class JvmScriptCompilationConfigurationBuilder : PropertiesCollection.Builder(), JvmScriptCompilationConfigurationKeys { diff --git a/plugins/scripting/scripting-compiler/build.gradle.kts b/plugins/scripting/scripting-compiler/build.gradle.kts index b2bae3e3c5d..856c03f27ea 100644 --- a/plugins/scripting/scripting-compiler/build.gradle.kts +++ b/plugins/scripting/scripting-compiler/build.gradle.kts @@ -12,8 +12,11 @@ dependencies { compileOnly(project(":compiler:psi")) compileOnly(project(":compiler:plugin-api")) compileOnly(project(":compiler:cli")) + compileOnly(project(":compiler:backend.js")) compileOnly(project(":core:descriptors.runtime")) compile(project(":kotlin-scripting-common")) + compile(project(":kotlin-scripting-js")) + compile(project(":kotlin-util-klib")) compile(project(":kotlin-scripting-jvm")) compile(project(":kotlin-scripting-compiler-impl")) compile(kotlinStdlib()) @@ -25,6 +28,7 @@ dependencies { testCompile(project(":compiler:cli")) testCompile(project(":compiler:cli-common")) testCompile(project(":compiler:frontend.java")) + testCompile(project(":compiler:backend.js")) testCompile(projectTests(":compiler:tests-common")) testCompile(commonDep("junit:junit")) diff --git a/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/repl/js/CoreScriptingJsCompiler.kt b/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/repl/js/CoreScriptingJsCompiler.kt new file mode 100644 index 00000000000..fab17a3c0d0 --- /dev/null +++ b/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/repl/js/CoreScriptingJsCompiler.kt @@ -0,0 +1,115 @@ +/* + * 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.js + +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.* +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.* +import org.jetbrains.kotlin.ir.backend.js.utils.NameTables +import org.jetbrains.kotlin.ir.declarations.IrModuleFragment +import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns +import org.jetbrains.kotlin.ir.util.ExternalDependenciesGenerator +import org.jetbrains.kotlin.ir.util.IrDeserializer +import org.jetbrains.kotlin.ir.util.SymbolTable +import org.jetbrains.kotlin.js.config.JSConfigurationKeys +import org.jetbrains.kotlin.psi2ir.Psi2IrTranslator +import org.jetbrains.kotlin.serialization.js.ModuleKind +import kotlin.script.experimental.api.valueOr +import kotlin.script.experimental.host.StringScriptSource + +class DeserializerWithDependencies(val deserializer: IrDeserializer, val dependencies: List) + +class CoreScriptingJsCompiler( + private val environment: KotlinCoreEnvironment, + private val nameTables: NameTables, + private val dependencyDescriptors: List, + private val createDeserializer: (ModuleDescriptor, SymbolTable, IrBuiltIns) -> DeserializerWithDependencies? = { _, _, _ -> null } +) { + private val analyzerEngine: JsReplCodeAnalyzer = JsReplCodeAnalyzer(environment.project, dependencyDescriptors) + private val symbolTable: SymbolTable = SymbolTable() + + fun compile(codeLine: ReplCodeLine): ReplCompileResult { + val snippet = codeLine.code + val snippetId = codeLine.no + + val messageCollector = environment.configuration[CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY] as MessageCollector + + 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 }) } + + analyzerEngine.analyzeReplLine(snippetKtFile, codeLine).also { + AnalyzerWithCompilerReport.reportDiagnostics(it, messageCollector) + if (messageCollector.hasErrors()) return ReplCompileResult.Error("Error while analysis") + } + + val psi2ir = Psi2IrTranslator(environment.configuration.languageVersionSettings) + val psi2irContext = psi2ir.createGeneratorContext( + analyzerEngine.context.module, + analyzerEngine.trace.bindingContext, + symbolTable + ) + + val deserializerWithDependencies = createDeserializer(psi2irContext.moduleDescriptor, symbolTable, psi2irContext.irBuiltIns) + + val irModuleFragment = psi2irContext.generateModuleFragment(listOf(snippetKtFile), deserializerWithDependencies?.deserializer) + + val deserializedFragments = deserializerWithDependencies?.dependencies ?: emptyList() + + val irFiles = sortDependencies(deserializedFragments).flatMap { it.files } + irModuleFragment.files + irModuleFragment.files.clear() + irModuleFragment.files += irFiles + + + val context = JsIrBackendContext( + irModuleFragment.descriptor, + psi2irContext.irBuiltIns, + psi2irContext.symbolTable, + irModuleFragment, + emptySet(), + environment.configuration, + true + ) + + ExternalDependenciesGenerator( + irModuleFragment.descriptor, + psi2irContext.symbolTable, + psi2irContext.irBuiltIns, + deserializer = deserializerWithDependencies?.deserializer + ).generateUnboundSymbolsAsDependencies() + + with(context.implicitDeclarationFile) { + if (!irModuleFragment.files.contains(this)) { + irModuleFragment.files += this + } + } + context.implicitDeclarationFile.declarations.clear() + + environment.configuration.put(JSConfigurationKeys.MODULE_KIND, ModuleKind.PLAIN) + + val code = compileForRepl( + context, + irModuleFragment, + nameTables + ) + + return createCompileResult( + LineId(codeLine), + code + ) + } +} diff --git a/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/repl/js/JsReplCodeAnalyzer.kt b/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/repl/js/JsReplCodeAnalyzer.kt new file mode 100644 index 00000000000..60cdbc3e033 --- /dev/null +++ b/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/repl/js/JsReplCodeAnalyzer.kt @@ -0,0 +1,86 @@ +/* + * 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.js + +import com.intellij.openapi.project.Project +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.cli.common.repl.ReplCodeLine +import org.jetbrains.kotlin.cli.jvm.compiler.NoScopeRecordCliBindingTrace +import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl +import org.jetbrains.kotlin.context.ContextForNewModule +import org.jetbrains.kotlin.context.MutableModuleContext +import org.jetbrains.kotlin.context.ProjectContext +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.ExpectActualTracker +import org.jetbrains.kotlin.incremental.components.LookupTracker +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.resolve.BindingTraceContext +import org.jetbrains.kotlin.resolve.LazyTopDownAnalyzer +import org.jetbrains.kotlin.resolve.TopDownAnalysisMode +import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics +import org.jetbrains.kotlin.resolve.lazy.declarations.FileBasedDeclarationProviderFactory +import org.jetbrains.kotlin.scripting.compiler.plugin.repl.ReplCodeAnalyzer +import org.jetbrains.kotlin.scripting.definitions.ScriptPriorities + +class JsReplCodeAnalyzer(private val project: Project, private val dependencies: List) { + + private val replState = ReplCodeAnalyzer.ResettableAnalyzerState() + val trace: BindingTraceContext = NoScopeRecordCliBindingTrace() + + private val builtIns: KotlinBuiltIns = dependencies.single { it.allDependencyModules.isEmpty() }.builtIns + lateinit var context: MutableModuleContext + private fun createTopDownAnalyzerJS(files: Collection): LazyTopDownAnalyzer { + context = ContextForNewModule( + ProjectContext(project, "TopDownAnalyzer for JS"), + Name.special("