diff --git a/libraries/scripting/common/src/kotlin/script/experimental/api/replData.kt b/libraries/scripting/common/src/kotlin/script/experimental/api/replData.kt index f5008ee8ef7..6b224bb056d 100644 --- a/libraries/scripting/common/src/kotlin/script/experimental/api/replData.kt +++ b/libraries/scripting/common/src/kotlin/script/experimental/api/replData.kt @@ -8,6 +8,7 @@ package kotlin.script.experimental.api import java.io.Serializable import kotlin.script.experimental.util.PropertiesCollection +// Warning: during the transition to the new REPL infrastructure, should be kept in sync with REPL_CODE_LINE_FIRST_NO/REPL_CODE_LINE_FIRST_GEN const val REPL_SNIPPET_FIRST_NO = 1 const val REPL_SNIPPET_FIRST_GEN = 1 @@ -48,3 +49,17 @@ val ScriptCompilationConfigurationKeys.repl */ val ReplScriptCompilationConfigurationKeys.resultFieldPrefix by PropertiesCollection.key("res") +typealias MakeSnippetIdentifier = (ScriptCompilationConfiguration, ReplSnippetId) -> String + +/** + * The REPL snippet class identifier generation function + */ +val ReplScriptCompilationConfigurationKeys.makeSnippetIdentifier by PropertiesCollection.key( + { _, snippetId -> + makeDefaultSnippetIdentifier(snippetId) + }) + +fun makeDefaultSnippetIdentifier(snippetId: ReplSnippetId) = + "Line_${snippetId.no}${if (snippetId.generation > REPL_SNIPPET_FIRST_GEN) "_gen_${snippetId.generation}" else ""}" + + diff --git a/libraries/scripting/common/src/kotlin/script/experimental/api/scriptCompilation.kt b/libraries/scripting/common/src/kotlin/script/experimental/api/scriptCompilation.kt index 7250062b6ed..6b813cfcc43 100644 --- a/libraries/scripting/common/src/kotlin/script/experimental/api/scriptCompilation.kt +++ b/libraries/scripting/common/src/kotlin/script/experimental/api/scriptCompilation.kt @@ -57,6 +57,11 @@ fun ScriptCompilationConfiguration?.with(body: ScriptCompilationConfiguration.Bu */ val ScriptCompilationConfigurationKeys.displayName by PropertiesCollection.key() +/** + * The default script class name + */ +val ScriptCompilationConfigurationKeys.defaultIdentifier by PropertiesCollection.key("Script") + /** * The script filename extension * Used for the primary script definition selection as well as to assign a kotlin-specific file type to the files with the extension in Intellij IDEA diff --git a/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/jsr223/KotlinJsr223ScriptEngineImpl.kt b/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/jsr223/KotlinJsr223ScriptEngineImpl.kt index 16b3a404c97..6076281dfc9 100644 --- a/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/jsr223/KotlinJsr223ScriptEngineImpl.kt +++ b/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/jsr223/KotlinJsr223ScriptEngineImpl.kt @@ -9,9 +9,7 @@ import org.jetbrains.kotlin.cli.common.repl.* import java.util.concurrent.locks.ReentrantReadWriteLock import javax.script.ScriptContext import javax.script.ScriptEngineFactory -import kotlin.script.experimental.api.ScriptCompilationConfiguration -import kotlin.script.experimental.api.ScriptEvaluationConfiguration -import kotlin.script.experimental.api.hostConfiguration +import kotlin.script.experimental.api.* import kotlin.script.experimental.host.ScriptingHostConfiguration import kotlin.script.experimental.host.withDefaultsFrom import kotlin.script.experimental.jvm.baseClassLoader @@ -42,6 +40,18 @@ class KotlinJsr223ScriptEngineImpl( val compilationConfiguration by lazy { ScriptCompilationConfiguration(baseCompilationConfiguration) { hostConfiguration.update { it.withDefaultsFrom(jsr223HostConfiguration) } + repl { + // Snippet classes should be named uniquely, to avoid classloading clashes in the "eval in eval" scenario + // TODO: consider applying the logic for any REPL, alternatively - develop other naming scheme to avoid clashes + makeSnippetIdentifier { configuration, snippetId -> + val scriptContext: ScriptContext? = configuration[ScriptCompilationConfiguration.jsr223.getScriptContext]?.invoke() + val engineState = scriptContext?.let { + it.getBindings(ScriptContext.ENGINE_SCOPE)?.get(KOTLIN_SCRIPT_STATE_BINDINGS_KEY) + } + if (engineState == null) makeDefaultSnippetIdentifier(snippetId) + else "ScriptingHost${System.identityHashCode(engineState).toString(16)}_${makeDefaultSnippetIdentifier(snippetId)}" + } + } } } diff --git a/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/repl/legacyReplCompilation.kt b/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/repl/legacyReplCompilation.kt index 206ddc075c6..8894076db82 100644 --- a/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/repl/legacyReplCompilation.kt +++ b/libraries/scripting/jvm-host/src/kotlin/script/experimental/jvmhost/repl/legacyReplCompilation.kt @@ -74,7 +74,10 @@ internal class SourceCodeFromReplCodeLine( compilationConfiguration: ScriptCompilationConfiguration ) : SourceCode { override val text: String get() = codeLine.code - override val name: String = "${makeScriptBaseName(codeLine)}.${compilationConfiguration[ScriptCompilationConfiguration.fileExtension]}" + override val name: String = + "${compilationConfiguration[ScriptCompilationConfiguration.repl.makeSnippetIdentifier]!!( + compilationConfiguration, ReplSnippetIdImpl(codeLine.no, codeLine.generation, 0) + )}.${compilationConfiguration[ScriptCompilationConfiguration.fileExtension]}" override val locationId: String? = null } diff --git a/plugins/scripting/scripting-compiler-impl/src/org/jetbrains/kotlin/scripting/definitions/ScriptDefinition.kt b/plugins/scripting/scripting-compiler-impl/src/org/jetbrains/kotlin/scripting/definitions/ScriptDefinition.kt index 1816d56aeb2..6ba3a7651a8 100644 --- a/plugins/scripting/scripting-compiler-impl/src/org/jetbrains/kotlin/scripting/definitions/ScriptDefinition.kt +++ b/plugins/scripting/scripting-compiler-impl/src/org/jetbrains/kotlin/scripting/definitions/ScriptDefinition.kt @@ -31,6 +31,7 @@ abstract class ScriptDefinition : UserDataHolderBase() { abstract fun isScript(file: File): Boolean abstract val fileExtension: String abstract val name: String + open val defaultClassName: String = "Script" // TODO: used in settings, find out the reason and refactor accordingly abstract val definitionId: String @@ -138,6 +139,9 @@ abstract class ScriptDefinition : UserDataHolderBase() { compilationConfiguration[ScriptCompilationConfiguration.displayName]?.takeIf { it.isNotBlank() } ?: compilationConfiguration[ScriptCompilationConfiguration.baseClass]!!.typeName.substringAfterLast('.') + override val defaultClassName: String + get() = compilationConfiguration[ScriptCompilationConfiguration.defaultIdentifier] ?: super.defaultClassName + override val definitionId: String get() = compilationConfiguration[ScriptCompilationConfiguration.baseClass]!!.typeName override val contextClassLoader: ClassLoader? by lazy { diff --git a/plugins/scripting/scripting-compiler-impl/src/org/jetbrains/kotlin/scripting/resolve/refineCompilationConfiguration.kt b/plugins/scripting/scripting-compiler-impl/src/org/jetbrains/kotlin/scripting/resolve/refineCompilationConfiguration.kt index 58a831b35e8..5c9339a2fe5 100644 --- a/plugins/scripting/scripting-compiler-impl/src/org/jetbrains/kotlin/scripting/resolve/refineCompilationConfiguration.kt +++ b/plugins/scripting/scripting-compiler-impl/src/org/jetbrains/kotlin/scripting/resolve/refineCompilationConfiguration.kt @@ -291,7 +291,7 @@ fun SourceCode.getVirtualFile(definition: ScriptDefinition): VirtualFile { val vFile = LocalFileSystem.getInstance().findFileByIoFile(file) if (vFile != null) return vFile } - val scriptName = withCorrectExtension(name ?: "script", definition.fileExtension) + val scriptName = withCorrectExtension(name ?: definition.defaultClassName, definition.fileExtension) val scriptPath = when (this) { is FileScriptSource -> file.path is ExternalSourceCode -> externalLocation.toString() diff --git a/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/repl/GenericReplCompiler.kt b/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/repl/GenericReplCompiler.kt index 11ed61c8900..08938cafbe3 100644 --- a/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/repl/GenericReplCompiler.kt +++ b/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/repl/GenericReplCompiler.kt @@ -109,7 +109,6 @@ open class GenericReplCompiler( CompilationErrorHandler.THROW_EXCEPTION ) - val generatedClassname = makeScriptBaseName(codeLine) compilerState.history.push(LineId(codeLine), scriptDescriptor) val classes = generationState.factory.asList().map { CompiledClassData(it.relativePath, it.asByteArray()) } @@ -117,7 +116,7 @@ open class GenericReplCompiler( return ReplCompileResult.CompiledClasses( LineId(codeLine), compilerState.history.map { it.id }, - generatedClassname, + scriptDescriptor.name.identifier, classes, generationState.scriptSpecific.resultFieldName != null, classpathAddendum ?: emptyList(),