1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
|
/* 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.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(enteringFlatten(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
val 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()
}
}
}
|