Add improved classloader construction short circuit

In order for sbt to function well, it needs the test interface, jansi
and forked jline jars provided by a classloader that is parent to all
other sbt classloaders. To do this for just the test interface jar, I
just checked if the top loader in the app configuration had the correct
name. Now that there are three jars, this is more complicated so I
updated the launcher to create a top loader with the method getEarlyJars
implemented and returning the three needed jars. This is a much more
scalable design.

If sbt is entered with a configuration that does not have a top loader
with the getEarlyJars method defined, then we just fall back on
constructing the default layered classlaoder from the configuration
classpath.

The motivation for this change is that I discovered that sbt immediately
crashed when I tried to run a non-snapshot version. After this change, I
verified that both snapshot and non-snapshot versions of the latest sbt
code could load with both an obsolete and up-to-date launcher.
This commit is contained in:
Ethan Atkins 2020-07-08 11:15:48 -07:00
parent 6110734383
commit 464ea1b97e
1 changed files with 14 additions and 15 deletions

View File

@ -9,7 +9,7 @@ package sbt.internal
import java.io.File
import java.lang.reflect.InvocationTargetException
import java.net.{ URL, URLClassLoader }
import java.net.URL
import java.util.concurrent.{ ExecutorService, Executors }
import ClassLoaderClose.close
@ -60,21 +60,20 @@ private[sbt] class XMainConfiguration {
def run(moduleName: String, configuration: xsbti.AppConfiguration): xsbti.MainResult = {
val topLoader = configuration.provider.scalaProvider.launcher.topLoader
val updatedConfiguration =
if (topLoader.getClass.getCanonicalName.contains("TestInterfaceLoader")) {
topLoader match {
case u: URLClassLoader =>
val urls = u.getURLs
var i = 0
while (i < urls.length && i >= 0) {
if (urls(i).toString.contains("jline")) i = -2
else i += 1
}
if (i < 0) configuration
else makeConfiguration(configuration)
case _ => configuration
try {
val method = topLoader.getClass.getMethod("getEarlyJars")
val jars = method.invoke(topLoader).asInstanceOf[Array[URL]]
var canReuseConfiguration = jars.length == 3
var j = 0
while (j < jars.length && canReuseConfiguration) {
val s = jars(j).toString
canReuseConfiguration =
s.contains("jline") || s.contains("test-interface") || s.contains("jansi")
j += 1
}
} else {
makeConfiguration(configuration)
if (canReuseConfiguration) configuration else makeConfiguration(configuration)
} catch {
case _: NoSuchMethodException => makeConfiguration(configuration)
}
val loader = updatedConfiguration.provider.loader
Thread.currentThread.setContextClassLoader(loader)