From 967ceb28ab2737ee16112b4c9be46419f43b9a99 Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Sun, 15 Apr 2012 11:36:43 +0200 Subject: big fat error message for default mirror failures --- src/library/scala/reflect/ReflectionUtils.scala | 22 ++++++++ src/library/scala/reflect/package.scala | 67 ++++++++++++++++++++++--- 2 files changed, 83 insertions(+), 6 deletions(-) diff --git a/src/library/scala/reflect/ReflectionUtils.scala b/src/library/scala/reflect/ReflectionUtils.scala index 1be46eac55..79a42f6ec4 100644 --- a/src/library/scala/reflect/ReflectionUtils.scala +++ b/src/library/scala/reflect/ReflectionUtils.scala @@ -27,6 +27,28 @@ object ReflectionUtils { case ex if pf isDefinedAt unwrapThrowable(ex) => pf(unwrapThrowable(ex)) } + private def systemProperties: Iterator[(String, String)] = { + import scala.collection.JavaConverters._ + System.getProperties.asScala.iterator + } + + private def searchForBootClasspath = ( + systemProperties find (_._1 endsWith ".boot.class.path") map (_._2) getOrElse "" + ) + + def show(cl: ClassLoader) = { + def inferClasspath(cl: ClassLoader) = cl match { + case cl: java.net.URLClassLoader => "[" + (cl.getURLs mkString ",") + "]" + case _ => "" + } + cl match { + case cl if cl != null => + "%s of type %s with classpath %s".format(cl, cl.getClass, inferClasspath(cl)) + case null => + "primordial classloader with boot classpath [%s]".format(searchForBootClasspath) + } + } + def defaultReflectionClassLoader() = { // say no to non-determinism of mirror classloaders // default classloader will be instantiated using current system classloader diff --git a/src/library/scala/reflect/package.scala b/src/library/scala/reflect/package.scala index 1738642932..6f40a3ac3e 100644 --- a/src/library/scala/reflect/package.scala +++ b/src/library/scala/reflect/package.scala @@ -3,6 +3,7 @@ package scala package object reflect { import ReflectionUtils._ + import scala.compat.Platform.EOL // !!! This was a val; we can't throw exceptions that aggressively without breaking // non-standard environments, e.g. google app engine. I made it a lazy val, but @@ -12,15 +13,69 @@ package object reflect { // todo. default mirror (a static object) might become a source for memory leaks (because it holds a strong reference to a classloader)! lazy val mirror: api.Mirror = mkMirror(defaultReflectionClassLoader) + private def mirrorDiagnostics(cl: ClassLoader): String = """ + | + | This error has happened because `scala.reflect.runtime.package` located in + | scala-compiler.jar cannot be loaded. Classloader you are using is: + | %s. + | + | In Scala 2.10.0 M3, scala-compiler.jar is required to be on the classpath + | for manifests and type tags to function. This will change in the final release, + | but for now you need to adjust your scripts or build system to proceed. + | Here are the instructions for some of the situations that might be relevant. + | + | If you compile your application directly from the command line + | or a hand-rolled script, this is a bug. Please, report it. + | + | If you compile your application with Maven using the maven-scala plugin, + | set its "fork" configuration entry to "false: + | + | + | org.scala-tools + | maven-scala-plugin + | 2.15.0 + | + | + | + | ... + | + | + | false + | ... + | + | + | + | + | + | If you compile your application with SBT, + | + | + | If you compile your application in Scala IDE, + | . + | + | If you launch your application directly from the command line + | or a hand-rolled script, add `scala-compiler.jar` to the classpath: + | + | scalac HelloWorld.scala + | scala HelloWorld -cp path/to/scala-compiler.jar + | + | If you launch your application with Maven using the maven-scala plugin, + | set its "fork" configuration entry to "false as shown above. + | + | If you launch your application with SBT, make sure that you use + | + | + | If you launch your application in Scala IDE, make sure that both scala-library.jar and scala-compiler.jar + | are in bootstrap entries on the classpath of your launch configuration. + """.stripMargin('|').format(show(cl)) + def mkMirror(classLoader: ClassLoader): api.Mirror = { - // we use (Java) reflection here so that we can keep reflect.runtime and reflect.internals in a seperate jar - // note that we must instantiate the mirror with current classloader, otherwise we won't be able to cast it to api.Mirror - // that's not a problem, though, because mirror can service classes from arbitrary classloaders - val instance = invokeFactoryOpt(getClass.getClassLoader, "scala.reflect.runtime.package", "mkMirror", classLoader) + val coreClassLoader = getClass.getClassLoader + val instance = invokeFactoryOpt(coreClassLoader, "scala.reflect.runtime.package", "mkMirror", classLoader) instance match { case Some(x: api.Mirror) => x - case Some(_) => throw new UnsupportedOperationException("Available scala reflection implementation is incompatible with this interface") - case None => throw new UnsupportedOperationException("Scala reflection not available on this platform") + case Some(_) => throw new UnsupportedOperationException("Available scala reflection implementation is incompatible with this interface." + mirrorDiagnostics(coreClassLoader)) + case None => throw new UnsupportedOperationException("Scala reflection not available on this platform." + mirrorDiagnostics(coreClassLoader)) } } -- cgit v1.2.3