Reputation: 993
I am trying to write a proxy macro using scala macros. I want to be able to proxy a trait X and return instances of X that invoke a function for all methods of X.
Here is what I did so far. Say we want to proxy the trait TheTrait (which is defined below), we can run ProxyMacro.proxy passing a function that will be called for all invocations of the proxy methods.
trait TheTrait
{
def myMethod(x: String)(y: Int): String
}
val proxy = ProxyMacro.proxy[TheTrait] {
case ("myMethod", args) =>
"ok"
}
println(proxy.myMethod("hello")(5))
The implementation so far is this:
package macrotests
import scala.language.experimental.macros
import scala.reflect.macros.whitebox.Context
object ProxyMacro
{
type Implementor = (String, Any) => Any
def proxy[T](implementor: Implementor): T = macro impl[T]
def impl[T: c.WeakTypeTag](c: Context)(implementor: c.Expr[Implementor]): c.Expr[T] = {
import c.universe._
val tpe = weakTypeOf[T]
val decls = tpe.decls.map { decl =>
val termName = decl.name.toTermName
val method = decl.asMethod
val params = method.paramLists.map(_.map(s => internal.valDef(s)))
val paramVars = method.paramLists.flatMap(_.map { s =>
internal.captureVariable(s)
internal.referenceCapturedVariable(s)
})
q""" def $termName (...$params) = {
$implementor (${termName.toString}, List(..${paramVars}) ).asInstanceOf[${method.returnType}]
}"""
}
c.Expr[T] {
q"""
new $tpe {
..$decls
}
"""
}
}
}
But there is a problem. This doesn't compile due to List(..${paramVars}). This should just create a list with all the values of the method arguments. But I get a compilation issue (not worth pasting it) on that line.
How can I convert the list of method arguments to their values?
Upvotes: 0
Views: 207
Reputation: 1384
showInfo is useful when you debug macro
def showInfo(s: String) =
c.info(c.enclosingPosition, s.split("\n").mkString("\n |---macro info---\n |", "\n |", ""), true)
change
val paramVars = method.paramLists.flatMap(_.map { s =>
internal.captureVariable(s)
internal.referenceCapturedVariable(s)
})
(this result is List(x0$1, x1$1)
)
to
val paramVars = method.paramLists.flatMap(_.map { s =>
s.name
})
(this result is List(x, y)
)
Upvotes: 2