summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Macros.scala152
1 files changed, 71 insertions, 81 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala
index c21222f450..d43dceca58 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala
@@ -8,9 +8,10 @@ import scala.reflect.ReflectionUtils
import scala.collection.mutable.ListBuffer
import scala.compat.Platform.EOL
import scala.reflect.makro.runtime.{Context => MacroContext}
-import scala.reflect.runtime.Mirror
import util.Statistics._
import scala.reflect.makro.util._
+import java.lang.{Class => jClass}
+import java.lang.reflect.{Array => jArray, Method => jMethod}
/**
* Code to deal with macros, namely with:
@@ -583,59 +584,53 @@ trait Macros extends Traces {
runtimeType
}
- /** Primary mirror that is used to resolve and run macro implementations.
+ /** Primary classloader that is used to resolve and run macro implementations.
* Loads classes from -Xmacro-primary-classpath, or from -cp if the option is not specified.
+ * Is also capable of detecting REPL and reusing its classloader.
*/
- private lazy val primaryMirror: Mirror = {
+ private lazy val primaryClassloader: ClassLoader = {
if (global.forMSIL)
throw new UnsupportedOperationException("Scala reflection not available on this platform")
- val libraryClassLoader = {
- if (settings.XmacroPrimaryClasspath.value != "") {
- macroLogVerbose("primary macro mirror: initializing from -Xmacro-primary-classpath: %s".format(settings.XmacroPrimaryClasspath.value))
- val classpath = toURLs(settings.XmacroFallbackClasspath.value)
- ScalaClassLoader.fromURLs(classpath, self.getClass.getClassLoader)
- } else {
- macroLogVerbose("primary macro mirror: initializing from -cp: %s".format(global.classPath.asURLs))
- val classpath = global.classPath.asURLs
- var loader: ClassLoader = ScalaClassLoader.fromURLs(classpath, self.getClass.getClassLoader)
-
- // [Eugene] a heuristic to detect REPL
- if (global.settings.exposeEmptyPackage.value) {
- import scala.tools.nsc.interpreter._
- val virtualDirectory = global.settings.outputDirs.getSingleOutput.get
- loader = new AbstractFileClassLoader(virtualDirectory, loader) {}
- }
-
- loader
+ if (settings.XmacroPrimaryClasspath.value != "") {
+ macroLogVerbose("primary macro classloader: initializing from -Xmacro-primary-classpath: %s".format(settings.XmacroPrimaryClasspath.value))
+ val classpath = toURLs(settings.XmacroFallbackClasspath.value)
+ ScalaClassLoader.fromURLs(classpath, self.getClass.getClassLoader)
+ } else {
+ macroLogVerbose("primary macro classloader: initializing from -cp: %s".format(global.classPath.asURLs))
+ val classpath = global.classPath.asURLs
+ var loader: ClassLoader = ScalaClassLoader.fromURLs(classpath, self.getClass.getClassLoader)
+
+ // [Eugene] a heuristic to detect the REPL
+ if (global.settings.exposeEmptyPackage.value) {
+ macroLogVerbose("primary macro classloader: initializing from a REPL classloader".format(global.classPath.asURLs))
+ import scala.tools.nsc.interpreter._
+ val virtualDirectory = global.settings.outputDirs.getSingleOutput.get
+ loader = new AbstractFileClassLoader(virtualDirectory, loader) {}
}
- }
- new Mirror(libraryClassLoader) { override def toString = "<primary macro mirror>" }
+ loader
+ }
}
- /** Fallback mirror that is used to resolve and run macro implementations.
- * Loads classes from -Xmacro-fallback-classpath aka "macro fallback classpath".
+ /** Fallback classloader that is used to resolve and run macro implementations when `primaryClassloader` fails.
+ * Loads classes from -Xmacro-fallback-classpath.
*/
- private lazy val fallbackMirror: Mirror = {
+ private lazy val fallbackClassloader: ClassLoader = {
if (global.forMSIL)
throw new UnsupportedOperationException("Scala reflection not available on this platform")
- val fallbackClassLoader = {
- macroLogVerbose("fallback macro mirror: initializing from -Xmacro-fallback-classpath: %s".format(settings.XmacroFallbackClasspath.value))
- val classpath = toURLs(settings.XmacroFallbackClasspath.value)
- ScalaClassLoader.fromURLs(classpath, self.getClass.getClassLoader)
- }
-
- new Mirror(fallbackClassLoader) { override def toString = "<fallback macro mirror>" }
+ macroLogVerbose("fallback macro classloader: initializing from -Xmacro-fallback-classpath: %s".format(settings.XmacroFallbackClasspath.value))
+ val classpath = toURLs(settings.XmacroFallbackClasspath.value)
+ ScalaClassLoader.fromURLs(classpath, self.getClass.getClassLoader)
}
/** Produces a function that can be used to invoke macro implementation for a given macro definition:
* 1) Looks up macro implementation symbol in this universe.
- * 2) Loads its enclosing class from the primary mirror.
- * 3) Loads the companion of that enclosing class from the primary mirror.
+ * 2) Loads its enclosing class from the primary classloader.
+ * 3) Loads the companion of that enclosing class from the primary classloader.
* 4) Resolves macro implementation within the loaded companion.
- * 5) If 2-4 fails, repeats them for the fallback mirror.
+ * 5) If 2-4 fails, repeats them for the fallback classloader.
*
* @return Some(runtime) if macro implementation can be loaded successfully from either of the mirrors,
* None otherwise.
@@ -691,7 +686,10 @@ trait Macros extends Traces {
macroLogVerbose("resolved implementation %s at %s".format(macroImpl, macroImpl.pos))
if (macroImpl.isErroneous) { macroTraceVerbose("macro implementation is erroneous (this means that either macro body or macro implementation signature failed to typecheck)")(macroDef); return None }
- def loadMacroImpl(macroMirror: Mirror): Option[(Object, macroMirror.Symbol)] = {
+ // [Eugene++] I don't use Scala reflection here, because it seems to interfere with JIT magic
+ // whenever you instantiate a mirror (and not do anything with in, just instantiate), performance drops by 15-20%
+ // I'm not sure what's the reason - for me it's pure voodoo
+ def loadMacroImpl(cl: ClassLoader): Option[(Object, jMethod)] = {
try {
// this logic relies on the assumptions that were valid for the old macro prototype
// namely that macro implementations can only be defined in top-level classes and modules
@@ -702,10 +700,10 @@ trait Macros extends Traces {
// for now I leave it as a todo and move along to more the important stuff
macroTraceVerbose("loading implementation class: ")(macroImpl.owner.fullName)
- macroTraceVerbose("classloader is: ")(ReflectionUtils.show(macroMirror.classLoader))
+ macroTraceVerbose("classloader is: ")(ReflectionUtils.show(cl))
// [Eugene] relies on the fact that macro implementations can only be defined in static classes
- // [Martin to Eugene] There's similar logic buried in Symbol#flatname. Maybe we can refactor?
+ // [Martin to Eugene++] There's similar logic buried in Symbol#flatname. Maybe we can refactor?
def classfile(sym: Symbol): String = {
def recur(sym: Symbol): String = sym match {
case sym if sym.owner.isPackageClass =>
@@ -720,51 +718,43 @@ trait Macros extends Traces {
else recur(sym.enclClass)
}
- // [Eugene] this doesn't work for inner classes
+ // [Eugene++] this doesn't work for inner classes
// neither does macroImpl.owner.javaClassName, so I had to roll my own implementation
//val receiverName = macroImpl.owner.fullName
val implClassName = classfile(macroImpl.owner)
- val implClassSymbol: macroMirror.Symbol = macroMirror.symbolForName(implClassName)
-
- if (macroDebugVerbose) {
- println("implClassSymbol is: " + implClassSymbol.fullNameString)
-
- if (implClassSymbol != macroMirror.NoSymbol) {
- val implClass = macroMirror.classToJava(implClassSymbol)
- val implSource = implClass.getProtectionDomain.getCodeSource
- println("implClass is %s from %s".format(implClass, implSource))
- println("implClassLoader is %s".format(implClass.getClassLoader, ReflectionUtils.show(implClass.getClassLoader)))
- }
+ val implObj = try {
+ val implObjClass = jClass.forName(implClassName, true, cl)
+ implObjClass getField "MODULE$" get null
+ } catch {
+ case ex: NoSuchFieldException => macroTraceVerbose("exception when loading implObj: ")(ex); null
+ case ex: NoClassDefFoundError => macroTraceVerbose("exception when loading implObj: ")(ex); null
+ case ex: ClassNotFoundException => macroTraceVerbose("exception when loading implObj: ")(ex); null
}
- val implObjSymbol = implClassSymbol.companionModule
- macroTraceVerbose("implObjSymbol is: ")(implObjSymbol.fullNameString)
-
- if (implObjSymbol == macroMirror.NoSymbol) None
+ if (implObj == null) None
else {
- // yet another reflection method that doesn't work for inner classes
- //val receiver = macroMirror.companionInstance(receiverClass)
- val implObj = try {
- val implObjClass = java.lang.Class.forName(implClassName, true, macroMirror.classLoader)
- implObjClass getField "MODULE$" get null
- } catch {
- case ex: NoSuchFieldException => macroTraceVerbose("exception when loading implObj: ")(ex); null
- case ex: NoClassDefFoundError => macroTraceVerbose("exception when loading implObj: ")(ex); null
- case ex: ClassNotFoundException => macroTraceVerbose("exception when loading implObj: ")(ex); null
- }
-
- if (implObj == null) None
- else {
- val implMethSymbol = implObjSymbol.info.member(macroMirror.newTermName(macroImpl.name.toString))
- macroLogVerbose("implMethSymbol is: " + implMethSymbol.fullNameString)
- macroLogVerbose("jimplMethSymbol is: " + macroMirror.methodToJava(implMethSymbol))
+ // [Eugene++] doh, it seems that I need to copy/paste Scala reflection logic
+ // see `JavaMirrors.methodToJava` or whatever it's called now
+ val implMeth = {
+ def typeToJavaClass(tpe: Type): jClass[_] = tpe match {
+ case ExistentialType(_, rtpe) => typeToJavaClass(rtpe)
+ case TypeRef(_, ArrayClass, List(elemtpe)) => jArray.newInstance(typeToJavaClass(elemtpe), 0).getClass
+ case TypeRef(_, sym: ClassSymbol, _) => jClass.forName(classfile(sym), true, cl)
+ case _ => throw new NoClassDefFoundError("no Java class corresponding to "+tpe+" found")
+ }
- if (implMethSymbol == macroMirror.NoSymbol) None
- else {
- macroLogVerbose("successfully loaded macro impl as (%s, %s)".format(implObj, implMethSymbol))
- Some((implObj, implMethSymbol))
+ val paramClasses = transformedType(macroImpl).paramTypes map typeToJavaClass
+ try implObj.getClass getDeclaredMethod (macroImpl.name.toString, paramClasses: _*)
+ catch {
+ case ex: NoSuchMethodException =>
+ val expandedName =
+ if (macroImpl.isPrivate) nme.expandedName(macroImpl.name.toTermName, macroImpl.owner).toString
+ else macroImpl.name.toString
+ implObj.getClass getDeclaredMethod (expandedName, paramClasses: _*)
}
}
+ macroLogVerbose("successfully loaded macro impl as (%s, %s)".format(implObj, implMeth))
+ Some((implObj, implMeth))
}
} catch {
case ex: ClassNotFoundException =>
@@ -776,18 +766,18 @@ trait Macros extends Traces {
}
}
- val primary = loadMacroImpl(primaryMirror)
+ val primary = loadMacroImpl(primaryClassloader)
primary match {
- case Some((implObj, implMethSymbol)) =>
- def runtime(args: List[Any]) = primaryMirror.invoke(implObj, implMethSymbol)(args: _*).asInstanceOf[Any]
+ case Some((implObj, implMeth)) =>
+ def runtime(args: List[Any]) = implMeth.invoke(implObj, (args map (_.asInstanceOf[AnyRef])): _*).asInstanceOf[Any]
Some(runtime _)
case None =>
if (settings.XmacroFallbackClasspath.value != "") {
macroLogVerbose("trying to load macro implementation from the fallback mirror: %s".format(settings.XmacroFallbackClasspath.value))
- val fallback = loadMacroImpl(fallbackMirror)
+ val fallback = loadMacroImpl(fallbackClassloader)
fallback match {
- case Some((implObj, implMethSymbol)) =>
- def runtime(args: List[Any]) = fallbackMirror.invoke(implObj, implMethSymbol)(args: _*).asInstanceOf[Any]
+ case Some((implObj, implMeth)) =>
+ def runtime(args: List[Any]) = implMeth.invoke(implObj, (args map (_.asInstanceOf[AnyRef])): _*).asInstanceOf[Any]
Some(runtime _)
case None =>
None