Skip to content

Commit 079ddf7

Browse files
committed
refactor class loader extensions
1 parent 5587767 commit 079ddf7

File tree

5 files changed

+50
-60
lines changed

5 files changed

+50
-60
lines changed

compiler/src/dotty/tools/MainGenericRunner.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ object MainGenericRunner {
170170
val newSettings = if arg.startsWith("-") then settings else settings.withPossibleEntryPaths(arg).withModeShouldBePossibleRun
171171
process(tail, newSettings.withResidualArgs(arg))
172172
end process
173-
173+
174174
def main(args: Array[String]): Unit =
175175
val scalaOpts = envOrNone("SCALA_OPTS").toArray.flatMap(_.split(" ")).filter(_.nonEmpty)
176176
val allArgs = scalaOpts ++ args
@@ -193,7 +193,7 @@ object MainGenericRunner {
193193

194194
case ExecuteMode.PossibleRun =>
195195
val newClasspath = (settings.classPath :+ ".").flatMap(_.split(classpathSeparator).filter(_.nonEmpty)).map(File(_).toURI.toURL)
196-
import dotty.tools.runner.RichClassLoader._
196+
import dotty.tools.runner.ClassLoaderOps._
197197
val newClassLoader = ScalaClassLoader.fromURLsParallelCapable(newClasspath)
198198
val targetToRun = settings.possibleEntryPaths.to(LazyList).find { entryPath =>
199199
newClassLoader.tryToLoadClass(entryPath).orElse {

compiler/src/dotty/tools/repl/ReplDriver.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import dotty.tools.dotc.util.{SourceFile, SourcePosition}
3131
import dotty.tools.dotc.{CompilationUnit, Driver}
3232
import dotty.tools.dotc.config.CompilerCommand
3333
import dotty.tools.io._
34-
import dotty.tools.runner.ScalaClassLoader.*
34+
import dotty.tools.runner.ClassLoaderOps.*
3535
import org.jline.reader._
3636

3737
import scala.annotation.tailrec

compiler/src/dotty/tools/runner/ObjectRunner.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ trait CommonRunner {
1919
* @throws java.lang.reflect.InvocationTargetException
2020
*/
2121
def run(urls: Seq[URL], objectName: String, arguments: Seq[String]): Unit = {
22-
import RichClassLoader._
23-
ScalaClassLoader.fromURLsParallelCapable(urls).run(objectName, arguments)
22+
import ClassLoaderOps._
23+
ScalaClassLoader.fromURLsParallelCapable(urls).runMain(objectName, arguments)
2424
}
2525

2626
/** Catches any non-fatal exception thrown by run (in the case of InvocationTargetException,

compiler/src/dotty/tools/runner/ScalaClassLoader.scala

Lines changed: 43 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -5,62 +5,61 @@ import scala.language.unsafeNulls
55

66
import java.lang.ClassLoader
77
import java.lang.invoke.{MethodHandles, MethodType}
8-
import java.lang.reflect.Modifier
9-
import java.net.{ URL, URLClassLoader }
10-
import java.lang.reflect.{ InvocationTargetException, UndeclaredThrowableException }
8+
import java.lang.reflect.{Modifier, InvocationTargetException, UndeclaredThrowableException}
9+
import java.net.{URL, URLClassLoader}
1110

1211
import scala.annotation.internal.sharable
1312
import scala.annotation.tailrec
1413
import scala.util.control.Exception.catching
1514

16-
final class RichClassLoader(private val self: ClassLoader) extends AnyVal {
17-
/** Execute an action with this classloader as context classloader. */
18-
private def asContext[T](action: => T): T = ScalaClassLoader.asContext(self)(action)
15+
object ClassLoaderOps:
16+
private def setContext(cl: ClassLoader) = Thread.currentThread.setContextClassLoader(cl)
1917

20-
/** Load and link a class with this classloader */
21-
def tryToLoadClass[T <: AnyRef](path: String): Option[Class[T]] = tryClass(path, initialize = false)
22-
23-
/** Load, link and initialize a class with this classloader */
24-
def tryToInitializeClass[T <: AnyRef](path: String): Option[Class[T]] = tryClass(path, initialize = true)
18+
extension (self: ClassLoader)
19+
/** Execute an action with this classloader as context classloader. */
20+
def asContext[T](action: => T): T =
21+
val saved = Thread.currentThread.getContextClassLoader
22+
try
23+
setContext(self)
24+
action
25+
finally setContext(saved)
2526

26-
private def tryClass[T <: AnyRef](path: String, initialize: Boolean): Option[Class[T]] =
27-
catching(classOf[ClassNotFoundException], classOf[SecurityException]) opt
28-
Class.forName(path, initialize, self).asInstanceOf[Class[T]]
27+
/** Load and link a class with this classloader */
28+
def tryToLoadClass[T <: AnyRef](path: String): Option[Class[T]] = tryClass(path, initialize = false)
2929

30-
/** Run the main method of a class to be loaded by this classloader */
31-
def run(objectName: String, arguments: Seq[String]): Unit = {
32-
val clsToRun = tryToInitializeClass(objectName).getOrElse(throw new ClassNotFoundException(objectName))
33-
val method = clsToRun.getMethod("main", classOf[Array[String]])
34-
if !Modifier.isStatic(method.getModifiers) then
35-
throw new NoSuchMethodException(s"$objectName.main is not static")
36-
try asContext(method.invoke(null, Array(arguments.toArray: AnyRef): _*))
37-
catch unwrapHandler({ case ex => throw ex })
38-
}
30+
/** Load, link and initialize a class with this classloader */
31+
def tryToInitializeClass[T <: AnyRef](path: String): Option[Class[T]] = tryClass(path, initialize = true)
3932

40-
@tailrec private def unwrapThrowable(x: Throwable): Throwable = x match {
41-
case _: InvocationTargetException | // thrown by reflectively invoked method or constructor
42-
_: ExceptionInInitializerError | // thrown when running a static initializer (e.g. a scala module constructor)
43-
_: UndeclaredThrowableException | // invocation on a proxy instance if its invocation handler's `invoke` throws an exception
44-
_: ClassNotFoundException | // no definition for a class instantiated by name
45-
_: NoClassDefFoundError // the definition existed when the executing class was compiled, but can no longer be found
46-
if x.getCause != null =>
47-
unwrapThrowable(x.getCause)
48-
case _ => x
49-
}
33+
private def tryClass[T <: AnyRef](path: String, initialize: Boolean): Option[Class[T]] =
34+
catching(classOf[ClassNotFoundException], classOf[SecurityException]) opt
35+
Class.forName(path, initialize, self).asInstanceOf[Class[T]]
5036

51-
// Transforms an exception handler into one which will only receive the unwrapped
52-
// exceptions (for the values of wrap covered in unwrapThrowable.)
53-
private def unwrapHandler[T](pf: PartialFunction[Throwable, T]): PartialFunction[Throwable, T] =
54-
pf.compose({ case ex => unwrapThrowable(ex) })
55-
}
37+
/** Run the main method of a class to be loaded by this classloader */
38+
def runMain(objectName: String, arguments: Seq[String]): Unit =
39+
val clsToRun = tryToInitializeClass(objectName).getOrElse(throw ClassNotFoundException(objectName))
40+
val method = clsToRun.getMethod("main", classOf[Array[String]])
41+
if !Modifier.isStatic(method.getModifiers) then
42+
throw NoSuchMethodException(s"$objectName.main is not static")
43+
try asContext(method.invoke(null, Array(arguments.toArray: AnyRef)*))
44+
catch unwrapHandler({ case ex => throw ex })
5645

57-
object RichClassLoader {
58-
implicit def wrapClassLoader(loader: ClassLoader): RichClassLoader = new RichClassLoader(loader)
59-
}
46+
@tailrec private def unwrapThrowable(x: Throwable): Throwable = x match
47+
case _: InvocationTargetException | // thrown by reflectively invoked method or constructor
48+
_: ExceptionInInitializerError | // thrown when running a static initializer (e.g. a scala module constructor)
49+
_: UndeclaredThrowableException | // invocation on a proxy instance if its invocation handler's `invoke` throws an exception
50+
_: ClassNotFoundException | // no definition for a class instantiated by name
51+
_: NoClassDefFoundError // the definition existed when the executing class was compiled, but can no longer be found
52+
if x.getCause != null =>
53+
unwrapThrowable(x.getCause)
54+
case _ => x
6055

61-
object ScalaClassLoader {
62-
def setContext(cl: ClassLoader) = Thread.currentThread.setContextClassLoader(cl)
56+
// Transforms an exception handler into one which will only receive the unwrapped
57+
// exceptions (for the values of wrap covered in unwrapThrowable.)
58+
private def unwrapHandler[T](pf: PartialFunction[Throwable, T]): PartialFunction[Throwable, T] =
59+
pf.compose({ case ex => unwrapThrowable(ex) })
60+
end ClassLoaderOps
6361

62+
object ScalaClassLoader:
6463
def fromURLsParallelCapable(urls: Seq[URL], parent: ClassLoader | Null = null): URLClassLoader =
6564
new URLClassLoader(urls.toArray, if parent == null then bootClassLoader else parent)
6665

@@ -70,13 +69,4 @@ object ScalaClassLoader {
7069
MethodHandles.lookup().findStatic(classOf[ClassLoader], "getPlatformClassLoader", MethodType.methodType(classOf[ClassLoader])).invoke().asInstanceOf[ClassLoader]
7170
catch case _: Throwable => null
7271
else null
73-
74-
extension (classLoader: ClassLoader)
75-
/** Execute an action with this classloader as context classloader. */
76-
def asContext[T](action: => T): T =
77-
val saved = Thread.currentThread.getContextClassLoader
78-
try
79-
setContext(classLoader)
80-
action
81-
finally setContext(saved)
82-
}
72+
end ScalaClassLoader

tests/run-with-compiler/i14541.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11

22
// test argument processing and "execution mode" in runner
33
object Test:
4-
import dotty.tools.runner.RichClassLoader.*
4+
import dotty.tools.runner.ClassLoaderOps.*
55
val classpath = dotty.tools.dotc.util.ClasspathFromClassloader(getClass.getClassLoader)
66
def main(args: Array[String]): Unit =
7-
getClass.getClassLoader.run("echo", List("hello", "raw", "world"))
7+
getClass.getClassLoader.runMain("echo", List("hello", "raw", "world"))
88
// caution: uses "SCALA_OPTS"
99
dotty.tools.MainGenericRunner.main(Array("--class-path", classpath, "echo", "hello", "run", "world"))
1010

0 commit comments

Comments
 (0)