Permalink
Browse files

cherry-picking important macro fixes from topic/reflection

  • Loading branch information...
xeno-by committed May 12, 2012
1 parent 7cac633 commit 364deb0b7f20c7d88e0a67d48cf276b37791179e
@@ -1 +1 @@
c020eccb8cf37963725985f36b44d070915cf4d2 ?scala-compiler.jar
5b3f50d124f84dcda869e17fb0cfd605ed40f385 ?scala-compiler.jar
@@ -1 +1 @@
31c7188cef85c28b84b9ce35bc6780996e5dd139 ?scala-library.jar
8f19876a8908e7d7d2a140a8434805cfec2c1346 ?scala-library.jar
@@ -1133,16 +1133,27 @@ trait Implicits {
* An EmptyTree is returned if materialization fails.
*/
private def tagOfType(pre: Type, tp: Type, tagClass: Symbol): SearchResult = {
def success(arg: Tree) =
def success(arg: Tree) = {
def isMacroException(msg: String): Boolean =
// [Eugene] very unreliable, ask Hubert about a better way
msg contains "exception during macro expansion"
def processMacroExpansionError(pos: Position, msg: String): SearchResult = {
// giving up and reporting all macro exceptions regardless of their source
// this might lead to an avalanche of errors if one of your implicit macros misbehaves
if (isMacroException(msg)) context.error(pos, msg)
failure(arg, "failed to typecheck the materialized tag: %n%s".format(msg), pos)
}
try {
val tree1 = typed(atPos(pos.focus)(arg))
def isErroneous = tree exists (_.isErroneous)
if (context.hasErrors) failure(tp, "failed to typecheck the materialized typetag: %n%s".format(context.errBuffer.head.errMsg), context.errBuffer.head.errPos)
if (context.hasErrors) processMacroExpansionError(context.errBuffer.head.errPos, context.errBuffer.head.errMsg)
else new SearchResult(tree1, EmptyTreeTypeSubstituter)
} catch {
case ex: TypeError =>
failure(arg, "failed to typecheck the materialized typetag: %n%s".format(ex.msg), ex.pos)
processMacroExpansionError(ex.pos, ex.msg)
}
}
val prefix = (
// ClassTags only exist for scala.reflect.mirror, so their materializer
@@ -641,143 +641,181 @@ trait Macros extends Traces {
*/
private type MacroRuntime = List[Any] => Any
private val macroRuntimesCache = perRunCaches.newWeakMap[Symbol, Option[MacroRuntime]]
private def macroRuntime(macroDef: Symbol): Option[MacroRuntime] =
macroRuntimesCache.getOrElseUpdate(macroDef, {
val runtime = {
macroTraceVerbose("looking for macro implementation: ")(macroDef)
macroTraceVerbose("macroDef is annotated with: ")(macroDef.annotations)
val ann = macroDef.getAnnotation(MacroImplAnnotation)
if (ann == None) {
macroTraceVerbose("@macroImpl annotation is missing (this means that macro definition failed to typecheck)")(macroDef)
return None
}
private lazy val fastTrack: Map[Symbol, MacroRuntime] = {
import scala.reflect.api.Universe
import scala.reflect.makro.internal._
Map( // challenge: how can we factor out the common code? Does not seem to be easy.
MacroInternal_materializeArrayTag -> (args => {
assert(args.length == 3, args)
val c = args(0).asInstanceOf[MacroContext]
materializeArrayTag_impl(c)(args(1).asInstanceOf[c.Expr[Universe]])(args(2).asInstanceOf[c.TypeTag[_]])
}),
MacroInternal_materializeErasureTag -> (args => {
assert(args.length == 3, args)
val c = args(0).asInstanceOf[MacroContext]
materializeErasureTag_impl(c)(args(1).asInstanceOf[c.Expr[Universe]])(args(2).asInstanceOf[c.TypeTag[_]])
}),
MacroInternal_materializeClassTag -> (args => {
assert(args.length == 3, args)
val c = args(0).asInstanceOf[MacroContext]
materializeClassTag_impl(c)(args(1).asInstanceOf[c.Expr[Universe]])(args(2).asInstanceOf[c.TypeTag[_]])
}),
MacroInternal_materializeTypeTag -> (args => {
assert(args.length == 3, args)
val c = args(0).asInstanceOf[MacroContext]
materializeTypeTag_impl(c)(args(1).asInstanceOf[c.Expr[Universe]])(args(2).asInstanceOf[c.TypeTag[_]])
}),
MacroInternal_materializeConcreteTypeTag -> (args => {
assert(args.length == 3, args)
val c = args(0).asInstanceOf[MacroContext]
materializeConcreteTypeTag_impl(c)(args(1).asInstanceOf[c.Expr[Universe]])(args(2).asInstanceOf[c.TypeTag[_]])
})
)
}
private def macroRuntime(macroDef: Symbol): Option[MacroRuntime] = {
macroTraceVerbose("looking for macro implementation: ")(macroDef)
if (fastTrack contains macroDef) {
macroLogVerbose("macro expansion serviced by a fast track")
Some(fastTrack(macroDef))
} else {
macroRuntimesCache.getOrElseUpdate(macroDef, {
val runtime = {
macroTraceVerbose("looking for macro implementation: ")(macroDef)
macroTraceVerbose("macroDef is annotated with: ")(macroDef.annotations)
val macroImpl = ann.get.args(0).symbol
if (macroImpl == NoSymbol) {
macroTraceVerbose("@macroImpl annotation is malformed (this means that macro definition failed to typecheck)")(macroDef)
return None
}
val ann = macroDef.getAnnotation(MacroImplAnnotation)
if (ann == None) {
macroTraceVerbose("@macroImpl annotation is missing (this means that macro definition failed to typecheck)")(macroDef)
return None
}
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
}
val macroImpl = ann.get.args(0).symbol
if (macroImpl == NoSymbol) {
macroTraceVerbose("@macroImpl annotation is malformed (this means that macro definition failed to typecheck)")(macroDef)
return None
}
def loadMacroImpl(macroMirror: Mirror): Option[(Object, macroMirror.Symbol)] = {
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
// with the new prototype that materialized in a SIP, macros need to be statically accessible, which is different
// for example, a macro def could be defined in a trait that is implemented by an object
// there are some more clever cases when seemingly non-static method ends up being statically accessible
// however, the code below doesn't account for these guys, because it'd take a look of time to get it right
// for now I leave it as a todo and move along to more the important stuff
macroTraceVerbose("loading implementation class from %s: ".format(macroMirror))(macroImpl.owner.fullName)
macroTraceVerbose("classloader is: ")("%s of type %s".format(macroMirror.classLoader, if (macroMirror.classLoader != null) macroMirror.classLoader.getClass.toString else "primordial classloader"))
def inferClasspath(cl: ClassLoader) = cl match {
case cl: java.net.URLClassLoader => "[" + (cl.getURLs mkString ",") + "]"
case null => "[" + scala.tools.util.PathResolver.Environment.javaBootClassPath + "]"
case _ => "<unknown>"
}
macroTraceVerbose("classpath is: ")(inferClasspath(macroMirror.classLoader))
// [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?
def classfile(sym: Symbol): String = {
def recur(sym: Symbol): String = sym match {
case sym if sym.owner.isPackageClass =>
val suffix = if (sym.isModuleClass) "$" else ""
sym.fullName + suffix
case sym =>
val separator = if (sym.owner.isModuleClass) "" else "$"
recur(sym.owner) + separator + sym.javaSimpleName.toString
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)] = {
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
// with the new prototype that materialized in a SIP, macros need to be statically accessible, which is different
// for example, a macro def could be defined in a trait that is implemented by an object
// there are some more clever cases when seemingly non-static method ends up being statically accessible
// however, the code below doesn't account for these guys, because it'd take a look of time to get it right
// for now I leave it as a todo and move along to more the important stuff
macroTraceVerbose("loading implementation class from %s: ".format(macroMirror))(macroImpl.owner.fullName)
macroTraceVerbose("classloader is: ")("%s of type %s".format(macroMirror.classLoader, if (macroMirror.classLoader != null) macroMirror.classLoader.getClass.toString else "primordial classloader"))
def inferClasspath(cl: ClassLoader) = cl match {
case cl: java.net.URLClassLoader => "[" + (cl.getURLs mkString ",") + "]"
case null => "[" + scala.tools.util.PathResolver.Environment.javaBootClassPath + "]"
case _ => "<unknown>"
}
macroTraceVerbose("classpath is: ")(inferClasspath(macroMirror.classLoader))
// [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?
def classfile(sym: Symbol): String = {
def recur(sym: Symbol): String = sym match {
case sym if sym.owner.isPackageClass =>
val suffix = if (sym.isModuleClass) "$" else ""
sym.fullName + suffix
case sym =>
val separator = if (sym.owner.isModuleClass) "" else "$"
recur(sym.owner) + separator + sym.javaSimpleName.toString
}
if (sym.isClass || sym.isModule) recur(sym)
else recur(sym.enclClass)
}
if (sym.isClass || sym.isModule) recur(sym)
else recur(sym.enclClass)
}
// [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)
// [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 (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 with classpath %s".format(implClass.getClassLoader, inferClasspath(implClass.getClassLoader)))
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 with classpath %s".format(implClass.getClassLoader, inferClasspath(implClass.getClassLoader)))
}
}
}
val implObjSymbol = implClassSymbol.companionModule
macroTraceVerbose("implObjSymbol is: ")(implObjSymbol.fullNameString)
val implObjSymbol = implClassSymbol.companionModule
macroTraceVerbose("implObjSymbol is: ")(implObjSymbol.fullNameString)
if (implObjSymbol == macroMirror.NoSymbol) 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
if (implObjSymbol == macroMirror.NoSymbol) None
else {
val implMethSymbol = implObjSymbol.info.member(macroMirror.newTermName(macroImpl.name.toString))
macroLogVerbose("implMethSymbol is: " + implMethSymbol.fullNameString)
macroLogVerbose("jimplMethSymbol is: " + macroMirror.methodToJava(implMethSymbol))
// 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 (implMethSymbol == macroMirror.NoSymbol) None
if (implObj == null) None
else {
macroLogVerbose("successfully loaded macro impl as (%s, %s)".format(implObj, implMethSymbol))
Some((implObj, implMethSymbol))
val implMethSymbol = implObjSymbol.info.member(macroMirror.newTermName(macroImpl.name.toString))
macroLogVerbose("implMethSymbol is: " + implMethSymbol.fullNameString)
macroLogVerbose("jimplMethSymbol is: " + macroMirror.methodToJava(implMethSymbol))
if (implMethSymbol == macroMirror.NoSymbol) None
else {
macroLogVerbose("successfully loaded macro impl as (%s, %s)".format(implObj, implMethSymbol))
Some((implObj, implMethSymbol))
}
}
}
} catch {
case ex: ClassNotFoundException =>
macroTraceVerbose("implementation class failed to load: ")(ex.toString)
None
}
} catch {
case ex: ClassNotFoundException =>
macroTraceVerbose("implementation class failed to load: ")(ex.toString)
None
}
}
val primary = loadMacroImpl(primaryMirror)
primary match {
case Some((implObj, implMethSymbol)) =>
def runtime(args: List[Any]) = primaryMirror.invoke(implObj, implMethSymbol)(args: _*).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)
fallback match {
case Some((implObj, implMethSymbol)) =>
def runtime(args: List[Any]) = fallbackMirror.invoke(implObj, implMethSymbol)(args: _*).asInstanceOf[Any]
Some(runtime _)
case None =>
None
val primary = loadMacroImpl(primaryMirror)
primary match {
case Some((implObj, implMethSymbol)) =>
def runtime(args: List[Any]) = primaryMirror.invoke(implObj, implMethSymbol)(args: _*).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)
fallback match {
case Some((implObj, implMethSymbol)) =>
def runtime(args: List[Any]) = fallbackMirror.invoke(implObj, implMethSymbol)(args: _*).asInstanceOf[Any]
Some(runtime _)
case None =>
None
}
} else {
None
}
} else {
None
}
}
}
}
if (runtime == None) macroDef setFlag IS_ERROR
runtime
})
if (runtime == None) macroDef setFlag IS_ERROR
runtime
})
}
}
/** Should become private again once we're done with migrating typetag generation from implicits */
def macroContext(typer: Typer, prefixTree: Tree, expandeeTree: Tree): MacroContext { val mirror: global.type } =
@@ -1203,8 +1241,10 @@ trait Macros extends Traces {
if (relevancyThreshold == -1) None
else {
var relevantElements = realex.getStackTrace().take(relevancyThreshold + 1)
var framesTillReflectiveInvocationOfMacroImpl = relevantElements.reverse.indexWhere(_.isNativeMethod) + 1
relevantElements = relevantElements dropRight framesTillReflectiveInvocationOfMacroImpl
def isMacroInvoker(este: StackTraceElement) = este.isNativeMethod || (este.getClassName != null && (este.getClassName contains "fastTrack"))
var threshold = relevantElements.reverse.indexWhere(isMacroInvoker) + 1
while (threshold != relevantElements.length && isMacroInvoker(relevantElements(relevantElements.length - threshold - 1))) threshold += 1
relevantElements = relevantElements dropRight threshold
realex.setStackTrace(relevantElements)
val message = new java.io.StringWriter()

0 comments on commit 364deb0

Please sign in to comment.