我想知道是否有办法绕过这个限制。显然,这个限制是 因为 JVM 而不是 Scala。此代码是自动生成的,因此无法将其拆分为多个方法。
scalac: Error while emitting M_SEMANT
Method too large: M_SEMANT.visit_1_1_0 (LNode;)V
我设法(几乎)编译了您自动生成的源代码。唯一可以打败代码生成器的是另一个代码生成器:)
此代码是自动生成的,因此无法将其拆分为多个方法。
我想这不完全正确。只是自动拆分代码比较好
我用过Scalameta。你的方法
visit_1_1_0
类 M_SEMANT
有很多分支 if (...) ... else ...
(以及你自动生成的代码的其余部分)。我把树枝if ($condExpr) $thenExpr else $elseExpr
改造成
def $condName(): Boolean = $condExpr
def $thenName() = $thenExpr
def $elseName() = $elseExpr
if ($condName()) $thenName() else $elseName()
所以
if
、then
和 else
成为嵌套方法。编译后,嵌套方法被移动到类级别
您可以对所有
if-else
递归应用此转换(然后您应该取消注释ScalametaTransformer
中的行)但事实证明,在if-else
中重写最外层的visit_1_1_0
就足够了。获得的方法似乎受 JVM 限制。
这是
sbt transformed/clean transform transformed/compile
的结果
matches M_SEMANT
matches template
matches visit_1_1_0
matches if-else
[error] /media/data/Projects1/cool-aps-semant1/cool-aps-semant/transformed/target/scala-2.13/src_managed/main/cool-semant.scala:31:15: type mismatch;
[error] found : Unit
[error] required: Int
[error] if (!cond) {
[error] ^
[error] /media/data/Projects1/cool-aps-semant1/cool-aps-semant/transformed/target/scala-2.13/src_managed/main/cool-semant.scala:25:11: type mismatch;
[error] found : Unit
[error] required: Int
[error] if (!cond) {
[error] ^
[error] two errors found
[error] (transformed / Compile / compileIncremental) Compilation failed
由于这两个地方在你的原始代码中是相同的,我猜那些编译错误存在于那里。但是我们没有
Method too large
了。
变换前的源在
src/main/scala
,变换后的源在transformed/target/scala-2.13/src_managed/main
.
project/build.sbt
libraryDependencies ++= Seq(
"org.scalameta" %% "scalameta" % "4.7.7"
)
build.sbt
ThisBuild / scalaVersion := "2.13.10"
lazy val root = (project in file("."))
.settings(
name := "aps-cool"
)
lazy val transformed = project
.settings(
Compile / unmanagedSourceDirectories += (Compile / sourceManaged).value
)
val transform = taskKey[Unit]("Transform sources")
transform := {
val inputDir = (root / Compile / scalaSource).value
val outputDir = (transformed / Compile / sourceManaged).value
Generator.gen(inputDir, outputDir, Seq("cool-semant.scala"), Map("cool-semant.scala" -> Seq(960)))
}
project/Generator.scala
import sbt.*
object Generator {
def gen(inputDir: File, outputDir: File, filesToTransform: Seq[String] = Seq(), emptyLineIndices: Map[String, Seq[Int]] = Map()): Unit = {
val finder: PathFinder = inputDir ** "*.scala"
for (inputFile <- finder.get) yield {
val inputStr = IO.read(inputFile)
val outputFile = outputDir / inputFile.relativeTo(inputDir).get.toString
val outputStr =
if (filesToTransform.isEmpty || filesToTransform.contains(inputFile.name))
ScalametaTransformer.transform(inputStr, emptyLineIndices.getOrElse(inputFile.name, Seq()))
else inputStr
IO.write(outputFile, outputStr)
}
}
}
项目/ScalametaTransformer.scala
import scala.meta.*
import scala.util.Properties
object ScalametaTransformer {
private val ifElseTransformer = new Transformer {
override def apply(tree: Tree): Tree = tree match {
case q"if ($condExpr) $thenExpr else $elseExpr" =>
println(s"matches if-else")
val condName = Term.fresh("cond")
val thenName = Term.fresh("then")
val elseName = Term.fresh("else")
q"""
def $condName(): Boolean = $condExpr
def $thenName() = $thenExpr
def $elseName() = $elseExpr
if ($condName()) $thenName() else $elseName()
"""
// apply recursively:
// val condExpr1 = super.apply(condExpr).asInstanceOf[Term]
// val thenExpr1 = super.apply(thenExpr).asInstanceOf[Term]
// val elseExpr1 = super.apply(elseExpr).asInstanceOf[Term]
// q"""
// def $condName() = $condExpr1
// def $thenName() = $thenExpr1
// def $elseName() = $elseExpr1
// if ($condName()) $thenName() else $elseName()
// """
case _ => super.apply(tree)
}
}
private val classMethodTransformer = new Transformer {
override def apply(tree: Tree): Tree = tree match {
case q"..$mods class M_SEMANT[..$tparams] ..$ctorMods (...$paramss) $template" =>
println("matches M_SEMANT")
val template1 = template match {
case template"{ ..$earlyStats } with ..$inits { $self => ..$stats }" =>
println("matches template")
val stats1 = stats.map {
case q"..$mods def visit_1_1_0[..$tparams](...$paramss): $tpeopt = $expr" =>
println("matches visit_1_1_0")
val expr1 = ifElseTransformer(expr).asInstanceOf[Term]
q"..$mods def visit_1_1_0[..$tparams](...$paramss): $tpeopt = $expr1"
case t => t
}
template"{ ..$earlyStats } with ..$inits { $self => ..$stats1 }"
}
q"..$mods class M_SEMANT[..$tparams] ..$ctorMods (...$paramss) $template1"
case _ => super.apply(tree)
}
}
def transform(str: String, emptyLineIndices: Seq[Int] = Seq()): String = {
val origTree = str.parse[Source].get
val newTree = classMethodTransformer(origTree)
insertEmptyLines(newTree.toString, emptyLineIndices)
}
private def insertEmptyLines(str: String, indices: Seq[Int]): String =
indices.foldLeft(str)(insertEmptyLine)
private def insertEmptyLine(str: String, index: Int): String =
str.linesIterator.patch(index, Iterator.fill(1)(""), 0).mkString(Properties.lineSeparator)
}
的解决方法