diff options
author | Miguel Garcia <miguelalfredo.garcia@epfl.ch> | 2012-07-01 18:08:39 +0200 |
---|---|---|
committer | Miguel Garcia <miguelalfredo.garcia@epfl.ch> | 2012-07-01 18:08:39 +0200 |
commit | 5d12fa4b791e73d5c99a0e145d28cbaba12823d2 (patch) | |
tree | e8c981e5ed3e587dc955a1d9c46216e5eb9da038 /src/compiler | |
parent | 0a9cea6ea58571deb67d60193d60d12be6bb0233 (diff) | |
download | scala-5d12fa4b791e73d5c99a0e145d28cbaba12823d2.tar.gz scala-5d12fa4b791e73d5c99a0e145d28cbaba12823d2.tar.bz2 scala-5d12fa4b791e73d5c99a0e145d28cbaba12823d2.zip |
GenASM: pipeline disk-write with building of classfiles
Diffstat (limited to 'src/compiler')
3 files changed, 91 insertions, 35 deletions
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala b/src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala index ff68aba845..42921e733d 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala @@ -37,7 +37,7 @@ trait BytecodeWriters { getFile(outputDirectory(sym), clsName, suffix) trait BytecodeWriter { - def writeClass(label: String, jclassName: String, jclassBytes: Array[Byte], sym: Symbol): Unit + def writeClass(label: String, jclassName: String, jclassBytes: Array[Byte], outfile: AbstractFile): Unit def close(): Unit = () } @@ -48,7 +48,9 @@ trait BytecodeWriters { ) val writer = new Jar(jfile).jarWriter(jarMainAttrs: _*) - def writeClass(label: String, jclassName: String, jclassBytes: Array[Byte], sym: Symbol) { + def writeClass(label: String, jclassName: String, jclassBytes: Array[Byte], outfile: AbstractFile) { + assert(outfile == null, + "The outfile formal param is there just because ClassBytecodeWriter overrides this method and uses it.") val path = jclassName + ".class" val out = writer.newOutputStream(path) @@ -72,21 +74,21 @@ trait BytecodeWriters { 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) + abstract override def writeClass(label: String, jclassName: String, jclassBytes: Array[Byte], outfile: AbstractFile) { + super.writeClass(label, jclassName, jclassBytes, outfile) - 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) + emitJavap(jclassBytes, javapFile) } } trait ClassBytecodeWriter extends BytecodeWriter { - def writeClass(label: String, jclassName: String, jclassBytes: Array[Byte], sym: Symbol) { - val outfile = getFile(sym, jclassName, ".class") + def writeClass(label: String, jclassName: String, jclassBytes: Array[Byte], outfile: AbstractFile) { + assert(outfile != null, + "Precisely this override requires its invoker to hand out a non-null AbstractFile.") val outstream = new DataOutputStream(outfile.bufferedOutput) try outstream.write(jclassBytes, 0, jclassBytes.length) @@ -98,8 +100,8 @@ trait BytecodeWriters { 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) + abstract override def writeClass(label: String, jclassName: String, jclassBytes: Array[Byte], outfile: AbstractFile) { + super.writeClass(label, jclassName, jclassBytes, outfile) val pathName = jclassName var dumpFile = pathName.split("[./]").foldLeft(baseDir: Path) (_ / _) changeExtension "class" toFile; diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala index 59adcc637a..7ca49bd3f4 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala @@ -29,6 +29,10 @@ abstract class GenASM extends SubComponent with BytecodeWriters { val phaseName = "jvm"
+ case class WorkUnit(label: String, jclassName: String, jclass: asm.ClassWriter, outF: AbstractFile)
+
+ type WorkUnitQueue = _root_.java.util.concurrent.LinkedBlockingQueue[WorkUnit]
+
/** Create a new phase */
override def newPhase(p: Phase): Phase = new AsmPhase(p)
@@ -148,6 +152,44 @@ abstract class GenASM extends SubComponent with BytecodeWriters { }
}
+ // -----------------------------------------------------------------------------------------
+ // Allow overlapping disk write of classfiles with building of the next classfiles.
+ // -----------------------------------------------------------------------------------------
+
+ val q = new WorkUnitQueue(500)
+
+ class WriteTask(bytecodeWriter: BytecodeWriter) extends _root_.java.lang.Runnable {
+
+ def run() {
+ var stop = false
+ try {
+ while (!stop) {
+ val WorkUnit(label, jclassName, jclass, outF) = q.take
+ if(jclass eq null) { stop = true }
+ else { writeIfNotTooBig(label, jclassName, jclass, outF) }
+ }
+ } catch {
+ case ex: InterruptedException => throw ex
+ }
+ }
+
+ private def writeIfNotTooBig(label: String, jclassName: String, jclass: asm.ClassWriter, outF: AbstractFile) {
+ try {
+ val arr = jclass.toByteArray()
+ bytecodeWriter.writeClass(label, jclassName, arr, outF)
+ } catch {
+ case e: java.lang.RuntimeException if(e.getMessage() == "Class file too large!") =>
+ // TODO check where ASM throws the equivalent of CodeSizeTooBigException
+ log("Skipped class "+jclassName+" because it exceeds JVM limits (it's too big or has methods that are too long).")
+ }
+ }
+
+ }
+
+ // -----------------------------------------------------------------------------------------
+ // what AsmPhase does.
+ // -----------------------------------------------------------------------------------------
+
override def run() {
if (settings.debug.value)
@@ -161,10 +203,14 @@ abstract class GenASM extends SubComponent with BytecodeWriters { var sortedClasses = classes.values.toList sortBy ("" + _.symbol.fullName)
debuglog("Created new bytecode generator for " + classes.size + " classes.")
- val bytecodeWriter = initBytecodeWriter(sortedClasses filter isJavaEntryPoint)
- val plainCodeGen = new JPlainBuilder(bytecodeWriter)
- val mirrorCodeGen = new JMirrorBuilder(bytecodeWriter)
- val beanInfoCodeGen = new JBeanInfoBuilder(bytecodeWriter)
+ val bytecodeWriter = initBytecodeWriter(sortedClasses filter isJavaEntryPoint)
+ val needsOutfileForSymbol = bytecodeWriter.isInstanceOf[ClassBytecodeWriter]
+
+ val plainCodeGen = new JPlainBuilder(q, needsOutfileForSymbol)
+ val mirrorCodeGen = new JMirrorBuilder(q, needsOutfileForSymbol)
+ val beanInfoCodeGen = new JBeanInfoBuilder(q, needsOutfileForSymbol)
+
+ new _root_.java.lang.Thread(new WriteTask(bytecodeWriter)).start()
while(!sortedClasses.isEmpty) {
val c = sortedClasses.head
@@ -187,6 +233,10 @@ abstract class GenASM extends SubComponent with BytecodeWriters { classes -= c.symbol // GC opportunity
}
+ q put WorkUnit(null, null, null, null)
+
+ while(!q.isEmpty) { _root_.java.lang.Thread.sleep(10) }
+
bytecodeWriter.close()
classes.clear()
reverseJavaName.clear()
@@ -448,7 +498,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters { val JAVA_LANG_STRING = asm.Type.getObjectType("java/lang/String")
/** basic functionality for class file building */
- abstract class JBuilder(bytecodeWriter: BytecodeWriter) {
+ abstract class JBuilder(wuQ: WorkUnitQueue, needsOutfileForSymbol: Boolean) {
val EMPTY_JTYPE_ARRAY = Array.empty[asm.Type]
val EMPTY_STRING_ARRAY = Array.empty[String]
@@ -504,17 +554,6 @@ abstract class GenASM extends SubComponent with BytecodeWriters { // utitilies useful when emitting plain, mirror, and beaninfo classes.
// -----------------------------------------------------------------------------------------
- def writeIfNotTooBig(label: String, jclassName: String, jclass: asm.ClassWriter, sym: Symbol) {
- try {
- val arr = jclass.toByteArray()
- bytecodeWriter.writeClass(label, jclassName, arr, sym)
- } catch {
- case e: java.lang.RuntimeException if(e.getMessage() == "Class file too large!") =>
- // TODO check where ASM throws the equivalent of CodeSizeTooBigException
- log("Skipped class "+jclassName+" because it exceeds JVM limits (it's too big or has methods that are too long).")
- }
- }
-
/** Specialized array conversion to prevent calling
* java.lang.reflect.Array.newInstance via TraversableOnce.toArray
*/
@@ -728,11 +767,18 @@ abstract class GenASM extends SubComponent with BytecodeWriters { }
}
+ def enqueue(label: String, jclassName: String, jclass: asm.ClassWriter, sym: Symbol) {
+ val outF: scala.tools.nsc.io.AbstractFile = {
+ if(needsOutfileForSymbol) getFile(sym, jclassName, ".class") else null
+ }
+ wuQ put WorkUnit(label, jclassName, jclass, outF)
+ }
+
} // end of class JBuilder
/** functionality for building plain and mirror classes */
- abstract class JCommonBuilder(bytecodeWriter: BytecodeWriter) extends JBuilder(bytecodeWriter) {
+ abstract class JCommonBuilder(wuQ: WorkUnitQueue, needsOutfileForSymbol: Boolean) extends JBuilder(wuQ, needsOutfileForSymbol) {
// -----------------------------------------------------------------------------------------
// more constants
@@ -1290,8 +1336,8 @@ abstract class GenASM extends SubComponent with BytecodeWriters { case class BlockInteval(start: BasicBlock, end: BasicBlock)
/** builder of plain classes */
- class JPlainBuilder(bytecodeWriter: BytecodeWriter)
- extends JCommonBuilder(bytecodeWriter)
+ class JPlainBuilder(wuQ: WorkUnitQueue, needsOutfileForSymbol: Boolean)
+ extends JCommonBuilder(wuQ, needsOutfileForSymbol)
with JAndroidBuilder {
val MIN_SWITCH_DENSITY = 0.7
@@ -1445,7 +1491,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters { addInnerClasses(clasz.symbol, jclass)
jclass.visitEnd()
- writeIfNotTooBig("" + c.symbol.name, thisName, jclass, c.symbol)
+ enqueue("" + c.symbol.name, thisName, jclass, c.symbol)
}
@@ -2863,7 +2909,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters { /** builder of mirror classes */
- class JMirrorBuilder(bytecodeWriter: BytecodeWriter) extends JCommonBuilder(bytecodeWriter) {
+ class JMirrorBuilder(wuQ: WorkUnitQueue, needsOutfileForSymbol: Boolean) extends JCommonBuilder(wuQ, needsOutfileForSymbol) {
private var cunit: CompilationUnit = _
def getCurrentCUnit(): CompilationUnit = cunit;
@@ -2907,7 +2953,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters { addInnerClasses(modsym, mirrorClass)
mirrorClass.visitEnd()
- writeIfNotTooBig("" + modsym.name, mirrorName, mirrorClass, modsym)
+ enqueue("" + modsym.name, mirrorName, mirrorClass, modsym)
}
@@ -2915,7 +2961,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters { /** builder of bean info classes */
- class JBeanInfoBuilder(bytecodeWriter: BytecodeWriter) extends JBuilder(bytecodeWriter) {
+ class JBeanInfoBuilder(wuQ: WorkUnitQueue, needsOutfileForSymbol: Boolean) extends JBuilder(wuQ, needsOutfileForSymbol) {
/**
* Generate a bean info class that describes the given class.
@@ -3036,7 +3082,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters { addInnerClasses(clasz.symbol, beanInfoClass)
beanInfoClass.visitEnd()
- writeIfNotTooBig("BeanInfo ", beanInfoName, beanInfoClass, clasz.symbol)
+ enqueue("BeanInfo ", beanInfoName, beanInfoClass, clasz.symbol)
}
} // end of class JBeanInfoBuilder
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala index 21260d399c..ad054015ef 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala @@ -183,7 +183,6 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with class BytecodeGenerator(bytecodeWriter: BytecodeWriter) extends BytecodeUtil { def this() = this(new ClassBytecodeWriter { }) def debugLevel = settings.debuginfo.indexOfChoice - import bytecodeWriter.writeClass val MIN_SWITCH_DENSITY = 0.7 val INNER_CLASSES_FLAGS = @@ -344,6 +343,15 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with writeClass("" + sym.name, jclass.getName(), toByteArray(jclass), sym) } + val needsOutfileForSymbol = bytecodeWriter.isInstanceOf[ClassBytecodeWriter] + + def writeClass(label: String, jclassName: String, jclassBytes: Array[Byte], sym: Symbol) { + val outF: scala.tools.nsc.io.AbstractFile = { + if(needsOutfileForSymbol) getFile(sym, jclassName, ".class") else null + } + bytecodeWriter.writeClass(label, jclassName, jclassBytes, outF) + } + /** Returns the ScalaSignature annotation if it must be added to this class, * none otherwise; furthermore, it adds to `jclass` the ScalaSig marker * attribute (marking that a scala signature annotation is present) or the |