8

在 Scala 2.10 中是否有一种方便的方法可以将 aMethodSymbol转换为方法定义树(即 a )的左侧?DefDef

例如,假设我想创建一个宏,该宏将采用一个 trait 的实例,并用一些调试功能包装该 trait 的所有方法。我可以写以下内容:

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

  //...
}

我已经省略了将这些方法粘贴到实现该特征的新匿名类中然后实例化该类的无聊业务——如果您有兴趣,可以在此处找到一个完整的工作示例。

现在我可以写这个,例如:

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

所以它有效,但仅在非常简单的情况下——如果 trait 具有带有参数列表、访问修饰符、注释等的方法,它就不会。

我真正想要的是一个函数,它将为新主体采用方法符号和树并返回一个DefDef. 我已经开始手写一个,但它涉及到很多像这样的繁琐的东西:

List(if (method.isImplicit) Some(Flag.IMPLICIT) else None, ...)

这很烦人,冗长且容易出错。我是否错过了在新的反射 API 中执行此操作的更好方法?

4

2 回答 2

4

据我所知,从符号到定义树没有标准的方法。

你最好的选择可能是遍历c.enclosingRun.units,在你去的时候递归到每unit.body棵树。如果您看到一个DefDef等于symbol您的符号的 ,那么您已经到达目的地。更新。duplicate在重用它之前不要忘记定义树!

这种技术远非世界上最方便的东西,但它应该有效。

于 2012-12-07T18:07:04.093 回答
2

您可以尝试以下方法。这适用于多个参数和咖喱函数和类型参数;)

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
于 2012-12-08T02:28:35.957 回答