Scala 宏和 JVM 的方法大小限制
我正在用 Scala 宏替换 Java 程序中的一些代码生成组件,并且遇到了 Java 虚拟机对单个方法生成的字节代码大小(64 KB)的限制。
例如,假设我们有一个大 XML 文件,它表示要在程序中使用的从整数到整数的映射。我们希望避免在运行时解析此文件,因此我们将编写一个宏,该宏将在编译时执行解析,并使用文件的内容来创建我们方法的主体:
import scala.language.experimental.macros
import scala.reflect.macros.Context
object BigMethod {
// For this simplified example we'll just make some data up.
val mapping = List.tabulate(7000)(i => (i, i + 1))
def lookup(i: Int): Int = macro lookup_impl
def lookup_impl(c: Context)(i: c.Expr[Int]): c.Expr[Int] = {
import c.universe._
val switch = reify(new scala.annotation.switch).tree
val cases = mapping map {
case (k, v) => CaseDef(c.literal(k).tree, EmptyTree, c.literal(v).tree)
}
c.Expr(Match(Annotated(switch, i.tree), cases))
}
}
在这种情况下,编译的方法将刚刚超过大小限制,但不是一个很好的错误说,我们得到了一个巨大的堆栈跟踪,其中包含许多调用,并被告知我们已经杀死了编译器。TreePrinter.printSeq
我有一个解决方案,涉及将事例拆分为固定大小的组,为每个组创建一个单独的方法,并添加一个顶级匹配项,将输入值调度到相应组的方法。它可以工作,但它令人不快,我宁愿每次编写宏时都不必使用此方法,其中生成的代码的大小取决于某些外部资源。
有没有更清洁的方法来解决这个问题?更重要的是,有没有办法更优雅地处理这种编译器错误?我不喜欢库用户仅仅因为宏正在处理的某些 XML 文件已越过某个(相当低的)大小 threshhold 而收到难以理解的“该条目似乎杀死了编译器”错误消息的想法。