Add dependency classloader to the evaluation classloader:
allows to use dependencies from classloader in the scenario with evaluation in the isolated environment
This commit is contained in:
+22
-4
@@ -8,13 +8,12 @@ package kotlin.script.experimental.jvmhost.test
|
||||
import junit.framework.TestCase
|
||||
import org.junit.Test
|
||||
import java.io.File
|
||||
import kotlin.script.experimental.api.ResultValue
|
||||
import kotlin.script.experimental.api.ScriptCompilationConfiguration
|
||||
import kotlin.script.experimental.api.dependencies
|
||||
import kotlin.script.experimental.api.valueOrThrow
|
||||
import kotlin.script.experimental.api.*
|
||||
import kotlin.script.experimental.host.toScriptSource
|
||||
import kotlin.script.experimental.jvm.JvmDependency
|
||||
import kotlin.script.experimental.jvm.JvmDependencyFromClassLoader
|
||||
import kotlin.script.experimental.jvm.baseClassLoader
|
||||
import kotlin.script.experimental.jvm.jvm
|
||||
import kotlin.script.experimental.jvmhost.BasicJvmScriptingHost
|
||||
|
||||
class ResolveFromClassloaderTest : TestCase() {
|
||||
@@ -29,6 +28,25 @@ class ResolveFromClassloaderTest : TestCase() {
|
||||
assertEquals(42, res.value)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testResolveFromClassloaderIsolated() {
|
||||
val script = "${ShouldBeVisibleFromScript::class.java.name}().x".toScriptSource()
|
||||
val compilationConfiguration = ScriptCompilationConfiguration {
|
||||
dependencies(JvmDependencyFromClassLoader { ShouldBeVisibleFromScript::class.java.classLoader })
|
||||
}
|
||||
val evaluationConfiguration = ScriptEvaluationConfiguration {
|
||||
jvm {
|
||||
baseClassLoader(null)
|
||||
}
|
||||
}
|
||||
val res = BasicJvmScriptingHost().eval(script, compilationConfiguration, evaluationConfiguration).valueOrThrow().returnValue
|
||||
when (res) {
|
||||
is ResultValue.Value -> assertEquals(42, res.value)
|
||||
is ResultValue.Error -> throw res.error
|
||||
else -> throw Exception("Unexpected evaluation result: $res")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testResolveFromClassloaderAndClassPath() {
|
||||
val script = """
|
||||
|
||||
+1
-1
@@ -28,7 +28,7 @@ class KJvmCompiledModuleFromClassLoader(val moduleClassLoader: ClassLoader) : KJ
|
||||
else DualClassLoader(moduleClassLoader, baseClassLoader)
|
||||
}
|
||||
|
||||
private class DualClassLoader(fallbackLoader: ClassLoader, parentLoader: ClassLoader?) :
|
||||
internal class DualClassLoader(fallbackLoader: ClassLoader, parentLoader: ClassLoader?) :
|
||||
ClassLoader(singleClassLoader(fallbackLoader, parentLoader) ?: parentLoader) {
|
||||
|
||||
private class Wrapper(parent: ClassLoader) : ClassLoader(parent) {
|
||||
|
||||
+13
-15
@@ -141,23 +141,21 @@ fun getConfigurationWithClassloader(
|
||||
|
||||
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)
|
||||
}
|
||||
fun recursiveScriptsSeq(res: Sequence<CompiledScript<*>>, script: CompiledScript<*>): Sequence<CompiledScript<*>> =
|
||||
if (processedScripts.add(script)) script.otherScripts.asSequence().fold(res + script, ::recursiveScriptsSeq)
|
||||
else res
|
||||
|
||||
val dependencies = seq(emptySequence(), this).flatMap { script ->
|
||||
val dependenciesWithConfigurations = recursiveScriptsSeq(emptySequence(), this).flatMap { script ->
|
||||
script.compilationConfiguration[ScriptCompilationConfiguration.dependencies]
|
||||
?.asSequence()
|
||||
?.flatMap { dep ->
|
||||
(dep as? JvmDependency)?.classpath?.asSequence()?.map { it.toURI().toURL() } ?: emptySequence()
|
||||
}
|
||||
?: emptySequence()
|
||||
}.distinct().toList()
|
||||
// TODO: previous dependencies and classloaders should be taken into account here
|
||||
return if (dependencies.isEmpty()) baseClassLoader
|
||||
else URLClassLoader(dependencies.toTypedArray(), baseClassLoader)
|
||||
?.asSequence()?.map { script.compilationConfiguration to it } ?: emptySequence()
|
||||
}
|
||||
return dependenciesWithConfigurations.fold(baseClassLoader) { parentClassLoader, (compilationConfiguration, scriptDependency) ->
|
||||
when (scriptDependency) {
|
||||
is JvmDependency -> URLClassLoader(scriptDependency.classpath.map { it.toURI().toURL() }.toTypedArray(), parentClassLoader)
|
||||
is JvmDependencyFromClassLoader -> DualClassLoader(scriptDependency.getClassLoader(compilationConfiguration), baseClassLoader)
|
||||
else -> baseClassLoader
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const val KOTLIN_SCRIPT_METADATA_PATH = "META-INF/kotlin/script"
|
||||
|
||||
Reference in New Issue
Block a user