Skip to content

Commit 8d28d94

Browse files
Merge pull request #14261 from dwijnand/trim-runner
Trim RichClassLoader and ScalaClassLoader
2 parents b460782 + 2c19c14 commit 8d28d94

File tree

1 file changed

+24
-189
lines changed

1 file changed

+24
-189
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,103 +1,47 @@
11
package dotty.tools
22
package runner
33

4-
import java.lang.invoke.{MethodHandles, MethodType}
5-
64
import scala.language.implicitConversions
7-
import java.lang.{ClassLoader => JClassLoader}
5+
6+
import java.lang.ClassLoader
7+
import java.lang.invoke.{MethodHandles, MethodType}
88
import java.lang.reflect.Modifier
9-
import java.net.{URLClassLoader => JURLClassLoader}
10-
import java.net.URL
9+
import java.net.{ URL, URLClassLoader }
10+
import java.lang.reflect.{ InvocationTargetException, UndeclaredThrowableException }
1111

12+
import scala.annotation.internal.sharable
1213
import scala.annotation.tailrec
1314
import scala.util.control.Exception.catching
14-
import scala.reflect.{ClassTag, classTag}
15-
import java.lang.reflect.InvocationTargetException
16-
import java.lang.reflect.UndeclaredThrowableException
17-
import dotty.tools.repl.AbstractFileClassLoader
18-
import dotty.tools.io.AbstractFile
19-
import dotty.tools.io.Streamable
20-
import scala.annotation.internal.sharable
2115

