是否有方便的方法将MethodSymbol
转换为Scala 2.10中方法定义树的左侧(即DefDef
)?
例如,假设我想创建一个宏来获取特征的实例,并用一些调试功能包装所有特征的方法。我可以写下面的内容:
import scala.language.experimental.macros
import scala.reflect.macros.Context
object WrapperExample {
def wrap[A](a: A): A = macro wrap_impl[A]
def wrap_impl[A: c.WeakTypeTag](c: Context)(a: c.Expr[A]) = {
import c.universe._
val wrapped = weakTypeOf[A]
val f = Select(reify(Predef).tree, "println")
val methods = wrapped.declarations.collect {
case m: MethodSymbol if !m.isConstructor => DefDef(
Modifiers(Flag.OVERRIDE),
m.name,
Nil, Nil,
TypeTree(),
Block(
Apply(f, c.literal("Calling: " + m.name.decoded).tree :: Nil),
Select(a.tree, m.name)
)
)
}.toList
//...
}
我已经省去了无聊的业务,将这些方法粘贴在一个新的匿名类中,该类实现了特性,然后实例化了该类 - 如果你有兴趣,你可以找到一个完整的工作示例here。
现在我可以写这个,例如:
scala> trait X { def foo = 1; def bar = 'a }
defined trait X
scala> val x = new X {}
x: X = $anon$1@15dd533
scala> val w: X = WrapperExample.wrap[X](x)
w: X = $1$$1@27c3a4a3
scala> w.foo
Calling: foo
res0: Int = 1
scala> w.bar
Calling: bar
res1: Symbol = 'a
所以它有效,但只是在非常简单的情况下 - 如果特征具有带参数列表的方法,具有访问修饰符,注释等,则不会。
我真正想要的是一个函数,它将为新主体采用方法符号和树并返回DefDef
。我已经开始手工编写,但它涉及很多这样的繁琐的东西:
List(if (method.isImplicit) Some(Flag.IMPLICIT) else None, ...)
这是烦人的,冗长的,容易出错的。我在新的Reflection API中错过了一些更好的方法吗?
答案 0 :(得分:4)
据我所知,从符号到定义树没有标准的方法。
你最好的选择可能是迭代c.enclosingRun.units
,然后在你去的时候递归到每个unit.body
树。如果您看到DefDef
,其symbol
等于您的符号,那么您已到达目的地。 UPD。在重复使用之前,不要忘记duplicate
定义树!
这种技术远非世界上最方便的东西,但它应该有用。
答案 1 :(得分:2)
您可以尝试以下方法。这适用于多个参数和curried函数和类型参数;)
val methods = wrapped.declarations.collect {
case m: MethodSymbol if !m.isConstructor => DefDef(
Modifiers(Flag.OVERRIDE),
m.name,
m.typeParams.map(TypeDef(_)),
m.paramss.map(_.map(ValDef(_))),
TypeTree(m.returnType),
Block(
Apply(f, c.literal("Calling: " + m.name.decoded).tree :: Nil),
m.paramss.foldLeft(Select(a.tree.duplicate, m.name): Tree)((prev, params) =>
Apply(prev, params.map(p => Ident(p.name)))
)
)
)
}.toList