Deduplicating classloaders and classpath entries on constructing script classloader

This commit is contained in:
Ilya Chernikov
2019-09-12 15:10:21 +02:00
parent df3fbaf417
commit dd953e0f66
2 changed files with 30 additions and 6 deletions
@@ -31,7 +31,7 @@ class KJvmCompiledModuleFromClassLoader(val moduleClassLoader: ClassLoader) : KJ
internal class DualClassLoader(fallbackLoader: ClassLoader, parentLoader: ClassLoader?) :
ClassLoader(singleClassLoader(fallbackLoader, parentLoader) ?: parentLoader) {
private class Wrapper(parent: ClassLoader) : ClassLoader(parent) {
internal class Wrapper(parent: ClassLoader) : ClassLoader(parent) {
fun openFindResources(name: String): Enumeration<URL> = super.findResources(name)
fun openFindResource(name: String): URL? = super.findResource(name)
}
@@ -53,7 +53,7 @@ internal class DualClassLoader(fallbackLoader: ClassLoader, parentLoader: ClassL
}
}
private val fallbackClassLoader: Wrapper? =
internal val fallbackClassLoader: Wrapper? =
if (/* optimization */ parentLoader == null || singleClassLoader(fallbackLoader, parentLoader) != null) null
else Wrapper(fallbackLoader)
@@ -6,6 +6,7 @@
package kotlin.script.experimental.jvm.impl
import java.io.*
import java.net.URL
import java.net.URLClassLoader
import kotlin.reflect.KClass
import kotlin.script.experimental.api.*
@@ -149,12 +150,35 @@ private fun CompiledScript<*>.makeClassLoaderFromDependencies(baseClassLoader: C
script.compilationConfiguration[ScriptCompilationConfiguration.dependencies]
?.asSequence()?.map { script.compilationConfiguration to it } ?: emptySequence()
}
val processedClasspathElements = mutableSetOf<URL>()
fun recursiveClassPath(res: Sequence<URL>, classLoader: ClassLoader?): Sequence<URL> =
when (classLoader) {
null -> res
is DualClassLoader -> recursiveClassPath(res, classLoader.parent) +
recursiveClassPath(emptySequence(), classLoader.fallbackClassLoader)
is URLClassLoader -> recursiveClassPath(res + classLoader.urLs, classLoader.parent)
else -> recursiveClassPath(res, classLoader.parent)
}
recursiveClassPath(emptySequence(), baseClassLoader).forEach { processedClasspathElements.add(it) }
val processedClassloaders = mutableSetOf<ClassLoader>()
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
}
is JvmDependency -> {
scriptDependency.classpath.mapNotNull {
val url = it.toURI().toURL()
if (processedClasspathElements.add(url)) url else null
}.takeUnless { it.isEmpty() }?.let { URLClassLoader(it.toTypedArray(), parentClassLoader) }
}
is JvmDependencyFromClassLoader -> {
val dependenciesClassLoader = scriptDependency.getClassLoader(compilationConfiguration)
if (processedClassloaders.add(dependenciesClassLoader)) DualClassLoader(dependenciesClassLoader, parentClassLoader)
else null
}
else -> null
} ?: parentClassLoader
}
}