scala.tools.nsc.backend.jvm.BytecodeWriters.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of scala-compiler Show documentation
Show all versions of scala-compiler Show documentation
Compiler for the Scala Programming Language
/* NSC -- new Scala compiler
* Copyright 2005-2013 LAMP/EPFL
* @author Paul Phillips
*/
package scala.tools.nsc
package backend.jvm
import java.io.{ DataOutputStream, FileOutputStream, OutputStream, File => JFile }
import scala.tools.nsc.io._
import scala.tools.nsc.util.ScalaClassLoader
import scala.tools.util.JavapClass
import java.util.jar.{ JarEntry, JarOutputStream, Attributes }
import Attributes.Name
import scala.language.postfixOps
/** For the last mile: turning generated bytecode in memory into
* something you can use. Has implementations for writing to class
* files, jars, and disassembled/javap output.
*/
trait BytecodeWriters {
val global: Global
import global._
private def outputDirectory(sym: Symbol): AbstractFile = (
settings.outputDirs.outputDirFor(beforeFlatten(sym.sourceFile))
)
private def getFile(base: AbstractFile, /*cls.getName()*/ clsName: String, suffix: String): AbstractFile = {
var dir = base
val pathParts = clsName.split("[./]").toList
for (part <- pathParts.init) {
dir = dir.subdirectoryNamed(part)
}
dir.fileNamed(pathParts.last + suffix)
}
private def getFile(sym: Symbol, clsName: String, suffix: String): AbstractFile =
getFile(outputDirectory(sym), clsName, suffix)
trait BytecodeWriter {
def writeClass(label: String, jclassName: String, jclassBytes: Array[Byte], sym: Symbol): Unit
def close(): Unit = ()
}
class DirectToJarfileWriter(jfile: JFile) extends BytecodeWriter {
val jarMainAttrs = (
if (settings.mainClass.isDefault) Nil
else List(Name.MAIN_CLASS -> settings.mainClass.value)
)
val writer = new Jar(jfile).jarWriter(jarMainAttrs: _*)
def writeClass(label: String, jclassName: String, jclassBytes: Array[Byte], sym: Symbol) {
val path = jclassName + ".class"
val out = writer.newOutputStream(path)
try out.write(jclassBytes, 0, jclassBytes.length)
finally out.flush()
informProgress("added " + label + path + " to jar")
}
override def close() = writer.close()
}
trait JavapBytecodeWriter extends BytecodeWriter {
val baseDir = Directory(settings.Ygenjavap.value).createDirectory()
def emitJavap(bytes: Array[Byte], javapFile: io.File) {
val pw = javapFile.printWriter()
val javap = new JavapClass(ScalaClassLoader.appLoader, pw) {
override def findBytes(path: String): Array[Byte] = bytes
}
try javap(Seq("-verbose", "dummy")) foreach (_.show())
finally pw.close()
}
abstract override def writeClass(label: String, jclassName: String, jclassBytes: Array[Byte], sym: Symbol) {
super.writeClass(label, jclassName, jclassBytes, sym)
val bytes = getFile(sym, jclassName, ".class").toByteArray
val segments = jclassName.split("[./]")
val javapFile = segments.foldLeft(baseDir: Path)(_ / _) changeExtension "javap" toFile;
javapFile.parent.createDirectory()
emitJavap(bytes, javapFile)
}
}
trait ClassBytecodeWriter extends BytecodeWriter {
def writeClass(label: String, jclassName: String, jclassBytes: Array[Byte], sym: Symbol) {
val outfile = getFile(sym, jclassName, ".class")
val outstream = new DataOutputStream(outfile.bufferedOutput)
try outstream.write(jclassBytes, 0, jclassBytes.length)
finally outstream.close()
informProgress("wrote '" + label + "' to " + outfile)
}
}
trait DumpBytecodeWriter extends BytecodeWriter {
val baseDir = Directory(settings.Ydumpclasses.value).createDirectory()
abstract override def writeClass(label: String, jclassName: String, jclassBytes: Array[Byte], sym: Symbol) {
super.writeClass(label, jclassName, jclassBytes, sym)
val pathName = jclassName
var dumpFile = pathName.split("[./]").foldLeft(baseDir: Path) (_ / _) changeExtension "class" toFile;
dumpFile.parent.createDirectory()
val outstream = new DataOutputStream(new FileOutputStream(dumpFile.path))
try outstream.write(jclassBytes, 0, jclassBytes.length)
finally outstream.close()
}
}
}