|
Scala example source code file (GenBCode.scala)
The GenBCode.scala Scala example source code/* NSC -- new Scala compiler * Copyright 2005-2012 LAMP/EPFL * @author Martin Odersky */ package scala package tools.nsc package backend package jvm import scala.collection.{ mutable, immutable } import scala.annotation.switch import scala.tools.asm /* * Prepare in-memory representations of classfiles using the ASM Tree API, and serialize them to disk. * * Three pipelines are at work, each taking work items from a queue dedicated to that pipeline: * * (There's another pipeline so to speak, the one that populates queue-1 by traversing a CompilationUnit until ClassDefs are found, * but the "interesting" pipelines are the ones described below) * * (1) In the first queue, an item consists of a ClassDef along with its arrival position. * This position is needed at the time classfiles are serialized to disk, * so as to emit classfiles in the same order CleanUp handed them over. * As a result, two runs of the compiler on the same files produce jars that are identical on a byte basis. * See `ant test.stability` * * (2) The second queue contains items where a ClassDef has been lowered into: * (a) an optional mirror class, * (b) a plain class, and * (c) an optional bean class. * * (3) The third queue contains items ready for serialization. * It's a priority queue that follows the original arrival order, * so as to emit identical jars on repeated compilation of the same sources. * * Plain, mirror, and bean classes are built respectively by PlainClassBuilder, JMirrorBuilder, and JBeanInfoBuilder. * * @author Miguel Garcia, http://lamp.epfl.ch/~magarcia/ScalaCompilerCornerReloaded/ * @version 1.0 * */ abstract class GenBCode extends BCodeSyncAndTry { import global._ val phaseName = "jvm" override def newPhase(prev: Phase) = new BCodePhase(prev) final class PlainClassBuilder(cunit: CompilationUnit) extends SyncAndTryBuilder(cunit) class BCodePhase(prev: Phase) extends StdPhase(prev) { override def name = phaseName override def description = "Generate bytecode from ASTs using the ASM library" override def erasedTypes = true private var bytecodeWriter : BytecodeWriter = null private var mirrorCodeGen : JMirrorBuilder = null private var beanInfoCodeGen : JBeanInfoBuilder = null /* ---------------- q1 ---------------- */ case class Item1(arrivalPos: Int, cd: ClassDef, cunit: CompilationUnit) { def isPoison = { arrivalPos == Int.MaxValue } } private val poison1 = Item1(Int.MaxValue, null, null) private val q1 = new java.util.LinkedList[Item1] /* ---------------- q2 ---------------- */ case class Item2(arrivalPos: Int, mirror: asm.tree.ClassNode, plain: asm.tree.ClassNode, bean: asm.tree.ClassNode, outFolder: scala.tools.nsc.io.AbstractFile) { def isPoison = { arrivalPos == Int.MaxValue } } private val poison2 = Item2(Int.MaxValue, null, null, null, null) private val q2 = new _root_.java.util.LinkedList[Item2] /* ---------------- q3 ---------------- */ /* * An item of queue-3 (the last queue before serializing to disk) contains three of these * (one for each of mirror, plain, and bean classes). * * @param jclassName internal name of the class * @param jclassBytes bytecode emitted for the class SubItem3 represents */ case class SubItem3( jclassName: String, jclassBytes: Array[Byte] ) case class Item3(arrivalPos: Int, mirror: SubItem3, plain: SubItem3, bean: SubItem3, outFolder: scala.tools.nsc.io.AbstractFile) { def isPoison = { arrivalPos == Int.MaxValue } } private val i3comparator = new java.util.Comparator[Item3] { override def compare(a: Item3, b: Item3) = { if (a.arrivalPos < b.arrivalPos) -1 else if (a.arrivalPos == b.arrivalPos) 0 else 1 } } private val poison3 = Item3(Int.MaxValue, null, null, null, null) private val q3 = new java.util.PriorityQueue[Item3](1000, i3comparator) /* * Pipeline that takes ClassDefs from queue-1, lowers them into an intermediate form, placing them on queue-2 */ class Worker1(needsOutFolder: Boolean) { val caseInsensitively = mutable.Map.empty[String, Symbol] def run() { while (true) { val item = q1.poll if (item.isPoison) { q2 add poison2 return } else { try { visit(item) } catch { case ex: Throwable => ex.printStackTrace() error(s"Error while emitting ${item.cunit.source}\n${ex.getMessage}") } } } } /* * Checks for duplicate internal names case-insensitively, * builds ASM ClassNodes for mirror, plain, and bean classes; * enqueues them in queue-2. * */ def visit(item: Item1) { val Item1(arrivalPos, cd, cunit) = item val claszSymbol = cd.symbol // GenASM checks this before classfiles are emitted, https://github.com/scala/scala/commit/e4d1d930693ac75d8eb64c2c3c69f2fc22bec739 val lowercaseJavaClassName = claszSymbol.javaClassName.toLowerCase caseInsensitively.get(lowercaseJavaClassName) match { case None => caseInsensitively.put(lowercaseJavaClassName, claszSymbol) case Some(dupClassSym) => item.cunit.warning( claszSymbol.pos, s"Class ${claszSymbol.javaClassName} differs only in case from ${dupClassSym.javaClassName}. " + "Such classes will overwrite one another on case-insensitive filesystems." ) } // -------------- mirror class, if needed -------------- val mirrorC = if (isStaticModule(claszSymbol) && isTopLevelModule(claszSymbol)) { if (claszSymbol.companionClass == NoSymbol) { mirrorCodeGen.genMirrorClass(claszSymbol, cunit) } else { log(s"No mirror class for module with linked class: ${claszSymbol.fullName}") null } } else null // -------------- "plain" class -------------- val pcb = new PlainClassBuilder(cunit) pcb.genPlainClass(cd) val outF = if (needsOutFolder) getOutFolder(claszSymbol, pcb.thisName, cunit) else null; val plainC = pcb.cnode // -------------- bean info class, if needed -------------- val beanC = if (claszSymbol hasAnnotation BeanInfoAttr) { beanInfoCodeGen.genBeanInfoClass( claszSymbol, cunit, fieldSymbols(claszSymbol), methodSymbols(cd) ) } else null // ----------- hand over to pipeline-2 val item2 = Item2(arrivalPos, mirrorC, plainC, beanC, outF) q2 add item2 // at the very end of this method so that no Worker2 thread starts mutating before we're done. } // end of method visit(Item1) } // end of class BCodePhase.Worker1 /* * Pipeline that takes ClassNodes from queue-2. The unit of work depends on the optimization level: * * (a) no optimization involves: * - converting the plain ClassNode to byte array and placing it on queue-3 */ class Worker2 { def run() { while (true) { val item = q2.poll if (item.isPoison) { q3 add poison3 return } else { try { addToQ3(item) } catch { case ex: Throwable => ex.printStackTrace() error(s"Error while emitting ${item.plain.name}\n${ex.getMessage}") } } } } private def addToQ3(item: Item2) { def getByteArray(cn: asm.tree.ClassNode): Array[Byte] = { val cw = new CClassWriter(extraProc) cn.accept(cw) cw.toByteArray } val Item2(arrivalPos, mirror, plain, bean, outFolder) = item val mirrorC = if (mirror == null) null else SubItem3(mirror.name, getByteArray(mirror)) val plainC = SubItem3(plain.name, getByteArray(plain)) val beanC = if (bean == null) null else SubItem3(bean.name, getByteArray(bean)) if (AsmUtils.traceSerializedClassEnabled && plain.name.contains(AsmUtils.traceSerializedClassPattern)) { def readClass(bytes: Array[Byte]): asm.tree.ClassNode = { val node = new asm.tree.ClassNode() new asm.ClassReader(bytes).accept(node, 0) node } if (mirrorC != null) AsmUtils.traceClass(readClass(mirrorC.jclassBytes)) AsmUtils.traceClass(readClass(plainC.jclassBytes)) if (beanC != null) AsmUtils.traceClass(readClass(beanC.jclassBytes)) } q3 add Item3(arrivalPos, mirrorC, plainC, beanC, outFolder) } } // end of class BCodePhase.Worker2 var arrivalPos = 0 /* * A run of the BCodePhase phase comprises: * * (a) set-up steps (most notably supporting maps in `BCodeTypes`, * but also "the" writer where class files in byte-array form go) * * (b) building of ASM ClassNodes, their optimization and serialization. * * (c) tear down (closing the classfile-writer and clearing maps) * */ override def run() { arrivalPos = 0 // just in case scalaPrimitives.init initBCodeTypes() // initBytecodeWriter invokes fullName, thus we have to run it before the typer-dependent thread is activated. bytecodeWriter = initBytecodeWriter(cleanup.getEntryPoints) mirrorCodeGen = new JMirrorBuilder beanInfoCodeGen = new JBeanInfoBuilder val needsOutfileForSymbol = bytecodeWriter.isInstanceOf[ClassBytecodeWriter] buildAndSendToDisk(needsOutfileForSymbol) // closing output files. bytecodeWriter.close() /* TODO Bytecode can be verified (now that all classfiles have been written to disk) * * (1) asm.util.CheckAdapter.verify() * public static void verify(ClassReader cr, ClassLoader loader, boolean dump, PrintWriter pw) * passing a custom ClassLoader to verify inter-dependent classes. * Alternatively, * - an offline-bytecode verifier could be used (e.g. Maxine brings one as separate tool). * - -Xverify:all * * (2) if requested, check-java-signatures, over and beyond the syntactic checks in `getGenericSignature()` * */ // clearing maps clearBCodeTypes() } /* * Sequentially: * (a) place all ClassDefs in queue-1 * (b) dequeue one at a time from queue-1, convert it to ASM ClassNode, place in queue-2 * (c) dequeue one at a time from queue-2, convert it to byte-array, place in queue-3 * (d) serialize to disk by draining queue-3. */ private def buildAndSendToDisk(needsOutFolder: Boolean) { feedPipeline1() (new Worker1(needsOutFolder)).run() (new Worker2).run() drainQ3() } /* Feed pipeline-1: place all ClassDefs on q1, recording their arrival position. */ private def feedPipeline1() { super.run() q1 add poison1 } /* Pipeline that writes classfile representations to disk. */ private def drainQ3() { def sendToDisk(cfr: SubItem3, outFolder: scala.tools.nsc.io.AbstractFile) { if (cfr != null){ val SubItem3(jclassName, jclassBytes) = cfr try { val outFile = if (outFolder == null) null else getFileForClassfile(outFolder, jclassName, ".class") bytecodeWriter.writeClass(jclassName, jclassName, jclassBytes, outFile) } catch { case e: FileConflictException => error(s"error writing $jclassName: ${e.getMessage}") } } } var moreComing = true // `expected` denotes the arrivalPos whose Item3 should be serialized next var expected = 0 while (moreComing) { val incoming = q3.poll moreComing = !incoming.isPoison if (moreComing) { val item = incoming val outFolder = item.outFolder sendToDisk(item.mirror, outFolder) sendToDisk(item.plain, outFolder) sendToDisk(item.bean, outFolder) expected += 1 } } // we're done assert(q1.isEmpty, s"Some ClassDefs remained in the first queue: $q1") assert(q2.isEmpty, s"Some classfiles remained in the second queue: $q2") assert(q3.isEmpty, s"Some classfiles weren't written to disk: $q3") } override def apply(cunit: CompilationUnit): Unit = { def gen(tree: Tree) { tree match { case EmptyTree => () case PackageDef(_, stats) => stats foreach gen case cd: ClassDef => q1 add Item1(arrivalPos, cd, cunit) arrivalPos += 1 } } gen(cunit.body) } } // end of class BCodePhase } // end of class GenBCode Other Scala source code examplesHere is a short list of links related to this Scala GenBCode.scala source code file: |
... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2024 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.