summaryrefslogtreecommitdiff
path: root/src/reflect
diff options
context:
space:
mode:
authorEugene Burmako <xeno.by@gmail.com>2013-05-22 04:34:33 -0700
committerEugene Burmako <xeno.by@gmail.com>2013-05-22 04:34:33 -0700
commit649d5bb3a59326ea8fb7790f6abc948951c73905 (patch)
tree2b9c2de75c6a68d4d0ebef3f6a35abea7c902cb6 /src/reflect
parent085b4d9bdb7ba9f9fe00c63e998e93278a34b161 (diff)
parent8f6b4743a0fac93f671e9eff3fca2b4dec4bf935 (diff)
downloadscala-649d5bb3a59326ea8fb7790f6abc948951c73905.tar.gz
scala-649d5bb3a59326ea8fb7790f6abc948951c73905.tar.bz2
scala-649d5bb3a59326ea8fb7790f6abc948951c73905.zip
Merge pull request #2568 from scalamacros/topic/abstractfile-classloader
Moves AbstractFileClassLoader to scala-reflect.jar
Diffstat (limited to 'src/reflect')
-rw-r--r--src/reflect/scala/reflect/internal/util/AbstractFileClassLoader.scala122
-rw-r--r--src/reflect/scala/reflect/internal/util/ScalaClassLoader.scala124
-rw-r--r--src/reflect/scala/reflect/runtime/ReflectionUtils.scala3
3 files changed, 248 insertions, 1 deletions
diff --git a/src/reflect/scala/reflect/internal/util/AbstractFileClassLoader.scala b/src/reflect/scala/reflect/internal/util/AbstractFileClassLoader.scala
new file mode 100644
index 0000000000..10a8b4c812
--- /dev/null
+++ b/src/reflect/scala/reflect/internal/util/AbstractFileClassLoader.scala
@@ -0,0 +1,122 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2013 LAMP/EPFL
+ */
+
+package scala
+package reflect.internal.util
+
+import scala.reflect.io.AbstractFile
+import java.security.cert.Certificate
+import java.security.{ ProtectionDomain, CodeSource }
+import java.net.{ URL, URLConnection, URLStreamHandler }
+import scala.collection.{ mutable, immutable }
+
+/**
+ * A class loader that loads files from a {@link scala.tools.nsc.io.AbstractFile}.
+ *
+ * @author Lex Spoon
+ */
+class AbstractFileClassLoader(val root: AbstractFile, parent: ClassLoader)
+ extends ClassLoader(parent)
+ with ScalaClassLoader
+{
+ protected def classNameToPath(name: String): String =
+ if (name endsWith ".class") name
+ else name.replace('.', '/') + ".class"
+
+ protected def findAbstractFile(name: String): AbstractFile = {
+ var file: AbstractFile = root
+ val pathParts = name split '/'
+
+ for (dirPart <- pathParts.init) {
+ file = file.lookupName(dirPart, directory = true)
+ if (file == null)
+ return null
+ }
+
+ file.lookupName(pathParts.last, directory = false) match {
+ case null => null
+ case file => file
+ }
+ }
+
+ protected def dirNameToPath(name: String): String =
+ name.replace('.', '/')
+
+ protected def findAbstractDir(name: String): AbstractFile = {
+ var file: AbstractFile = root
+ val pathParts = dirNameToPath(name) split '/'
+
+ for (dirPart <- pathParts) {
+ file = file.lookupName(dirPart, directory = true)
+ if (file == null)
+ return null
+ }
+
+ file
+ }
+
+ // parent delegation in JCL uses getResource; so either add parent.getResAsStream
+ // or implement findResource, which we do here as a study in scarlet (my complexion
+ // after looking at CLs and URLs)
+ override def findResource(name: String): URL = findAbstractFile(name) match {
+ case null => null
+ case file => new URL(null, "repldir:" + file.path, new URLStreamHandler {
+ override def openConnection(url: URL): URLConnection = new URLConnection(url) {
+ override def connect() { }
+ override def getInputStream = file.input
+ }
+ })
+ }
+
+ // this inverts delegation order: super.getResAsStr calls parent.getRes if we fail
+ override def getResourceAsStream(name: String) = findAbstractFile(name) match {
+ case null => super.getResourceAsStream(name)
+ case file => file.input
+ }
+ // ScalaClassLoader.classBytes uses getResAsStream, so we'll try again before delegating
+ override def classBytes(name: String): Array[Byte] = findAbstractFile(classNameToPath(name)) match {
+ case null => super.classBytes(name)
+ case file => file.toByteArray
+ }
+ override def findClass(name: String): Class[_] = {
+ val bytes = classBytes(name)
+ if (bytes.length == 0)
+ throw new ClassNotFoundException(name)
+ else
+ defineClass(name, bytes, 0, bytes.length, protectionDomain)
+ }
+
+ lazy val protectionDomain = {
+ val cl = Thread.currentThread().getContextClassLoader()
+ val resource = cl.getResource("scala/runtime/package.class")
+ if (resource == null || resource.getProtocol != "jar") null else {
+ val s = resource.getPath
+ val n = s.lastIndexOf('!')
+ if (n < 0) null else {
+ val path = s.substring(0, n)
+ new ProtectionDomain(new CodeSource(new URL(path), null.asInstanceOf[Array[Certificate]]), null, this, null)
+ }
+ }
+ }
+
+ private val packages = mutable.Map[String, Package]()
+
+ override def definePackage(name: String, specTitle: String, specVersion: String, specVendor: String, implTitle: String, implVersion: String, implVendor: String, sealBase: URL): Package = {
+ throw new UnsupportedOperationException()
+ }
+
+ override def getPackage(name: String): Package = {
+ findAbstractDir(name) match {
+ case null => super.getPackage(name)
+ case file => packages.getOrElseUpdate(name, {
+ val ctor = classOf[Package].getDeclaredConstructor(classOf[String], classOf[String], classOf[String], classOf[String], classOf[String], classOf[String], classOf[String], classOf[URL], classOf[ClassLoader])
+ ctor.setAccessible(true)
+ ctor.newInstance(name, null, null, null, null, null, null, null, this)
+ })
+ }
+ }
+
+ override def getPackages(): Array[Package] =
+ root.iterator.filter(_.isDirectory).map(dir => getPackage(dir.name)).toArray
+}
diff --git a/src/reflect/scala/reflect/internal/util/ScalaClassLoader.scala b/src/reflect/scala/reflect/internal/util/ScalaClassLoader.scala
new file mode 100644
index 0000000000..a7fd787dfc
--- /dev/null
+++ b/src/reflect/scala/reflect/internal/util/ScalaClassLoader.scala
@@ -0,0 +1,124 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2013 LAMP/EPFL
+ * @author Paul Phillips
+ */
+
+package scala
+package reflect.internal.util
+
+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.runtime.ReflectionUtils.unwrapHandler
+import ScalaClassLoader._
+import scala.util.control.Exception.{ catching }
+import scala.language.implicitConversions
+import scala.reflect.{ ClassTag, classTag }
+
+trait HasClassPath {
+ def classPathURLs: Seq[URL]
+}
+
+/** A wrapper around java.lang.ClassLoader to lower the annoyance
+ * of java reflection.
+ */
+trait ScalaClassLoader extends JClassLoader {
+ /** Executing an action with this classloader as context classloader */
+ def asContext[T](action: => T): T = {
+ val saved = contextLoader
+ try { setContext(this) ; action }
+ finally setContext(saved)
+ }
+ def setAsContext() { setContext(this) }
+
+ /** Load and link a class with this classloader */
+ def tryToLoadClass[T <: AnyRef](path: String): Option[Class[T]] = tryClass(path, initialize = false)
+ /** Load, link and initialize a class with this classloader */
+ def tryToInitializeClass[T <: AnyRef](path: String): Option[Class[T]] = tryClass(path, initialize = true)
+
+ private def tryClass[T <: AnyRef](path: String, initialize: Boolean): Option[Class[T]] =
+ catching(classOf[ClassNotFoundException], classOf[SecurityException]) opt
+ Class.forName(path, initialize, this).asInstanceOf[Class[T]]
+
+ /** Create an instance of a class with this classloader */
+ def create(path: String): AnyRef =
+ tryToInitializeClass[AnyRef](path) map (_.newInstance()) orNull
+
+ /** The actual bytes for a class file, or an empty array if it can't be found. */
+ def classBytes(className: String): Array[Byte] = classAsStream(className) match {
+ case null => Array()
+ case stream => scala.reflect.io.Streamable.bytes(stream)
+ }
+
+ /** An InputStream representing the given class name, or null if not found. */
+ def classAsStream(className: String) =
+ getResourceAsStream(className.replaceAll("""\.""", "/") + ".class")
+
+ /** Run the main method of a class to be loaded by this classloader */
+ def run(objectName: String, arguments: Seq[String]) {
+ 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")
+
+ try asContext(method.invoke(null, Array(arguments.toArray: AnyRef): _*)) // !!! : AnyRef shouldn't be necessary
+ catch unwrapHandler({ case ex => throw ex })
+ }
+}
+
+/** 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 {
+ /** 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 setContext(cl: JClassLoader) =
+ Thread.currentThread.setContextClassLoader(cl)
+ def savingContextLoader[T](body: => T): T = {
+ val saved = contextLoader
+ try body
+ finally setContext(saved)
+ }
+
+ class URLClassLoader(urls: Seq[URL], parent: JClassLoader)
+ extends JURLClassLoader(urls.toArray, parent)
+ with ScalaClassLoader
+ with HasClassPath {
+
+ private var classloaderURLs: Seq[URL] = urls
+ def classPathURLs: Seq[URL] = classloaderURLs
+
+ /** Override to widen to public */
+ override def addURL(url: URL) = {
+ classloaderURLs :+= url
+ super.addURL(url)
+ }
+ }
+
+ 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
+
+ /** Finding what jar a clazz or instance came from */
+ def originOfClass(x: Class[_]): Option[URL] =
+ Option(x.getProtectionDomain.getCodeSource) flatMap (x => Option(x.getLocation))
+}
diff --git a/src/reflect/scala/reflect/runtime/ReflectionUtils.scala b/src/reflect/scala/reflect/runtime/ReflectionUtils.scala
index 53495e6ac8..2db9706007 100644
--- a/src/reflect/scala/reflect/runtime/ReflectionUtils.scala
+++ b/src/reflect/scala/reflect/runtime/ReflectionUtils.scala
@@ -8,6 +8,7 @@ package reflect.runtime
import java.lang.{Class => jClass}
import java.lang.reflect.{ Method, InvocationTargetException, UndeclaredThrowableException }
+import scala.reflect.internal.util.AbstractFileClassLoader
/** A few java-reflection oriented utility functions useful during reflection bootstrapping.
*/
@@ -34,7 +35,7 @@ private[scala] object ReflectionUtils {
def isAbstractFileClassLoader(clazz: Class[_]): Boolean = {
if (clazz == null) return false
- if (clazz.getName == "scala.tools.nsc.interpreter.AbstractFileClassLoader") return true
+ if (clazz == classOf[AbstractFileClassLoader]) return true
isAbstractFileClassLoader(clazz.getSuperclass)
}
def inferClasspath(cl: ClassLoader): String = cl match {