diff options
author | Paul Phillips <paulp@improving.org> | 2011-11-03 04:51:52 +0000 |
---|---|---|
committer | Paul Phillips <paulp@improving.org> | 2011-11-03 04:51:52 +0000 |
commit | 938eab16f841fee67b2e34c983a7a2a6a5998127 (patch) | |
tree | 0c576763abf934891b519c61dfe5889541955f71 /src/compiler/scala/tools/nsc/util | |
parent | b6778be91900b8161e705dc2598ef7af86842b0b (diff) | |
download | scala-938eab16f841fee67b2e34c983a7a2a6a5998127.tar.gz scala-938eab16f841fee67b2e34c983a7a2a6a5998127.tar.bz2 scala-938eab16f841fee67b2e34c983a7a2a6a5998127.zip |
ScalaClassLoader changes.
Lots of fiddling in the interests of a better classloading future.
Diffstat (limited to 'src/compiler/scala/tools/nsc/util')
-rw-r--r-- | src/compiler/scala/tools/nsc/util/ClassPath.scala | 13 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/util/ScalaClassLoader.scala | 133 |
2 files changed, 97 insertions, 49 deletions
diff --git a/src/compiler/scala/tools/nsc/util/ClassPath.scala b/src/compiler/scala/tools/nsc/util/ClassPath.scala index 1487b42843..1820bd190f 100644 --- a/src/compiler/scala/tools/nsc/util/ClassPath.scala +++ b/src/compiler/scala/tools/nsc/util/ClassPath.scala @@ -464,5 +464,16 @@ extends ClassPath[T] { class JavaClassPath( containers: IndexedSeq[ClassPath[AbstractFile]], context: JavaContext) -extends MergedClassPath[AbstractFile](containers, context) { +extends MergedClassPath[AbstractFile](containers, context) { } + +object JavaClassPath { + def fromURLs(urls: Seq[URL], context: JavaContext): JavaClassPath = { + val containers = { + for (url <- urls ; f = AbstractFile getURL url ; if f != null) yield + new DirectoryClassPath(f, context) + } + new JavaClassPath(containers.toIndexedSeq, context) + } + def fromURLs(urls: Seq[URL]): JavaClassPath = + fromURLs(urls, ClassPath.DefaultJavaContext) } diff --git a/src/compiler/scala/tools/nsc/util/ScalaClassLoader.scala b/src/compiler/scala/tools/nsc/util/ScalaClassLoader.scala index 07fe254410..d048b75599 100644 --- a/src/compiler/scala/tools/nsc/util/ScalaClassLoader.scala +++ b/src/compiler/scala/tools/nsc/util/ScalaClassLoader.scala @@ -6,26 +6,34 @@ package scala.tools.nsc package util -import java.lang.{ ClassLoader => JavaClassLoader } +import java.lang.{ ClassLoader => JClassLoader } import java.lang.reflect.{ Constructor, Modifier, Method } +import java.io.{ File => JFile } +import java.net.{ URLClassLoader => JURLClassLoader } import java.net.URL +import scala.reflect.unwrapForHandler import ScalaClassLoader._ import scala.util.control.Exception.{ catching } +// import Exceptional.unwrap -trait ScalaClassLoader extends JavaClassLoader { +trait HasClassPath { + def classPathURLs: Seq[URL] +} + +/** A wrapper around java.lang.ClassLoader to lower the annoyance + * of java reflection. + */ +trait ScalaClassLoader extends JClassLoader { /** Override to see classloader activity traced */ protected def trace: Boolean = false /** Executing an action with this classloader as context classloader */ def asContext[T](action: => T): T = { - val oldLoader = getContextLoader - try { - setContextLoader(this) - action - } - finally setContextLoader(oldLoader) + val saved = contextLoader + try { setContext(this) ; action } + finally setContext(saved) } - def setAsContext() { setContextLoader(this) } + def setAsContext() { setContext(this) } /** Load and link a class with this classloader */ def tryToLoadClass[T <: AnyRef](path: String): Option[Class[T]] = tryClass(path, false) @@ -70,68 +78,97 @@ trait ScalaClassLoader extends JavaClassLoader { val clsToRun = tryToInitializeClass(objectName) getOrElse ( throw new ClassNotFoundException(objectName) ) - val method = clsToRun.getMethod("main", classOf[Array[String]]) if (!Modifier.isStatic(method.getModifiers)) throw new NoSuchMethodException(objectName + ".main is not static") - asContext(method.invoke(null, Array(arguments.toArray: AnyRef): _*)) // !!! : AnyRef shouldn't be necessary + try asContext(method.invoke(null, Array(arguments.toArray: AnyRef): _*)) // !!! : AnyRef shouldn't be necessary + catch unwrapForHandler({ case ex => throw ex }) } + + /** A list comprised of this classloader followed by all its + * (non-null) parent classloaders, if any. + */ + def loaderChain: List[ScalaClassLoader] = this :: (getParent match { + case null => Nil + case p => p.loaderChain + }) } +/** Methods for obtaining various classloaders. + * appLoader: the application classloader. (Also called the java system classloader.) + * extLoader: the extension classloader. + * bootLoader: the boot classloader. + * contextLoader: the context classloader. + */ object ScalaClassLoader { - implicit def apply(cl: JavaClassLoader): ScalaClassLoader = { - val loader = if (cl == null) JavaClassLoader.getSystemClassLoader() else cl - new JavaClassLoader(loader) with ScalaClassLoader + /** Returns loaders which are already ScalaClassLoaders unaltered, + * and translates java.net.URLClassLoaders into scala URLClassLoaders. + * Otherwise creates a new wrapper. + */ + implicit def apply(cl: JClassLoader): ScalaClassLoader = cl match { + case cl: ScalaClassLoader => cl + case cl: JURLClassLoader => new URLClassLoader(cl.getURLs.toSeq, cl.getParent) + case _ => new JClassLoader(cl) with ScalaClassLoader + } + def contextLoader = apply(Thread.currentThread.getContextClassLoader) + def appLoader = apply(JClassLoader.getSystemClassLoader) + def extLoader = apply(appLoader.getParent) + def bootLoader = apply(null) + def contextChain = loaderChain(contextLoader) + + def pathToErasure[T: ClassManifest] = pathToClass(classManifest[T].erasure) + def pathToClass(clazz: Class[_]) = clazz.getName.replace('.', JFile.separatorChar) + ".class" + def locate[T: ClassManifest] = contextLoader getResource pathToErasure[T] + + /** Tries to guess the classpath by type matching the context classloader + * and its parents, looking for any classloaders which will reveal their + * classpath elements as urls. It it can't find any, creates a classpath + * from the supplied string. + */ + def guessClassPathString(default: String = ""): String = { + val classpathURLs = contextChain flatMap { + case x: HasClassPath => x.classPathURLs + case x: JURLClassLoader => x.getURLs.toSeq + case _ => Nil + } + if (classpathURLs.isEmpty) default + else JavaClassPath.fromURLs(classpathURLs).asClasspathString } - class URLClassLoader(urls: Seq[URL], parent: JavaClassLoader) - extends java.net.URLClassLoader(urls.toArray, parent) - with ScalaClassLoader { + def loaderChain(head: JClassLoader) = { + def loop(cl: JClassLoader): List[JClassLoader] = + if (cl == null) Nil else cl :: loop(cl.getParent) - private var classloaderURLs = urls.toList + loop(head) + } + def setContext(cl: JClassLoader) = + Thread.currentThread.setContextClassLoader(cl) + + class URLClassLoader(urls: Seq[URL], parent: JClassLoader) + extends JURLClassLoader(urls.toArray, parent) + with ScalaClassLoader + with HasClassPath { + + private var classloaderURLs: Seq[URL] = urls private def classpathString = ClassPath.fromURLs(urls: _*) + def classPathURLs: Seq[URL] = classloaderURLs + def classPath: ClassPath[_] = JavaClassPath fromURLs classPathURLs /** Override to widen to public */ override def addURL(url: URL) = { - classloaderURLs +:= url + classloaderURLs :+= url super.addURL(url) } - override def run(objectName: String, arguments: Seq[String]) { - try super.run(objectName, arguments) - catch { case x: ClassNotFoundException => - throw new ClassNotFoundException(objectName + - " (args = %s, classpath = %s)".format(arguments mkString ", ", classpathString)) - } - } override def toString = urls.mkString("URLClassLoader(\n ", "\n ", "\n)\n") } - def setContextLoader(cl: JavaClassLoader) = Thread.currentThread.setContextClassLoader(cl) - def getContextLoader() = Thread.currentThread.getContextClassLoader() - def getSystemLoader(): ScalaClassLoader = ScalaClassLoader(null) - def defaultParentClassLoader() = findExtClassLoader() - - def fromURLs(urls: Seq[URL], parent: ClassLoader = defaultParentClassLoader()): URLClassLoader = - new URLClassLoader(urls.toList, parent) + def fromURLs(urls: Seq[URL], parent: ClassLoader = null): URLClassLoader = + new URLClassLoader(urls, parent) /** True if supplied class exists in supplied path */ def classExists(urls: Seq[URL], name: String): Boolean = - (fromURLs(urls) tryToLoadClass name).isDefined - - // we cannot use the app classloader here or we get what looks to - // be classloader deadlock, but if we pass null we bypass the extension - // classloader and our extensions, so we search the hierarchy to find - // the classloader whose parent is null. Resolves bug #857. - def findExtClassLoader(): JavaClassLoader = { - def search(cl: JavaClassLoader): JavaClassLoader = { - if (cl == null) null - else if (cl.getParent == null) cl - else search(cl.getParent) - } - - search(getContextLoader()) - } + fromURLs(urls) tryToLoadClass name isDefined /** Finding what jar a clazz or instance came from */ def origin(x: Any): Option[URL] = originOfClass(x.getClass) |