Improve class name generation for scripts and REPL snippets

- allow to override default name/scheme
- implement host/engine specific names for jsr223 to avoid classloading
  clashes (also fix "eval in eval" test)
This commit is contained in:
Ilya Chernikov
2019-09-16 14:13:09 +02:00
parent 6de05bb2c7
commit d3b826c71d
7 changed files with 43 additions and 7 deletions
@@ -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<String>("res")
typealias MakeSnippetIdentifier = (ScriptCompilationConfiguration, ReplSnippetId) -> String
/**
* The REPL snippet class identifier generation function
*/
val ReplScriptCompilationConfigurationKeys.makeSnippetIdentifier by PropertiesCollection.key<MakeSnippetIdentifier>(
{ _, snippetId ->
makeDefaultSnippetIdentifier(snippetId)
})
fun makeDefaultSnippetIdentifier(snippetId: ReplSnippetId) =
"Line_${snippetId.no}${if (snippetId.generation > REPL_SNIPPET_FIRST_GEN) "_gen_${snippetId.generation}" else ""}"
@@ -57,6 +57,11 @@ fun ScriptCompilationConfiguration?.with(body: ScriptCompilationConfiguration.Bu
*/
val ScriptCompilationConfigurationKeys.displayName by PropertiesCollection.key<String>()
/**
* The default script class name
*/
val ScriptCompilationConfigurationKeys.defaultIdentifier by PropertiesCollection.key<String>("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
@@ -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)}"
}
}
}
}
@@ -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
}