From f9eb631b13fdaa7ce08347e0f4f71bea9980fafa Mon Sep 17 00:00:00 2001 From: Ethan Atkins Date: Sat, 11 May 2019 18:44:05 -0700 Subject: [PATCH] Filter scala-library more safely I previously tried to fix https://github.com/sbt/sbt/issues/4608 in fc715cab44b4d68fb36710d0823bdb6d73364cc2 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. --- .../scala/sbt/internal/ClassLoaders.scala | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/main/src/main/scala/sbt/internal/ClassLoaders.scala b/main/src/main/scala/sbt/internal/ClassLoaders.scala index 89e24227c..0222c1da5 100644 --- a/main/src/main/scala/sbt/internal/ClassLoaders.scala +++ b/main/src/main/scala/sbt/internal/ClassLoaders.scala @@ -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) {