Rearrange scripting host classes, so the runner part is independent from the compiler
This commit is contained in:
+1
@@ -10,6 +10,7 @@ import kotlin.script.experimental.host.ScriptingHostConfiguration
|
|||||||
import kotlin.script.experimental.host.createCompilationConfigurationFromTemplate
|
import kotlin.script.experimental.host.createCompilationConfigurationFromTemplate
|
||||||
import kotlin.script.experimental.host.BasicScriptingHost
|
import kotlin.script.experimental.host.BasicScriptingHost
|
||||||
import kotlin.script.experimental.host.createEvaluationConfigurationFromTemplate
|
import kotlin.script.experimental.host.createEvaluationConfigurationFromTemplate
|
||||||
|
import kotlin.script.experimental.jvm.BasicJvmScriptEvaluator
|
||||||
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
|
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
|
||||||
|
|
||||||
open class BasicJvmScriptingHost(
|
open class BasicJvmScriptingHost(
|
||||||
|
|||||||
+21
@@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||||
|
* that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package kotlin.script.experimental.jvmhost.impl
|
||||||
|
|
||||||
|
import java.io.Serializable
|
||||||
|
import kotlin.script.experimental.jvm.impl.KJvmCompiledModule
|
||||||
|
|
||||||
|
class KJvmCompiledModuleInMemory(val compilerOutputFiles: Map<String, ByteArray>) : KJvmCompiledModule,
|
||||||
|
Serializable {
|
||||||
|
|
||||||
|
override fun createClassLoader(baseClassLoader: ClassLoader?): ClassLoader =
|
||||||
|
CompiledScriptClassLoader(baseClassLoader, compilerOutputFiles)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@JvmStatic
|
||||||
|
private val serialVersionUID = 0L
|
||||||
|
}
|
||||||
|
}
|
||||||
-76
@@ -1,76 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
|
||||||
* that can be found in the license/LICENSE.txt file.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package kotlin.script.experimental.jvmhost.impl
|
|
||||||
|
|
||||||
import java.io.ObjectInputStream
|
|
||||||
import java.io.ObjectOutputStream
|
|
||||||
import java.io.Serializable
|
|
||||||
import kotlin.reflect.KClass
|
|
||||||
import kotlin.script.experimental.api.*
|
|
||||||
import kotlin.script.experimental.jvmhost.getOrCreateActualClassloader
|
|
||||||
|
|
||||||
class KJvmCompiledScript<out ScriptBase : Any>(
|
|
||||||
sourceLocationId: String?,
|
|
||||||
compilationConfiguration: ScriptCompilationConfiguration,
|
|
||||||
private var scriptClassFQName: String,
|
|
||||||
otherScripts: List<CompiledScript<*>> = emptyList(),
|
|
||||||
internal var compiledModule: KJvmCompiledModule
|
|
||||||
) : CompiledScript<ScriptBase>, Serializable {
|
|
||||||
|
|
||||||
private var _sourceLocationId: String? = sourceLocationId
|
|
||||||
|
|
||||||
override val sourceLocationId: String?
|
|
||||||
get() = _sourceLocationId
|
|
||||||
|
|
||||||
private var _compilationConfiguration: ScriptCompilationConfiguration? = compilationConfiguration
|
|
||||||
|
|
||||||
override val compilationConfiguration: ScriptCompilationConfiguration
|
|
||||||
get() = _compilationConfiguration!!
|
|
||||||
|
|
||||||
private var _otherScripts: List<CompiledScript<*>> = otherScripts
|
|
||||||
|
|
||||||
override val otherScripts: List<CompiledScript<*>>
|
|
||||||
get() = _otherScripts
|
|
||||||
|
|
||||||
override suspend fun getClass(scriptEvaluationConfiguration: ScriptEvaluationConfiguration?): ResultWithDiagnostics<KClass<*>> = try {
|
|
||||||
// ensuring proper defaults are used
|
|
||||||
val actualEvaluationConfiguration = scriptEvaluationConfiguration ?: ScriptEvaluationConfiguration()
|
|
||||||
val classLoader = getOrCreateActualClassloader(actualEvaluationConfiguration)
|
|
||||||
|
|
||||||
val clazz = classLoader.loadClass(scriptClassFQName).kotlin
|
|
||||||
clazz.asSuccess()
|
|
||||||
} catch (e: Throwable) {
|
|
||||||
ResultWithDiagnostics.Failure(
|
|
||||||
ScriptDiagnostic(
|
|
||||||
"Unable to instantiate class $scriptClassFQName",
|
|
||||||
sourcePath = sourceLocationId,
|
|
||||||
exception = e
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun writeObject(outputStream: ObjectOutputStream) {
|
|
||||||
outputStream.writeObject(compilationConfiguration)
|
|
||||||
outputStream.writeObject(sourceLocationId)
|
|
||||||
outputStream.writeObject(otherScripts)
|
|
||||||
outputStream.writeObject(compiledModule)
|
|
||||||
outputStream.writeObject(scriptClassFQName)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
private fun readObject(inputStream: ObjectInputStream) {
|
|
||||||
_compilationConfiguration = inputStream.readObject() as ScriptCompilationConfiguration
|
|
||||||
_sourceLocationId = inputStream.readObject() as String?
|
|
||||||
_otherScripts = inputStream.readObject() as List<CompiledScript<*>>
|
|
||||||
compiledModule = inputStream.readObject() as KJvmCompiledModuleInMemory
|
|
||||||
scriptClassFQName = inputStream.readObject() as String
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
@JvmStatic
|
|
||||||
private val serialVersionUID = 2L
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+1
@@ -62,6 +62,7 @@ import kotlin.script.experimental.host.getMergedScriptText
|
|||||||
import kotlin.script.experimental.host.getScriptingClass
|
import kotlin.script.experimental.host.getScriptingClass
|
||||||
import kotlin.script.experimental.jvm.JvmDependency
|
import kotlin.script.experimental.jvm.JvmDependency
|
||||||
import kotlin.script.experimental.jvm.impl.BridgeDependenciesResolver
|
import kotlin.script.experimental.jvm.impl.BridgeDependenciesResolver
|
||||||
|
import kotlin.script.experimental.jvm.impl.KJvmCompiledScript
|
||||||
import kotlin.script.experimental.jvm.jdkHome
|
import kotlin.script.experimental.jvm.jdkHome
|
||||||
import kotlin.script.experimental.jvm.jvm
|
import kotlin.script.experimental.jvm.jvm
|
||||||
import kotlin.script.experimental.jvm.util.KotlinJars
|
import kotlin.script.experimental.jvm.util.KotlinJars
|
||||||
|
|||||||
-159
@@ -1,159 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
|
||||||
* that can be found in the license/LICENSE.txt file.
|
|
||||||
*/
|
|
||||||
|
|
||||||
@file:Suppress("unused")
|
|
||||||
|
|
||||||
package kotlin.script.experimental.jvmhost
|
|
||||||
|
|
||||||
import java.net.URLClassLoader
|
|
||||||
import kotlin.reflect.KClass
|
|
||||||
import kotlin.script.experimental.api.*
|
|
||||||
import kotlin.script.experimental.jvm.JvmDependency
|
|
||||||
import kotlin.script.experimental.jvmhost.impl.KJvmCompiledScript
|
|
||||||
import kotlin.script.experimental.util.PropertiesCollection
|
|
||||||
|
|
||||||
interface JvmScriptEvaluationConfigurationKeys
|
|
||||||
|
|
||||||
open class JvmScriptEvaluationConfigurationBuilder : PropertiesCollection.Builder(), JvmScriptEvaluationConfigurationKeys {
|
|
||||||
|
|
||||||
companion object : JvmScriptEvaluationConfigurationBuilder()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The base classloader to use for script classes loading
|
|
||||||
*/
|
|
||||||
val JvmScriptEvaluationConfigurationKeys.baseClassLoader by PropertiesCollection.key<ClassLoader?>(Thread.currentThread().contextClassLoader)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load script dependencies before evaluation, true by default
|
|
||||||
* If false, it is assumed that the all dependencies will be provided via baseClassLoader
|
|
||||||
*/
|
|
||||||
val JvmScriptEvaluationConfigurationKeys.loadDependencies by PropertiesCollection.key<Boolean>(true)
|
|
||||||
|
|
||||||
internal val JvmScriptEvaluationConfigurationKeys.actualClassLoader by PropertiesCollection.key<ClassLoader?>()
|
|
||||||
|
|
||||||
internal val JvmScriptEvaluationConfigurationKeys.scriptsInstancesSharingMap by PropertiesCollection.key<MutableMap<KClass<*>, EvaluationResult>>()
|
|
||||||
|
|
||||||
val ScriptEvaluationConfigurationKeys.jvm get() = JvmScriptEvaluationConfigurationBuilder()
|
|
||||||
|
|
||||||
class JvmScriptEvaluationContext(
|
|
||||||
val classLoader: ClassLoader,
|
|
||||||
val scriptsInstancesSharingMap: MutableMap<KClass<*>, EvaluationResult>?
|
|
||||||
)
|
|
||||||
|
|
||||||
open class BasicJvmScriptEvaluator : ScriptEvaluator {
|
|
||||||
|
|
||||||
override suspend operator fun invoke(
|
|
||||||
compiledScript: CompiledScript<*>,
|
|
||||||
scriptEvaluationConfiguration: ScriptEvaluationConfiguration
|
|
||||||
): ResultWithDiagnostics<EvaluationResult> = try {
|
|
||||||
val configuration = getConfigurationWithClassloader(compiledScript, scriptEvaluationConfiguration)
|
|
||||||
|
|
||||||
compiledScript.getClass(configuration).onSuccess { scriptClass ->
|
|
||||||
// in the future, when (if) we'll stop to compile everything into constructor
|
|
||||||
// run as SAM
|
|
||||||
// return res
|
|
||||||
|
|
||||||
val sharedScripts = configuration[ScriptEvaluationConfiguration.jvm.scriptsInstancesSharingMap]
|
|
||||||
|
|
||||||
sharedScripts?.get(scriptClass)?.asSuccess()
|
|
||||||
?: compiledScript.otherScripts.mapSuccess {
|
|
||||||
invoke(it, configuration)
|
|
||||||
}.onSuccess { importedScriptsEvalResults ->
|
|
||||||
|
|
||||||
val refinedEvalConfiguration =
|
|
||||||
configuration[ScriptEvaluationConfiguration.refineConfigurationBeforeEvaluate]
|
|
||||||
?.handler?.invoke(ScriptEvaluationConfigurationRefinementContext(compiledScript, configuration))
|
|
||||||
?.onFailure {
|
|
||||||
return@invoke ResultWithDiagnostics.Failure(it.reports)
|
|
||||||
}
|
|
||||||
?.resultOrNull()
|
|
||||||
?: configuration
|
|
||||||
|
|
||||||
scriptClass.evalWithConfigAndOtherScriptsResults(refinedEvalConfiguration, importedScriptsEvalResults).let {
|
|
||||||
sharedScripts?.put(scriptClass, it)
|
|
||||||
ResultWithDiagnostics.Success(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e: Throwable) {
|
|
||||||
ResultWithDiagnostics.Failure(e.asDiagnostics("Error evaluating script", path = compiledScript.sourceLocationId))
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getConfigurationWithClassloader(
|
|
||||||
script: CompiledScript<*>, baseConfiguration: ScriptEvaluationConfiguration
|
|
||||||
): ScriptEvaluationConfiguration =
|
|
||||||
if (baseConfiguration.containsKey(ScriptEvaluationConfiguration.jvm.actualClassLoader))
|
|
||||||
baseConfiguration
|
|
||||||
else {
|
|
||||||
val jvmScript = (script as? KJvmCompiledScript<*>)
|
|
||||||
?: throw IllegalArgumentException("Unexpected compiled script type: $script")
|
|
||||||
|
|
||||||
val classloader = jvmScript.getOrCreateActualClassloader(baseConfiguration)
|
|
||||||
|
|
||||||
ScriptEvaluationConfiguration(baseConfiguration) {
|
|
||||||
ScriptEvaluationConfiguration.jvm.actualClassLoader(classloader)
|
|
||||||
if (baseConfiguration[ScriptEvaluationConfiguration.scriptsInstancesSharing] == true) {
|
|
||||||
ScriptEvaluationConfiguration.jvm.scriptsInstancesSharingMap(mutableMapOf())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun KClass<*>.evalWithConfigAndOtherScriptsResults(
|
|
||||||
refinedEvalConfiguration: ScriptEvaluationConfiguration,
|
|
||||||
importedScriptsEvalResults: List<EvaluationResult>
|
|
||||||
): EvaluationResult {
|
|
||||||
val args = ArrayList<Any?>()
|
|
||||||
|
|
||||||
refinedEvalConfiguration[ScriptEvaluationConfiguration.constructorArgs]?.let {
|
|
||||||
args.addAll(it)
|
|
||||||
}
|
|
||||||
refinedEvalConfiguration[ScriptEvaluationConfiguration.implicitReceivers]?.let {
|
|
||||||
args.addAll(it)
|
|
||||||
}
|
|
||||||
refinedEvalConfiguration[ScriptEvaluationConfiguration.providedProperties]?.forEach {
|
|
||||||
args.add(it.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
importedScriptsEvalResults.forEach {
|
|
||||||
args.add((it.returnValue as ResultValue.Value).scriptInstance)
|
|
||||||
}
|
|
||||||
|
|
||||||
val ctor = java.constructors.single()
|
|
||||||
val instance = ctor.newInstance(*args.toArray())
|
|
||||||
|
|
||||||
return EvaluationResult(ResultValue.Value("", instance, "", instance), refinedEvalConfiguration)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun KJvmCompiledScript<*>.getOrCreateActualClassloader(evaluationConfiguration: ScriptEvaluationConfiguration): ClassLoader =
|
|
||||||
evaluationConfiguration[ScriptEvaluationConfiguration.jvm.actualClassLoader] ?: run {
|
|
||||||
val baseClassLoader = evaluationConfiguration[ScriptEvaluationConfiguration.jvm.baseClassLoader]
|
|
||||||
val classLoaderWithDeps =
|
|
||||||
if (evaluationConfiguration[ScriptEvaluationConfiguration.jvm.loadDependencies] == false) baseClassLoader
|
|
||||||
else makeClassLoaderFromDependencies(baseClassLoader)
|
|
||||||
compiledModule.createClassLoader(classLoaderWithDeps)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun CompiledScript<*>.makeClassLoaderFromDependencies(baseClassLoader: ClassLoader?): ClassLoader? {
|
|
||||||
val processedScripts = mutableSetOf<CompiledScript<*>>()
|
|
||||||
fun seq(res: Sequence<CompiledScript<*>>, script: CompiledScript<*>): Sequence<CompiledScript<*>> {
|
|
||||||
if (processedScripts.contains(script)) return res
|
|
||||||
processedScripts.add(script)
|
|
||||||
return script.otherScripts.asSequence().fold(res + script, ::seq)
|
|
||||||
}
|
|
||||||
|
|
||||||
val dependencies = seq(emptySequence(), this).flatMap { script ->
|
|
||||||
script.compilationConfiguration[ScriptCompilationConfiguration.dependencies]
|
|
||||||
?.asSequence()
|
|
||||||
?.flatMap { dep ->
|
|
||||||
(dep as? JvmDependency)?.classpath?.asSequence()?.map { it.toURI().toURL() } ?: emptySequence()
|
|
||||||
}
|
|
||||||
?: emptySequence()
|
|
||||||
}.distinct()
|
|
||||||
// TODO: previous dependencies and classloaders should be taken into account here
|
|
||||||
return if (dependencies.none()) baseClassLoader
|
|
||||||
else URLClassLoader(dependencies.toList().toTypedArray(), baseClassLoader)
|
|
||||||
}
|
|
||||||
+1
-1
@@ -13,7 +13,7 @@ import java.util.jar.Manifest
|
|||||||
import kotlin.script.experimental.api.*
|
import kotlin.script.experimental.api.*
|
||||||
import kotlin.script.experimental.jvm.JvmDependency
|
import kotlin.script.experimental.jvm.JvmDependency
|
||||||
import kotlin.script.experimental.jvmhost.impl.KJvmCompiledModuleInMemory
|
import kotlin.script.experimental.jvmhost.impl.KJvmCompiledModuleInMemory
|
||||||
import kotlin.script.experimental.jvmhost.impl.KJvmCompiledScript
|
import kotlin.script.experimental.jvm.impl.KJvmCompiledScript
|
||||||
|
|
||||||
// TODO: generate execution code (main)
|
// TODO: generate execution code (main)
|
||||||
|
|
||||||
|
|||||||
+32
@@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||||
|
* that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@file:Suppress("unused")
|
||||||
|
|
||||||
|
package kotlin.script.experimental.jvmhost
|
||||||
|
|
||||||
|
import kotlin.script.experimental.api.ScriptEvaluationConfiguration
|
||||||
|
import kotlin.script.experimental.api.ScriptEvaluationConfigurationKeys
|
||||||
|
import kotlin.script.experimental.jvm.baseClassLoader
|
||||||
|
import kotlin.script.experimental.jvm.jvm
|
||||||
|
import kotlin.script.experimental.util.PropertiesCollection
|
||||||
|
|
||||||
|
@Deprecated("use the same definitions from kotlin.script.experimental.jvm package", level = DeprecationLevel.WARNING)
|
||||||
|
interface JvmScriptEvaluationConfigurationKeys
|
||||||
|
|
||||||
|
@Deprecated("use the same definitions from kotlin.script.experimental.jvm package", level = DeprecationLevel.WARNING)
|
||||||
|
open class JvmScriptEvaluationConfigurationBuilder : PropertiesCollection.Builder(), JvmScriptEvaluationConfigurationKeys {
|
||||||
|
|
||||||
|
companion object : JvmScriptEvaluationConfigurationBuilder()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated("use the same definitions from kotlin.script.experimental.jvm package", level = DeprecationLevel.ERROR)
|
||||||
|
val JvmScriptEvaluationConfigurationKeys.baseClassLoader get() = ScriptEvaluationConfiguration.jvm.baseClassLoader
|
||||||
|
|
||||||
|
@Deprecated("use the same definitions from kotlin.script.experimental.jvm package", level = DeprecationLevel.ERROR)
|
||||||
|
val ScriptEvaluationConfigurationKeys.jvm get() = ScriptEvaluationConfiguration.jvm
|
||||||
|
|
||||||
|
@Deprecated("use the same definitions from kotlin.script.experimental.jvm package", level = DeprecationLevel.ERROR)
|
||||||
|
open class BasicJvmScriptEvaluator : kotlin.script.experimental.jvm.BasicJvmScriptEvaluator()
|
||||||
+2
-1
@@ -18,10 +18,11 @@ import kotlin.script.experimental.api.*
|
|||||||
import kotlin.script.experimental.host.BasicScriptingHost
|
import kotlin.script.experimental.host.BasicScriptingHost
|
||||||
import kotlin.script.experimental.host.FileScriptSource
|
import kotlin.script.experimental.host.FileScriptSource
|
||||||
import kotlin.script.experimental.host.toScriptSource
|
import kotlin.script.experimental.host.toScriptSource
|
||||||
|
import kotlin.script.experimental.jvm.BasicJvmScriptEvaluator
|
||||||
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
|
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
|
||||||
import kotlin.script.experimental.jvmhost.*
|
import kotlin.script.experimental.jvmhost.*
|
||||||
import kotlin.script.experimental.jvmhost.impl.CompiledScriptClassLoader
|
import kotlin.script.experimental.jvmhost.impl.CompiledScriptClassLoader
|
||||||
import kotlin.script.experimental.jvmhost.impl.KJvmCompiledScript
|
import kotlin.script.experimental.jvm.impl.KJvmCompiledScript
|
||||||
import kotlin.script.templates.standard.SimpleScriptTemplate
|
import kotlin.script.templates.standard.SimpleScriptTemplate
|
||||||
|
|
||||||
class ScriptingHostTest : TestCase() {
|
class ScriptingHostTest : TestCase() {
|
||||||
|
|||||||
@@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||||
|
* that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package kotlin.script.experimental.jvm
|
||||||
|
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
import kotlin.script.experimental.api.*
|
||||||
|
import kotlin.script.experimental.jvm.impl.getConfigurationWithClassloader
|
||||||
|
import kotlin.script.experimental.jvm.impl.sharedScripts
|
||||||
|
|
||||||
|
open class BasicJvmScriptEvaluator : ScriptEvaluator {
|
||||||
|
|
||||||
|
override suspend operator fun invoke(
|
||||||
|
compiledScript: CompiledScript<*>,
|
||||||
|
scriptEvaluationConfiguration: ScriptEvaluationConfiguration
|
||||||
|
): ResultWithDiagnostics<EvaluationResult> = try {
|
||||||
|
val configuration = getConfigurationWithClassloader(compiledScript, scriptEvaluationConfiguration)
|
||||||
|
|
||||||
|
compiledScript.getClass(configuration).onSuccess { scriptClass ->
|
||||||
|
// in the future, when (if) we'll stop to compile everything into constructor
|
||||||
|
// run as SAM
|
||||||
|
// return res
|
||||||
|
|
||||||
|
val sharedScripts = configuration.sharedScripts
|
||||||
|
|
||||||
|
sharedScripts?.get(scriptClass)?.asSuccess()
|
||||||
|
?: compiledScript.otherScripts.mapSuccess {
|
||||||
|
invoke(it, configuration)
|
||||||
|
}.onSuccess { importedScriptsEvalResults ->
|
||||||
|
|
||||||
|
val refinedEvalConfiguration =
|
||||||
|
configuration[ScriptEvaluationConfiguration.refineConfigurationBeforeEvaluate]
|
||||||
|
?.handler?.invoke(
|
||||||
|
ScriptEvaluationConfigurationRefinementContext(
|
||||||
|
compiledScript,
|
||||||
|
configuration
|
||||||
|
)
|
||||||
|
)
|
||||||
|
?.onFailure {
|
||||||
|
return@invoke ResultWithDiagnostics.Failure(it.reports)
|
||||||
|
}
|
||||||
|
?.resultOrNull()
|
||||||
|
?: configuration
|
||||||
|
|
||||||
|
scriptClass.evalWithConfigAndOtherScriptsResults(refinedEvalConfiguration, importedScriptsEvalResults).let {
|
||||||
|
sharedScripts?.put(scriptClass, it)
|
||||||
|
ResultWithDiagnostics.Success(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
ResultWithDiagnostics.Failure(
|
||||||
|
e.asDiagnostics(
|
||||||
|
"Error evaluating script",
|
||||||
|
path = compiledScript.sourceLocationId
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun KClass<*>.evalWithConfigAndOtherScriptsResults(
|
||||||
|
refinedEvalConfiguration: ScriptEvaluationConfiguration,
|
||||||
|
importedScriptsEvalResults: List<EvaluationResult>
|
||||||
|
): EvaluationResult {
|
||||||
|
val args = ArrayList<Any?>()
|
||||||
|
|
||||||
|
refinedEvalConfiguration[ScriptEvaluationConfiguration.constructorArgs]?.let {
|
||||||
|
args.addAll(it)
|
||||||
|
}
|
||||||
|
refinedEvalConfiguration[ScriptEvaluationConfiguration.implicitReceivers]?.let {
|
||||||
|
args.addAll(it)
|
||||||
|
}
|
||||||
|
refinedEvalConfiguration[ScriptEvaluationConfiguration.providedProperties]?.forEach {
|
||||||
|
args.add(it.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
importedScriptsEvalResults.forEach {
|
||||||
|
args.add((it.returnValue as ResultValue.Value).scriptInstance)
|
||||||
|
}
|
||||||
|
|
||||||
|
val ctor = java.constructors.single()
|
||||||
|
val instance = ctor.newInstance(*args.toArray())
|
||||||
|
|
||||||
|
return EvaluationResult(
|
||||||
|
ResultValue.Value("", instance, "", instance),
|
||||||
|
refinedEvalConfiguration
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
+1
-13
@@ -3,27 +3,15 @@
|
|||||||
* that can be found in the license/LICENSE.txt file.
|
* that can be found in the license/LICENSE.txt file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package kotlin.script.experimental.jvmhost.impl
|
package kotlin.script.experimental.jvm.impl
|
||||||
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.Serializable
|
|
||||||
import java.net.URLClassLoader
|
import java.net.URLClassLoader
|
||||||
|
|
||||||
interface KJvmCompiledModule {
|
interface KJvmCompiledModule {
|
||||||
fun createClassLoader(baseClassLoader: ClassLoader?): ClassLoader
|
fun createClassLoader(baseClassLoader: ClassLoader?): ClassLoader
|
||||||
}
|
}
|
||||||
|
|
||||||
class KJvmCompiledModuleInMemory(val compilerOutputFiles: Map<String, ByteArray>) : KJvmCompiledModule, Serializable {
|
|
||||||
|
|
||||||
override fun createClassLoader(baseClassLoader: ClassLoader?): ClassLoader =
|
|
||||||
CompiledScriptClassLoader(baseClassLoader, compilerOutputFiles)
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
@JvmStatic
|
|
||||||
private val serialVersionUID = 0L
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class KJvmCompiledModuleFromClassPath(val classpath: Collection<File>) : KJvmCompiledModule {
|
class KJvmCompiledModuleFromClassPath(val classpath: Collection<File>) : KJvmCompiledModule {
|
||||||
|
|
||||||
override fun createClassLoader(baseClassLoader: ClassLoader?): ClassLoader =
|
override fun createClassLoader(baseClassLoader: ClassLoader?): ClassLoader =
|
||||||
+129
@@ -0,0 +1,129 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||||
|
* that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package kotlin.script.experimental.jvm.impl
|
||||||
|
|
||||||
|
import java.io.ObjectInputStream
|
||||||
|
import java.io.ObjectOutputStream
|
||||||
|
import java.io.Serializable
|
||||||
|
import java.net.URLClassLoader
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
import kotlin.script.experimental.api.*
|
||||||
|
import kotlin.script.experimental.jvm.*
|
||||||
|
import kotlin.script.experimental.jvm.actualClassLoader
|
||||||
|
|
||||||
|
class KJvmCompiledScript<out ScriptBase : Any>(
|
||||||
|
sourceLocationId: String?,
|
||||||
|
compilationConfiguration: ScriptCompilationConfiguration,
|
||||||
|
private var scriptClassFQName: String,
|
||||||
|
otherScripts: List<CompiledScript<*>> = emptyList(),
|
||||||
|
var compiledModule: KJvmCompiledModule
|
||||||
|
) : CompiledScript<ScriptBase>, Serializable {
|
||||||
|
|
||||||
|
private var _sourceLocationId: String? = sourceLocationId
|
||||||
|
|
||||||
|
override val sourceLocationId: String?
|
||||||
|
get() = _sourceLocationId
|
||||||
|
|
||||||
|
private var _compilationConfiguration: ScriptCompilationConfiguration? = compilationConfiguration
|
||||||
|
|
||||||
|
override val compilationConfiguration: ScriptCompilationConfiguration
|
||||||
|
get() = _compilationConfiguration!!
|
||||||
|
|
||||||
|
private var _otherScripts: List<CompiledScript<*>> = otherScripts
|
||||||
|
|
||||||
|
override val otherScripts: List<CompiledScript<*>>
|
||||||
|
get() = _otherScripts
|
||||||
|
|
||||||
|
override suspend fun getClass(scriptEvaluationConfiguration: ScriptEvaluationConfiguration?): ResultWithDiagnostics<KClass<*>> = try {
|
||||||
|
// ensuring proper defaults are used
|
||||||
|
val actualEvaluationConfiguration = scriptEvaluationConfiguration ?: ScriptEvaluationConfiguration()
|
||||||
|
val classLoader = getOrCreateActualClassloader(actualEvaluationConfiguration)
|
||||||
|
|
||||||
|
val clazz = classLoader.loadClass(scriptClassFQName).kotlin
|
||||||
|
clazz.asSuccess()
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
ResultWithDiagnostics.Failure(
|
||||||
|
ScriptDiagnostic(
|
||||||
|
"Unable to instantiate class $scriptClassFQName",
|
||||||
|
sourcePath = sourceLocationId,
|
||||||
|
exception = e
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun writeObject(outputStream: ObjectOutputStream) {
|
||||||
|
outputStream.writeObject(compilationConfiguration)
|
||||||
|
outputStream.writeObject(sourceLocationId)
|
||||||
|
outputStream.writeObject(otherScripts)
|
||||||
|
outputStream.writeObject(compiledModule)
|
||||||
|
outputStream.writeObject(scriptClassFQName)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
private fun readObject(inputStream: ObjectInputStream) {
|
||||||
|
_compilationConfiguration = inputStream.readObject() as ScriptCompilationConfiguration
|
||||||
|
_sourceLocationId = inputStream.readObject() as String?
|
||||||
|
_otherScripts = inputStream.readObject() as List<CompiledScript<*>>
|
||||||
|
compiledModule = inputStream.readObject() as KJvmCompiledModule
|
||||||
|
scriptClassFQName = inputStream.readObject() as String
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@JvmStatic
|
||||||
|
private val serialVersionUID = 2L
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun KJvmCompiledScript<*>.getOrCreateActualClassloader(evaluationConfiguration: ScriptEvaluationConfiguration): ClassLoader =
|
||||||
|
evaluationConfiguration[ScriptEvaluationConfiguration.jvm.actualClassLoader] ?: run {
|
||||||
|
val baseClassLoader = evaluationConfiguration[ScriptEvaluationConfiguration.jvm.baseClassLoader]
|
||||||
|
val classLoaderWithDeps =
|
||||||
|
if (evaluationConfiguration[ScriptEvaluationConfiguration.jvm.loadDependencies] == false) baseClassLoader
|
||||||
|
else makeClassLoaderFromDependencies(baseClassLoader)
|
||||||
|
compiledModule.createClassLoader(classLoaderWithDeps)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getConfigurationWithClassloader(
|
||||||
|
script: CompiledScript<*>, baseConfiguration: ScriptEvaluationConfiguration
|
||||||
|
): ScriptEvaluationConfiguration =
|
||||||
|
if (baseConfiguration.containsKey(ScriptEvaluationConfiguration.jvm.actualClassLoader))
|
||||||
|
baseConfiguration
|
||||||
|
else {
|
||||||
|
val jvmScript = (script as? KJvmCompiledScript<*>)
|
||||||
|
?: throw IllegalArgumentException("Unexpected compiled script type: $script")
|
||||||
|
|
||||||
|
val classloader = jvmScript.getOrCreateActualClassloader(baseConfiguration)
|
||||||
|
|
||||||
|
ScriptEvaluationConfiguration(baseConfiguration) {
|
||||||
|
ScriptEvaluationConfiguration.jvm.actualClassLoader(classloader)
|
||||||
|
if (baseConfiguration[ScriptEvaluationConfiguration.scriptsInstancesSharing] == true) {
|
||||||
|
ScriptEvaluationConfiguration.jvm.scriptsInstancesSharingMap(mutableMapOf())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val ScriptEvaluationConfiguration.sharedScripts get() = get(ScriptEvaluationConfiguration.jvm.scriptsInstancesSharingMap)
|
||||||
|
|
||||||
|
private fun CompiledScript<*>.makeClassLoaderFromDependencies(baseClassLoader: ClassLoader?): ClassLoader? {
|
||||||
|
val processedScripts = mutableSetOf<CompiledScript<*>>()
|
||||||
|
fun seq(res: Sequence<CompiledScript<*>>, script: CompiledScript<*>): Sequence<CompiledScript<*>> {
|
||||||
|
if (processedScripts.contains(script)) return res
|
||||||
|
processedScripts.add(script)
|
||||||
|
return script.otherScripts.asSequence().fold(res + script, ::seq)
|
||||||
|
}
|
||||||
|
|
||||||
|
val dependencies = seq(emptySequence(), this).flatMap { script ->
|
||||||
|
script.compilationConfiguration[ScriptCompilationConfiguration.dependencies]
|
||||||
|
?.asSequence()
|
||||||
|
?.flatMap { dep ->
|
||||||
|
(dep as? JvmDependency)?.classpath?.asSequence()?.map { it.toURI().toURL() } ?: emptySequence()
|
||||||
|
}
|
||||||
|
?: emptySequence()
|
||||||
|
}.distinct()
|
||||||
|
// TODO: previous dependencies and classloaders should be taken into account here
|
||||||
|
return if (dependencies.none()) baseClassLoader
|
||||||
|
else URLClassLoader(dependencies.toList().toTypedArray(), baseClassLoader)
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||||
|
* that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@file:Suppress("unused")
|
||||||
|
|
||||||
|
package kotlin.script.experimental.jvm
|
||||||
|
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
import kotlin.script.experimental.api.*
|
||||||
|
import kotlin.script.experimental.util.PropertiesCollection
|
||||||
|
|
||||||
|
interface JvmScriptEvaluationConfigurationKeys
|
||||||
|
|
||||||
|
open class JvmScriptEvaluationConfigurationBuilder : PropertiesCollection.Builder(), JvmScriptEvaluationConfigurationKeys {
|
||||||
|
|
||||||
|
companion object : JvmScriptEvaluationConfigurationBuilder()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The base classloader to use for script classes loading
|
||||||
|
*/
|
||||||
|
val JvmScriptEvaluationConfigurationKeys.baseClassLoader by PropertiesCollection.key<ClassLoader?>(Thread.currentThread().contextClassLoader)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load script dependencies before evaluation, true by default
|
||||||
|
* If false, it is assumed that the all dependencies will be provided via baseClassLoader
|
||||||
|
*/
|
||||||
|
val JvmScriptEvaluationConfigurationKeys.loadDependencies by PropertiesCollection.key<Boolean>(true)
|
||||||
|
|
||||||
|
internal val JvmScriptEvaluationConfigurationKeys.actualClassLoader by PropertiesCollection.key<ClassLoader?>()
|
||||||
|
|
||||||
|
internal val JvmScriptEvaluationConfigurationKeys.scriptsInstancesSharingMap by PropertiesCollection.key<MutableMap<KClass<*>, EvaluationResult>>()
|
||||||
|
|
||||||
|
val ScriptEvaluationConfigurationKeys.jvm get() = JvmScriptEvaluationConfigurationBuilder()
|
||||||
Reference in New Issue
Block a user