Skip to content

Commit

Permalink
Fix com-lihaoyi#370 Use ExtClassLoader instead of null as parent
Browse files Browse the repository at this point in the history
  • Loading branch information
lolgab committed Apr 23, 2022
1 parent 319d193 commit dbe464e
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 2 deletions.
17 changes: 15 additions & 2 deletions main/api/src/mill/api/ClassLoader.scala
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ object ClassLoader {
* mill could be compiled only with jdk 9 or above. We don't want to introduce this restriction now.
*/
private def refinePlatformParent(parent: java.lang.ClassLoader): ClassLoader = {
if (!java9OrAbove || parent != null) parent
else {
if (parent != null) parent
else if (java9OrAbove) {
// Make sure when `parent == null`, we only delegate java.* classes
// to the parent getPlatformClassLoader. This is necessary because
// in Java 9+, somehow the getPlatformClassLoader ends up with all
Expand All @@ -69,6 +69,19 @@ object ClassLoader {
.getMethod("getPlatformClassLoader")
.invoke(null)
.asInstanceOf[ClassLoader]
} else {
// With Java 8 we want a clean classloader that still contains classes
// coming from com.sun.* etc.
// We get the application classloader parent which happens to be of
// type sun.misc.Launcher$ExtClassLoader
// We can't call the method directly since it doesn't exist in Java 9
// So we load it via reflection.
val launcherClass = getClass.getClassLoader().loadClass("sun.misc.Launcher")
val getLauncherMethod = launcherClass.getMethod("getLauncher")
val launcher = getLauncherMethod.invoke(null)
val getClassLoaderMethod = launcher.getClass().getMethod("getClassLoader")
val appClassLoader = getClassLoaderMethod.invoke(launcher).asInstanceOf[ClassLoader]
appClassLoader.getParent()
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package mill.scalalib

import utest._

object ClassLoaderTests extends TestSuite {
try {
getClass().getClassLoader().loadClass("com.sun.nio.zipfs.ZipFileSystemProvider")
} catch {
case _: ClassNotFoundException if !System.getProperty("java.specification.version").startsWith("1.") =>
// Don't fail on Java 9+
}
val tests = Tests {}
}
50 changes: 50 additions & 0 deletions scalalib/test/src/TestClassLoaderTests.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package mill.scalalib

import mill.{Agg, T}

import scala.util.Success
import mill.testrunner.TestRunner.TestArgs
import mill.util.{TestEvaluator, TestUtil}
import org.scalacheck.Prop.forAll
import utest._
import utest.framework.TestPath

object TestClassLoaderTests extends TestSuite {
object testclassloader extends TestUtil.BaseModule with ScalaModule {
override def millSourcePath = TestUtil.getSrcPathBase() / millOuterCtx.enclosing.split('.')

def scalaVersion = sys.props.getOrElse("TEST_SCALA_2_12_VERSION", ???)

object test extends super.Tests with TestModule.Utest {
override def ivyDeps = T {
super.ivyDeps() ++ Agg(
ivy"com.lihaoyi::utest:${sys.props.getOrElse("TEST_UTEST_VERSION", ???)}"
)
}
}
}

val resourcePath = os.pwd / "scalalib" / "test" / "resources" / "classloader-test"

def workspaceTest[T](
m: TestUtil.BaseModule,
resourcePath: os.Path = resourcePath
)(t: TestEvaluator => T)(
implicit tp: TestPath
): T = {
val eval = new TestEvaluator(m)
os.remove.all(m.millSourcePath)
os.remove.all(eval.outPath)
os.makeDir.all(m.millSourcePath / os.up)
os.copy(resourcePath, m.millSourcePath)
t(eval)
}

override def tests: Tests = Tests {
test("classloader can load com.sun.* on Java 8") {
workspaceTest(testclassloader) { eval =>
assert(eval.apply(testclassloader.test.test()).isRight)
}
}
}
}

0 comments on commit dbe464e

Please sign in to comment.