Filter scala-library more safely

I previously tried to fix https://github.com/sbt/sbt/issues/4608 in
fc715cab44 by finding the instance of
xsbt.boot.BootFilteredLoader in the classloader heirarchy. This was a
risky approach since it made a lot of assumptions about the classloaders
used to invoke xMain.run. Since the point is to filter out the scala
standard library jar, I reworked things to just find all the parents of
the scala provider loader and then walk the graph from the root
classloader until it finds the classloader that contains the scala
library. If no such classloader exists, it ends up returning the parent
of the scala provider library.

I also renamed the libraryLoader parameter to scalaProviderLoader since
that is what is actually passedin. It is actually the libraryLoader that
we want to exclude.
This commit is contained in:
Ethan Atkins 2019-05-11 18:44:05 -07:00
parent cf932c8a13
commit f9eb631b13
1 changed files with 18 additions and 8 deletions

View File

@ -209,19 +209,29 @@ private[sbt] object SbtMetaBuildClassLoader {
throw new IllegalStateException(s"sbt was launched with a non URLClassLoader: $cl")
}
}
def apply(libraryLoader: ClassLoader, fullLoader: ClassLoader): ClassLoader = {
@tailrec
def bootLoader(classLoader: ClassLoader): ClassLoader = classLoader.getParent match {
case null => classLoader
case c if c.getClass.getCanonicalName == "xsbt.boot.BootFilteredLoader" => c
case c => bootLoader(c)
def apply(scalaProviderLoader: ClassLoader, fullLoader: ClassLoader): ClassLoader = {
val libFilter: URL => Boolean = _.getFile.endsWith("scala-library.jar")
def bootLoader(loader: ClassLoader): ClassLoader = {
@tailrec def getAllParents(
classLoader: ClassLoader,
parents: List[ClassLoader]
): List[ClassLoader] = classLoader.getParent match {
case null => parents
case cl => getAllParents(cl, classLoader :: parents)
}
@tailrec def getLoader(remaining: List[ClassLoader]): ClassLoader = remaining match {
case head :: (next: URLClassLoader) :: _ if next.getURLs.exists(libFilter) => head
case head :: Nil => head
case _ :: tail => getLoader(tail)
}
getLoader(getAllParents(loader, Nil))
}
val interfaceFilter: URL => Boolean = _.getFile.endsWith("test-interface-1.0.jar")
val (interfaceURL, rest) = fullLoader.urls.partition(interfaceFilter)
val interfaceLoader = new URLClassLoader(interfaceURL, bootLoader(libraryLoader)) {
val interfaceLoader = new URLClassLoader(interfaceURL, bootLoader(scalaProviderLoader)) {
override def toString: String = s"SbtTestInterfaceClassLoader(${getURLs.head})"
}
val updatedLibraryLoader = new URLClassLoader(libraryLoader.urls, interfaceLoader) {
val updatedLibraryLoader = new URLClassLoader(scalaProviderLoader.urls, interfaceLoader) {
override def toString: String = s"ScalaClassLoader(jars = {${getURLs.mkString(", ")}}"
}
new URLClassLoader(rest, updatedLibraryLoader) {