【发布时间】:2013-06-04 23:30:18
【问题描述】:
我正在用 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 的大量调用,并被告知我们已经杀死编译器。
我有a solution,它涉及将案例分成固定大小的组,为每个组创建一个单独的方法,并添加一个顶级匹配,将输入值分配给适当的组的方法。它有效,但令人不快,而且我不希望每次编写宏时都必须使用这种方法,其中生成的代码的大小取决于某些外部资源。
有没有更简洁的方法来解决这个问题?更重要的是,有没有办法更优雅地处理这种编译器错误?我不喜欢图书馆用户收到一个难以理解的“那个条目似乎已经杀死了编译器”错误消息的想法,只是因为某个宏正在处理的 XML 文件已经超过了一些(相当低的)大小阈值。
【问题讨论】:
-
这个问题已被标记为"already answered",但我所问的与该问题中所问的完全不同。我知道改变 JVM 的方法大小限制是不可能的——我询问的是 Scala 新 (2.10) 宏系统上下文中的变通方法和错误处理。
-
天真地尝试了 -optimise 和 2.11 只是坐在那里沉思。因为我在这个地球上的时间是有限的,所以我 ctl-c'd。也许这对我来说会变得很明显,为什么这必须以糟糕的方式结束。
-
@som-snytt:有趣——这里也一样,但不知道这意味着什么。如果没有
-optimize,至少 2.11.0-M3 确实给出了合理的错误消息。 -
Rex 对方法限制的一般解决方案不明智的评论或挑战:groups.google.com/forum/#!topic/scala-internals/f6PwUxc8K7I
-
请注意,您的方法在 32kb 之后将不会被 JIT (HotSpot) 编译。根据 HotSpot 开发人员 Vladimir Ivanov 的 JIT 编译器概述。 Slides (English),Video (Russian)。滑动 N58。 Vladimir 提到“太大”意味着大约 32kb。
标签: java scala macros jvm scala-macros