From 464ea1b97e2408767a333aabd5f440109c07f5d6 Mon Sep 17 00:00:00 2001 From: Ethan Atkins Date: Wed, 8 Jul 2020 11:15:48 -0700 Subject: [PATCH] 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. --- .../sbt/internal/XMainConfiguration.scala | 29 +++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/main/src/main/scala/sbt/internal/XMainConfiguration.scala b/main/src/main/scala/sbt/internal/XMainConfiguration.scala index d83826c37..1e72eb139 100644 --- a/main/src/main/scala/sbt/internal/XMainConfiguration.scala +++ b/main/src/main/scala/sbt/internal/XMainConfiguration.scala @@ -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)