2

我正在使用 scala 2.10 的工具箱 api 在运行时从 AST 编译和构造类。我有一个特性的 AST,我想编译它并将其混合到未来的类定义中,但无法获得将其应用于我的类的任何句柄。

scala> import scala.reflect.runtime.universe._
import scala.reflect.runtime.universe._

scala> import scala.tools.reflect.ToolBox
import scala.tools.reflect.ToolBox

scala> val tb = scala.reflect.runtime.currentMirror.mkToolBox()
tb: scala.tools.reflect.ToolBox[reflect.runtime.universe.type] = scala.tools.reflect.ToolBoxFactory$ToolBoxImpl@7469d79a

scala> val ast_trait = tb.parse("trait T")//My AST is manually constructed starting with ClassDef
ast_trait: tb.u.Tree = abstract trait T extends scala.AnyRef

//compile and execute the tree
scala> tb.eval(ast_trait)
res0: Any = ()

//attempt to define a class that 
scala> tb.eval(tb.parse("class C extends T"))implements T. In my code this is a constructed AST
scala.tools.reflect.ToolBoxError: reflective compilation has failed:

not found: type T
/*stack trace*/

这会失败,因为当使用工具箱编译类/特征时,它们会被包装并命名为 __wrapper$[wrapCount]$[uuid]$[name]。

问题出现了如何获得要与类定义一起使用的已定义特征的句柄。

显而易见的解决方案是scala.reflect.runtime.universe.Type通过typeOf[T].

//Obtain an AST via tb.parse
scala> val ast_type = tb.parse("scala.reflect.runtime.universe.typeOf[T]")
ast_type: tb.u.Tree = scala.reflect.runtime.universe.typeOf[T]

//eval the trait defintion and typeOf in the same block
scala> tb.eval(Block(ast_trait, ast_type))
warning: there were 1 deprecation warning(s); re-run with -deprecation for details
scala.tools.reflect.ToolBoxError: reflective compilation has failed:

No TypeTag available for T
not enough arguments for method typeOf: (implicit ttag: reflect.runtime.universe.TypeTag[T])reflect.runtime.universe.Type.
Unspecified value parameter ttag.
/*stack trace*/

我不完全确定为什么会失败,但我之前没有成功地以任何形式使用隐式类型标签和工具箱编译。

//A simpler all in one example
scala> tb.eval(tb.parse("trait T; scala.reflect.runtime.universe.typeOf[T]"))
scala.tools.reflect.ToolBoxError: reflective compilation has failed:

No TypeTag available for T
not enough arguments for method typeOf: (implicit ttag: reflect.runtime.universe.TypeTag[T])reflect.runtime.universe.Type.
Unspecified value parameter ttag.
/*stack trace*/

有没有办法禁用包装,以便使用 AST 中指定的名称定义特征?

如果没有,如何获得特征的全名?

更新: using 的建议classOf就像一个魅力,直到特性实际应用于一个类。

scala> val mirror = scala.reflect.runtime.currentMirror

scala> val clazz = tb.eval(tb.parse("trait T{def m = 0}; classOf[T]")).asInstanceOf[Class[_]]
clazz: Class[_] = interface __wrapper$5$36e5027b48a44570a47be32d536aa67d.__wrapper$5$36e5027b48a44570a47be32d536aa67d$T$1
scala> val tpe = mirror.classSymbol(clazz)
tpe: reflect.runtime.universe.ClassSymbol = trait T$1

scala> val tree = ClassDef(Modifiers(), "C", List(), Template(
     | List(Ident(tpe)),
     | emptyValDef,
     | List()))
tree: reflect.runtime.universe.ClassDef = class C extends T$1

scala> tb.eval(tree)
scala.tools.reflect.ToolBoxError: reflective compilation has failed:

class C needs to be abstract, since method m in trait T$1 of type ()Int is not defined
/*stack trace*/

似乎特征的实现正在丢失,它只是作为一个接口应用(这是由 classOf 返回的 java 类定义的)。

我假设工具箱的包装器正在使用新的 uuid 重命名用于实现 trait 的 java 类(通常具有相同的类名,后跟“$”),然后编译器无法找到它并假设它只是一个接口。

如何将具有实现的特征应用于类?

4

0 回答 0