22-
trait HasClassPath {
23-
def classPathURLs: Seq[URL]
24-
}
25-
26-
final class RichClassLoader(private val self: JClassLoader) extends AnyVal {
16+
final class RichClassLoader(private val self: ClassLoader) extends AnyVal {
2717
/** Executing an action with this classloader as context classloader */
28-
def asContext[T](action: => T): T = {
18+
private def asContext[T](action: => T): T = {
2919
val saved = Thread.currentThread.getContextClassLoader
3020
try { ScalaClassLoader.setContext(self) ; action }
3121
finally ScalaClassLoader.setContext(saved)
3222
}
3323

3424
/** Load and link a class with this classloader */
3525
def tryToLoadClass[T <: AnyRef](path: String): Option[Class[T]] = tryClass(path, initialize = false)
26+
3627
/** Load, link and initialize a class with this classloader */
3728
def tryToInitializeClass[T <: AnyRef](path: String): Option[Class[T]] = tryClass(path, initialize = true)
3829

3930
private def tryClass[T <: AnyRef](path: String, initialize: Boolean): Option[Class[T]] =
4031
catching(classOf[ClassNotFoundException], classOf[SecurityException]) opt
4132
Class.forName(path, initialize, self).asInstanceOf[Class[T]]
4233

43-
/** Create an instance of a class with this classloader */
44-
def create(path: String): AnyRef =
45-
tryToInitializeClass[AnyRef](path).map(_.getConstructor().newInstance()).orNull
46-
47-
/** Create an instance with ctor args, or invoke errorFn before throwing. */
48-
def create[T <: AnyRef : ClassTag](path: String, errorFn: String => Unit)(args: AnyRef*): T = {
49-
def fail(msg: String) = error(msg, new IllegalArgumentException(msg))
50-
def error(msg: String, e: Throwable) = { errorFn(msg) ; throw e }
51-
try {
52-
val clazz = Class.forName(path, /*initialize =*/ true, /*loader =*/ self)
53-
if (classTag[T].runtimeClass isAssignableFrom clazz) {
54-
val ctor = {
55-
val maybes = clazz.getConstructors filter (c => c.getParameterCount == args.size &&
56-
(c.getParameterTypes zip args).forall { case (k, a) => k isAssignableFrom a.getClass })
57-
if (maybes.size == 1) maybes.head
58-
else fail(s"Constructor must accept arg list (${args map (_.getClass.getName) mkString ", "}): ${path}")
59-
}
60-
(ctor.newInstance(args: _*)).asInstanceOf[T]
61-
} else {
62-
errorFn(s"""Loader for ${classTag[T]}: [${show(classTag[T].runtimeClass.getClassLoader)}]
63-
|Loader for ${clazz.getName}: [${show(clazz.getClassLoader)}]""".stripMargin)
64-
fail(s"Not a ${classTag[T]}: ${path}")
65-
}
66-
} catch {
67-
case e: ClassNotFoundException =>
68-
error(s"Class not found: ${path}", e)
69-
case e @ (_: LinkageError | _: ReflectiveOperationException) =>
70-
error(s"Unable to create instance: ${path}: ${e.toString}", e)
71-
}
72-
}
73-
74-
/** The actual bytes for a class file, or an empty array if it can't be found. */
75-
def classBytes(className: String): Array[Byte] = classAsStream(className) match {
76-
case null => Array()
77-
case stream => Streamable.bytes(stream)
78-
}
79-
80-
/** An InputStream representing the given class name, or null if not found. */
81-
def classAsStream(className: String) = self.getResourceAsStream {
82-
if (className endsWith ".class") className
83-
else s"${className.replace('.', '/')}.class" // classNameToPath
84-
}
85-
8634
/** Run the main method of a class to be loaded by this classloader */
8735
def run(objectName: String, arguments: Seq[String]): Unit = {
88-
val clsToRun = tryToInitializeClass(objectName) getOrElse (
89-
throw new ClassNotFoundException(objectName)
90-
)
36+
val clsToRun = tryToInitializeClass(objectName).getOrElse(throw new ClassNotFoundException(objectName))
9137
val method = clsToRun.getMethod("main", classOf[Array[String]])
92-
if (!Modifier.isStatic(method.getModifiers))
93-
throw new NoSuchMethodException(objectName + ".main is not static")
94-
95-
try asContext(method.invoke(null, Array(arguments.toArray: AnyRef): _*)) // !!! : AnyRef shouldn't be necessary
38+
if !Modifier.isStatic(method.getModifiers) then
39+
throw new NoSuchMethodException(s"$objectName.main is not static")
40+
try asContext(method.invoke(null, Array(arguments.toArray: AnyRef): _*))
9641
catch unwrapHandler({ case ex => throw ex })
9742
}
9843

99-
@tailrec
100-
def unwrapThrowable(x: Throwable): Throwable = x match {
44+
@tailrec private def unwrapThrowable(x: Throwable): Throwable = x match {
10145
case _: InvocationTargetException | // thrown by reflectively invoked method or constructor
10246
_: ExceptionInInitializerError | // thrown when running a static initializer (e.g. a scala module constructor)
10347
_: UndeclaredThrowableException | // invocation on a proxy instance if its invocation handler's `invoke` throws an exception
@@ -107,136 +51,27 @@ final class RichClassLoader(private val self: JClassLoader) extends AnyVal {
10751
unwrapThrowable(x.getCause)
10852
case _ => x
10953
}
54+
11055
// Transforms an exception handler into one which will only receive the unwrapped
11156
// exceptions (for the values of wrap covered in unwrapThrowable.)
112-
def unwrapHandler[T](pf: PartialFunction[Throwable, T]): PartialFunction[Throwable, T] =
57+
private def unwrapHandler[T](pf: PartialFunction[Throwable, T]): PartialFunction[Throwable, T] =
11358
pf.compose({ case ex => unwrapThrowable(ex) })
114-
115-
def show(cl: ClassLoader): String = {
116-
import scala.reflect.Selectable.reflectiveSelectable
117-
118-
@tailrec
119-
def isAbstractFileClassLoader(clazz: Class[_]): Boolean = {
120-
if (clazz == null) return false
121-
if (clazz == classOf[AbstractFileClassLoader]) return true
122-
isAbstractFileClassLoader(clazz.getSuperclass)
123-
}
124-
def inferClasspath(cl: ClassLoader): String = cl match {
125-
case cl: java.net.URLClassLoader if cl.getURLs != null =>
126-
(cl.getURLs mkString ",")
127-
case cl if cl != null && isAbstractFileClassLoader(cl.getClass) =>
128-
cl.asInstanceOf[{val root: AbstractFile}].root.canonicalPath
129-
case null =>
130-
val loadBootCp = (flavor: String) => scala.util.Properties.propOrNone(flavor + ".boot.class.path")
131-
loadBootCp("sun") orElse loadBootCp("java") getOrElse "<unknown>"
132-
case _ =>
133-
"<unknown>"
134-
}
135-
cl match {
136-
case null => s"primordial classloader with boot classpath [${inferClasspath(cl)}]"
137-
case _ => s"$cl of type ${cl.getClass} with classpath [${inferClasspath(cl)}] and parent being ${show(cl.getParent)}"
138-
}
139-
}
14059
}
14160

14261
object RichClassLoader {
14362
implicit def wrapClassLoader(loader: ClassLoader): RichClassLoader = new RichClassLoader(loader)
14463
}
14564

146-
/** A wrapper around java.lang.ClassLoader to lower the annoyance
147-
* of java reflection.
148-
*/
149-
trait ScalaClassLoader extends JClassLoader {
150-
private def wrap = new RichClassLoader(this)
151-
/** Executing an action with this classloader as context classloader */
152-
def asContext[T](action: => T): T = wrap.asContext(action)
153-
154-
/** Load and link a class with this classloader */
155-
def tryToLoadClass[T <: AnyRef](path: String): Option[Class[T]] = wrap.tryToLoadClass[T](path)
156-
/** Load, link and initialize a class with this classloader */
157-
def tryToInitializeClass[T <: AnyRef](path: String): Option[Class[T]] = wrap.tryToInitializeClass(path)
158-
159-
/** Create an instance of a class with this classloader */
160-
def create(path: String): AnyRef = wrap.create(path)
161-
162-
/** Create an instance with ctor args, or invoke errorFn before throwing. */
163-
def create[T <: AnyRef : ClassTag](path: String, errorFn: String => Unit)(args: AnyRef*): T =
164-
wrap.create[T](path, errorFn)(args: _*)
165-
166-
/** The actual bytes for a class file, or an empty array if it can't be found. */
167-
def classBytes(className: String): Array[Byte] = wrap.classBytes(className)
168-
169-
/** An InputStream representing the given class name, or null if not found. */
170-
def classAsStream(className: String) = wrap.classAsStream(className)
171-
172-
/** Run the main method of a class to be loaded by this classloader */
173-
def run(objectName: String, arguments: Seq[String]): Unit = wrap.run(objectName, arguments)
174-
}
175-
176-
177-
/** Methods for obtaining various classloaders.
178-
* appLoader: the application classloader. (Also called the java system classloader.)
179-
* extLoader: the extension classloader.
180-
* bootLoader: the boot classloader.
181-
* contextLoader: the context classloader.
182-
*/
18365
object ScalaClassLoader {
184-
/** Returns loaders which are already ScalaClassLoaders unaltered,
185-
* and translates java.net.URLClassLoaders into scala URLClassLoaders.
186-
* Otherwise creates a new wrapper.
187-
*/
188-
implicit def apply(cl: JClassLoader): ScalaClassLoader = cl match {
189-
case cl: ScalaClassLoader => cl
190-
case cl: JURLClassLoader => new URLClassLoader(cl.getURLs.toSeq, cl.getParent)
191-
case _ => new JClassLoader(cl) with ScalaClassLoader
192-
}
193-
def contextLoader = apply(Thread.currentThread.getContextClassLoader)
194-
def appLoader = apply(JClassLoader.getSystemClassLoader)
195-
def setContext(cl: JClassLoader) = Thread.currentThread.setContextClassLoader(cl)
196-
197-
class URLClassLoader(urls: Seq[URL], parent: JClassLoader)
198-
extends JURLClassLoader(urls.toArray, parent)
199-
with ScalaClassLoader
200-
with HasClassPath {
201-
private[this] var classloaderURLs: Seq[URL] = urls
202-
def classPathURLs: Seq[URL] = classloaderURLs
66+
def setContext(cl: ClassLoader) = Thread.currentThread.setContextClassLoader(cl)
20367

204-
/** Override to widen to public */
205-
override def addURL(url: URL) = {
206-
classloaderURLs :+= url
207-
super.addURL(url)
208-
}
209-
override def close(): Unit = {
210-
super.close()
211-
classloaderURLs = null
212-
}
213-
}
214-
215-
def fromURLs(urls: Seq[URL], parent: ClassLoader = null): URLClassLoader = {
216-
new URLClassLoader(urls, if (parent == null) bootClassLoader else parent)
217-
}
68+
def fromURLsParallelCapable(urls: Seq[URL], parent: ClassLoader = null): URLClassLoader =
69+
new URLClassLoader(urls.toArray, if parent == null then bootClassLoader else parent)
21870

219-
def fromURLsParallelCapable(urls: Seq[URL], parent: ClassLoader = null): JURLClassLoader = {
220-
new JURLClassLoader(urls.toArray, if (parent == null) bootClassLoader else parent)
221-
}
222-
223-
/** True if supplied class exists in supplied path */
224-
def classExists(urls: Seq[URL], name: String): Boolean =
225-
(fromURLs(urls) tryToLoadClass name).isDefined
226-
227-
/** Finding what jar a clazz or instance came from */
228-
def originOfClass(x: Class[_]): Option[URL] =
229-
Option(x.getProtectionDomain.getCodeSource) flatMap (x => Option(x.getLocation))
230-
231-
@sharable private[this] val bootClassLoader: ClassLoader = {
232-
if (!scala.util.Properties.isJavaAtLeast("9")) null
233-
else {
234-
try {
71+
@sharable private[this] val bootClassLoader: ClassLoader =
72+
if scala.util.Properties.isJavaAtLeast("9") then
73+
try
23574
MethodHandles.lookup().findStatic(classOf[ClassLoader], "getPlatformClassLoader", MethodType.methodType(classOf[ClassLoader])).invoke().asInstanceOf[ClassLoader]
236-
} catch {
237-
case _: Throwable =>
238-
null
239-
}
240-
}
241-
}
75+
catch case _: Throwable => null
76+
else null
24277
}

0 commit comments

Comments
 (0)