如何在 Scala 宏中获取方法主体树



我正在尝试将scala对象转换为js对象

object SObject {
def foo(in:String):String =  s"scalajs-$in" 
}
val o = ScalaObjectToJSObjectMacro(SObject) // js.Dynamical.literal(foo = (in:String) => s"scalajs-$in")

在宏中,我能够获取对象(in.tpe.decls.toList(的所有方法,然后对于每个方法名称,返回类型,参数在MethodSymbolApi中可用,但没有正文树:s

object ScalaObjectToJSObjectMacro {
def apply[T](in: T): js.Object = macro macroImpl
def macroImpl(c: blackbox.Context)(in: c.Tree): c.Tree = {
import c.universe._
val methods = in.tpe.decls.toList
.filter(s => {
s.isMethod && s.asMethod.isPublic && !s.asMethod.isConstructor
})
.map(m => {
val mt = m.asMethod
val name = mt.name
val returnType = mt.returnType
val params = mt.paramLists
val body = ??? //TODO
})
println(s"methods: ${methods}")
q"""
scala.scalajs.js.Dynamic.literal()
"""
}
}

我能够访问方法主体的一种方法是使用以下准引用:

q"..$mods def $ename[..$tparams](...$paramss): $tpeopt = $expr"

方法主体包含在expr中。请注意以下代码片段中如何通过模式匹配访问expr

def impl(c: blackbox.Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
import c.universe._
val result = {
annottees.map(_.tree).toList match {
case q"def $ename[..$tparams](...$paramss): $tpeopt = $expr" :: Nil =>
// do something with $expr
...
}
}
c.Expr[Any](result)
}

你可以从Tree获得Symbol,反之则不然。方法主体在此方法的Tree中,而不是在此TreeSymbol中。因此,您应该使用原始Treein而不是符号s。教程中介绍了遍历Trees。

尝试

def apply[T](in: T): js.Object = macro macroImpl
def macroImpl(c: blackbox.Context)(in: c.Tree): c.Tree = {
import c.universe._
object traverser extends Traverser {
var methods = List[Symbol]()
override def traverse(tree: Tree): Unit = tree match {
case t@DefDef(modifiers, name, typeParams, paramss, returnType, body) if name != termNames.CONSTRUCTOR && !modifiers.hasFlag(Flag.PRIVATE) =>
methods = t.symbol :: methods
// your logic with t, t.symbol, name, paramss, returnType, body etc.
super.traverseTrees(typeParams)
super.traverseTreess(paramss)
super.traverse(returnType)
super.traverse(body)
case _ => super.traverse(tree)
}
}
traverser.traverse(in)
println(s"methods: ${traverser.methods}")
q"""
scala.scalajs.js.Dynamic.literal()
"""
}

最新更新