diff options
author | Miguel Garcia <miguelalfredo.garcia@epfl.ch> | 2013-03-24 18:03:44 +0100 |
---|---|---|
committer | Miguel Garcia <miguelalfredo.garcia@epfl.ch> | 2013-04-27 10:56:27 +0200 |
commit | 38f426d2ae2058b44ac96d5b6618d7552a23c359 (patch) | |
tree | 6dfd08860b3e7f2fb6aa225c736dea90feddd2e3 | |
parent | 0c5e2e8f577165ac622a93e0b407f272130f0f37 (diff) | |
download | scala-38f426d2ae2058b44ac96d5b6618d7552a23c359.tar.gz scala-38f426d2ae2058b44ac96d5b6618d7552a23c359.tar.bz2 scala-38f426d2ae2058b44ac96d5b6618d7552a23c359.zip |
compiler flag -Ygen-asmp to emit .asmp textual files for bytecode
This commit adds the -Ygen-asmp compiler flag, similar in purpose to what -Ygen-javap used to be.
The ASM-based counterpart to javap is useful even in those cases where javap is available,
as it produces for example readable (textual) output for pickles.
Additionally, javap displays quite differently two constant pools that have identical contents
(javap shows their physical layout, not just the information the entries hold).
Finally, stack maps (classfile version 50 and up) are displayed in encoded form by javap, their expansion by ASM is more readable.
3 files changed, 50 insertions, 10 deletions
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala b/src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala index 66aed14d1c..e20b75ed13 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala @@ -41,6 +41,17 @@ trait BytecodeWriters { private def getFile(sym: Symbol, clsName: String, suffix: String): AbstractFile = getFile(outputDirectory(sym), clsName, suffix) + def factoryNonJarBytecodeWriter(): BytecodeWriter = { + val emitAsmp = settings.Ygenasmp.isSetByUser + val doDump = settings.Ydumpclasses.isSetByUser + (emitAsmp, doDump) match { + case (false, false) => new ClassBytecodeWriter { } + case (false, true ) => new ClassBytecodeWriter with DumpBytecodeWriter { } + case (true, false) => new ClassBytecodeWriter with AsmpBytecodeWriter + case (true, true ) => new ClassBytecodeWriter with AsmpBytecodeWriter with DumpBytecodeWriter { } + } + } + trait BytecodeWriter { def writeClass(label: String, jclassName: String, jclassBytes: Array[Byte], sym: Symbol): Unit def close(): Unit = () @@ -65,6 +76,43 @@ trait BytecodeWriters { override def close() = writer.close() } + /* + * The ASM textual representation for bytecode overcomes disadvantages of javap ouput in three areas: + * (a) pickle dingbats undecipherable to the naked eye; + * (b) two constant pools, while having identical contents, are displayed differently due to physical layout. + * (c) stack maps (classfile version 50 and up) are displayed in encoded form by javap, + * their expansion by ASM is more readable. + * + * */ + trait AsmpBytecodeWriter extends BytecodeWriter { + import scala.tools.asm + + private val baseDir = Directory(settings.Ygenasmp.value).createDirectory() + + private def emitAsmp(jclassBytes: Array[Byte], asmpFile: io.File) { + val pw = asmpFile.printWriter() + try { + val cnode = new asm.tree.ClassNode() + val cr = new asm.ClassReader(jclassBytes) + cr.accept(cnode, 0) + val trace = new scala.tools.asm.util.TraceClassVisitor(new java.io.PrintWriter(new java.io.StringWriter())) + cnode.accept(trace) + trace.p.print(pw) + } + finally pw.close() + } + + abstract override def writeClass(label: String, jclassName: String, jclassBytes: Array[Byte], sym: Symbol) { + super.writeClass(label, jclassName, jclassBytes, sym) + + val segments = jclassName.split("[./]") + val asmpFile = segments.foldLeft(baseDir: Path)(_ / _) changeExtension "asmp" toFile; + + asmpFile.parent.createDirectory() + emitAsmp(jclassBytes, asmpFile) + } + } + trait ClassBytecodeWriter extends BytecodeWriter { def writeClass(label: String, jclassName: String, jclassBytes: Array[Byte], sym: Symbol) { val outfile = getFile(sym, jclassName, ".class") diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala index db43e633fc..133db77a0c 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala @@ -75,16 +75,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { new DirectToJarfileWriter(f.file) - case _ => - if (settings.Ydumpclasses.isDefault) - new ClassBytecodeWriter { } - else - new ClassBytecodeWriter with DumpBytecodeWriter { } - // TODO A ScalapBytecodeWriter could take asm.util.Textifier as starting point. - // Three areas where javap ouput is less than ideal (e.g. when comparing versions of the same classfile) are: - // (a) unreadable pickle; - // (b) two constant pools, while having identical contents, are displayed differently due to physical layout. - // (c) stack maps (classfile version 50 and up) are displayed in encoded form by javap, their expansion makes more sense instead. + case _ => factoryNonJarBytecodeWriter() } } diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index ee9a3aed2a..af5a1e852b 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -156,6 +156,7 @@ trait ScalaSettings extends AbsScalaSettings val Yshowsymkinds = BooleanSetting ("-Yshow-symkinds", "Print abbreviated symbol kinds next to symbol names.") val skip = PhasesSetting ("-Yskip", "Skip") val Ygenjavap = StringSetting ("-Ygen-javap", "dir", "Generate a parallel output directory of .javap files.", "") + val Ygenasmp = StringSetting ("-Ygen-asmp", "dir", "Generate a parallel output directory of .asmp files (ie ASM Textifier output).", "") val Ydumpclasses = StringSetting ("-Ydump-classes", "dir", "Dump the generated bytecode to .class files (useful for reflective compilation that utilizes in-memory classloaders).", "") val Ynosqueeze = BooleanSetting ("-Yno-squeeze", "Disable creation of compact code in matching.") val Ystatistics = BooleanSetting ("-Ystatistics", "Print compiler statistics.") andThen (scala.reflect.internal.util.Statistics.enabled = _) |