summaryrefslogblamecommitdiff
path: root/src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala
blob: 0bc41b51bba719b43e96569731466eb64400e060 (plain) (tree)
1
2
3
4
5
6
7
8
9








                                
                                                                                  
                           
                                            
                                  

                                                              
                          









                                                                    
                                                                   
















                                                                                        






                                                                    

                                                                




                                             

                                                         
                                         


                                                    
                                                                       


                                                           
                                                                  










                                                                                  
                                                                                             




                                        
 









                                                                  
 

                                                                          
 

                                                                                  
 



                                                                                                           
 



                                  
 
/* NSC -- new Scala compiler
 * Copyright 2005-2011 LAMP/EPFL
 * @author  Paul Phillips
 */

package scala.tools.nsc
package backend.jvm

import ch.epfl.lamp.fjbg._
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 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: JClass, suffix: String): AbstractFile = {
    var dir = base
    val pathParts = cls.getName().split("[./]").toList
    for (part <- pathParts.init) {
      dir = dir.subdirectoryNamed(part)
    }
    dir.fileNamed(pathParts.last + suffix)
  }
  private def getFile(sym: Symbol, cls: JClass, suffix: String): AbstractFile =
    getFile(outputDirectory(sym), cls, suffix)

  trait BytecodeWriter {
    def writeClass(label: String, jclass: JClass, 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, jclass: JClass, sym: Symbol) {
      val path = jclass.getName + ".class"
      val out  = writer.newOutputStream(path)

      try jclass writeTo out
      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, jclass: JClass, sym: Symbol) {
      super.writeClass(label, jclass, sym)

      val bytes     = getFile(sym, jclass, ".class").toByteArray
      val segments  = jclass.getName().split("[./]")
      val javapFile = segments.foldLeft(baseDir: Path)(_ / _) changeExtension "javap" toFile;

      javapFile.parent.createDirectory()
      emitJavap(bytes, javapFile)
    }
  }

  trait ClassBytecodeWriter extends BytecodeWriter {
    def writeClass(label: String, jclass: JClass, sym: Symbol) {
      val outfile   = getFile(sym, jclass, ".class")
      val outstream = new DataOutputStream(outfile.bufferedOutput)

      try jclass writeTo outstream
      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, jclass: JClass, sym: Symbol) {
      super.writeClass(label, jclass, sym)

      val pathName = jclass.getName()
      var dumpFile = pathName.split("[./]").foldLeft(baseDir: Path) (_ / _) changeExtension "class" toFile;
      dumpFile.parent.createDirectory()
      val outstream = new DataOutputStream(new FileOutputStream(dumpFile.path))

      try jclass writeTo outstream
      finally outstream.close()
    }
  }
}