|
| 1 | +package dotty.tools.repl |
| 2 | + |
| 3 | +import scala.reflect.{ ClassTag, classTag } |
| 4 | +import scala.util.control.Exception.catching |
| 5 | + |
| 6 | +object ClassLoaderOps: |
| 7 | + extension (cl: ClassLoader) |
| 8 | + /** Create an instance with ctor args, or invoke errorFn before throwing. */ |
| 9 | + def createInstance[T <: AnyRef : ClassTag](path: String, errorFn: String => Unit)(args: AnyRef*): T = |
| 10 | + def fail(msg: String) = error(msg, new IllegalArgumentException(msg)) |
| 11 | + def error(msg: String, e: Throwable) = { errorFn(msg) ; throw e } |
| 12 | + try |
| 13 | + val clazz = Class.forName(path, /*initialize =*/ true, /*loader =*/ cl) |
| 14 | + if classTag[T].runtimeClass.isAssignableFrom(clazz) then |
| 15 | + val ctor = |
| 16 | + val maybes = clazz.getConstructors.filter(c => |
| 17 | + c.getParameterCount == args.size |
| 18 | + && (c.getParameterTypes zip args).forall { case (k, a) => k.isAssignableFrom(a.getClass) }) |
| 19 | + if maybes.size == 1 then maybes.head |
| 20 | + else fail(s"Constructor must accept arg list (${args.map(_.getClass.getName).mkString(", ")}): ${path}") |
| 21 | + (ctor.newInstance(args*)).asInstanceOf[T] |
| 22 | + else |
| 23 | + // TODO show is undefined; in the original code, it is imported from |
| 24 | + // import scala.reflect.runtime.ReflectionUtils.show |
| 25 | + //errorFn(s"""Loader for ${classTag[T]}: [${show(classTag[T].runtimeClass.getClassLoader)}] |
| 26 | + // |Loader for ${clazz.getName}: [${show(clazz.getClassLoader)}]""".stripMargin) |
| 27 | + fail(s"Not a ${classTag[T]}: ${path}") |
| 28 | + catch |
| 29 | + case e: ClassNotFoundException => |
| 30 | + error(s"Class not found: ${path}", e) |
| 31 | + case e @ (_: LinkageError | _: ReflectiveOperationException) => |
| 32 | + error(s"Unable to create instance: ${path}: ${e.toString}", e) |
| 33 | + |
| 34 | + /** Load and link a class with this classloader */ |
| 35 | + def tryToLoadClass[T <: AnyRef](path: String): Option[Class[T]] = |
| 36 | + tryClass(path, initialize = false) |
| 37 | + |
| 38 | + /** Load, link and initialize a class with this classloader */ |
| 39 | + def tryToInitializeClass[T <: AnyRef](path: String): Option[Class[T]] = |
| 40 | + tryClass(path, initialize = true) |
| 41 | + |
| 42 | + private def tryClass[T <: AnyRef](path: String, initialize: Boolean): Option[Class[T]] = |
| 43 | + catching(classOf[ClassNotFoundException], classOf[SecurityException]) opt |
| 44 | + Class.forName(path, initialize, cl).asInstanceOf[Class[T]] |
| 45 | + |
| 46 | + /** The actual bytes for a class file, or an empty array if it can't be found. */ |
| 47 | + def classBytes(className: String): Array[Byte] = classAsStream(className) match |
| 48 | + case null => Array() |
| 49 | + case stream => dotty.tools.io.Streamable.bytes(stream) |
| 50 | + |
| 51 | + private inline def classAsStream(className: String) = cl.getResourceAsStream { |
| 52 | + if className.endsWith(".class") then className |
| 53 | + else s"${className.replace('.', '/')}.class" // classNameToPath |
| 54 | + } |
| 55 | +end ClassLoaderOps |
0 commit comments