Skip to content

Commit 25e97e1

Browse files
committed
copied GenBCode files from scala/scala
(does not compile)
1 parent 4e34962 commit 25e97e1

8 files changed

+6378
-0
lines changed

src/dotty/tools/dotc/backend/jvm/BCodeBodyBuilder.scala

Lines changed: 1234 additions & 0 deletions
Large diffs are not rendered by default.

src/dotty/tools/dotc/backend/jvm/BCodeGlue.scala

Lines changed: 716 additions & 0 deletions
Large diffs are not rendered by default.

src/dotty/tools/dotc/backend/jvm/BCodeHelpers.scala

Lines changed: 1323 additions & 0 deletions
Large diffs are not rendered by default.

src/dotty/tools/dotc/backend/jvm/BCodeIdiomatic.scala

Lines changed: 725 additions & 0 deletions
Large diffs are not rendered by default.

src/dotty/tools/dotc/backend/jvm/BCodeSkelBuilder.scala

Lines changed: 724 additions & 0 deletions
Large diffs are not rendered by default.

src/dotty/tools/dotc/backend/jvm/BCodeSyncAndTry.scala

Lines changed: 395 additions & 0 deletions
Large diffs are not rendered by default.

src/dotty/tools/dotc/backend/jvm/BCodeTypes.scala

Lines changed: 880 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 381 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,381 @@
1+
/* NSC -- new Scala compiler
2+
* Copyright 2005-2012 LAMP/EPFL
3+
* @author Martin Odersky
4+
*/
5+
6+
7+
package scala
8+
package tools.nsc
9+
package backend
10+
package jvm
11+
12+
import scala.collection.{ mutable, immutable }
13+
import scala.annotation.switch
14+
15+
import scala.tools.asm
16+
17+
/*
18+
* Prepare in-memory representations of classfiles using the ASM Tree API, and serialize them to disk.
19+
*
20+
* Three pipelines are at work, each taking work items from a queue dedicated to that pipeline:
21+
*
22+
* (There's another pipeline so to speak, the one that populates queue-1 by traversing a CompilationUnit until ClassDefs are found,
23+
* but the "interesting" pipelines are the ones described below)
24+
*
25+
* (1) In the first queue, an item consists of a ClassDef along with its arrival position.
26+
* This position is needed at the time classfiles are serialized to disk,
27+
* so as to emit classfiles in the same order CleanUp handed them over.
28+
* As a result, two runs of the compiler on the same files produce jars that are identical on a byte basis.
29+
* See `ant test.stability`
30+
*
31+
* (2) The second queue contains items where a ClassDef has been lowered into:
32+
* (a) an optional mirror class,
33+
* (b) a plain class, and
34+
* (c) an optional bean class.
35+
*
36+
* (3) The third queue contains items ready for serialization.
37+
* It's a priority queue that follows the original arrival order,
38+
* so as to emit identical jars on repeated compilation of the same sources.
39+
*
40+
* Plain, mirror, and bean classes are built respectively by PlainClassBuilder, JMirrorBuilder, and JBeanInfoBuilder.
41+
*
42+
* @author Miguel Garcia, http://lamp.epfl.ch/~magarcia/ScalaCompilerCornerReloaded/
43+
* @version 1.0
44+
*
45+
*/
46+
abstract class GenBCode extends BCodeSyncAndTry {
47+
import global._
48+
49+
val phaseName = "jvm"
50+
51+
override def newPhase(prev: Phase) = new BCodePhase(prev)
52+
53+
final class PlainClassBuilder(cunit: CompilationUnit) extends SyncAndTryBuilder(cunit)
54+
55+
class BCodePhase(prev: Phase) extends StdPhase(prev) {
56+
57+
override def name = phaseName
58+
override def description = "Generate bytecode from ASTs using the ASM library"
59+
override def erasedTypes = true
60+
61+
private var bytecodeWriter : BytecodeWriter = null
62+
private var mirrorCodeGen : JMirrorBuilder = null
63+
private var beanInfoCodeGen : JBeanInfoBuilder = null
64+
65+
/* ---------------- q1 ---------------- */
66+
67+
case class Item1(arrivalPos: Int, cd: ClassDef, cunit: CompilationUnit) {
68+
def isPoison = { arrivalPos == Int.MaxValue }
69+
}
70+
private val poison1 = Item1(Int.MaxValue, null, null)
71+
private val q1 = new java.util.LinkedList[Item1]
72+
73+
/* ---------------- q2 ---------------- */
74+
75+
case class Item2(arrivalPos: Int,
76+
mirror: asm.tree.ClassNode,
77+
plain: asm.tree.ClassNode,
78+
bean: asm.tree.ClassNode,
79+
outFolder: scala.tools.nsc.io.AbstractFile) {
80+
def isPoison = { arrivalPos == Int.MaxValue }
81+
}
82+
83+
private val poison2 = Item2(Int.MaxValue, null, null, null, null)
84+
private val q2 = new _root_.java.util.LinkedList[Item2]
85+
86+
/* ---------------- q3 ---------------- */
87+
88+
/*
89+
* An item of queue-3 (the last queue before serializing to disk) contains three of these
90+
* (one for each of mirror, plain, and bean classes).
91+
*
92+
* @param jclassName internal name of the class
93+
* @param jclassBytes bytecode emitted for the class SubItem3 represents
94+
*/
95+
case class SubItem3(
96+
jclassName: String,
97+
jclassBytes: Array[Byte]
98+
)
99+
100+
case class Item3(arrivalPos: Int,
101+
mirror: SubItem3,
102+
plain: SubItem3,
103+
bean: SubItem3,
104+
outFolder: scala.tools.nsc.io.AbstractFile) {
105+
106+
def isPoison = { arrivalPos == Int.MaxValue }
107+
}
108+
private val i3comparator = new java.util.Comparator[Item3] {
109+
override def compare(a: Item3, b: Item3) = {
110+
if (a.arrivalPos < b.arrivalPos) -1
111+
else if (a.arrivalPos == b.arrivalPos) 0
112+
else 1
113+
}
114+
}
115+
private val poison3 = Item3(Int.MaxValue, null, null, null, null)
116+
private val q3 = new java.util.PriorityQueue[Item3](1000, i3comparator)
117+
118+
/*
119+
* Pipeline that takes ClassDefs from queue-1, lowers them into an intermediate form, placing them on queue-2
120+
*/
121+
class Worker1(needsOutFolder: Boolean) {
122+
123+
val caseInsensitively = mutable.Map.empty[String, Symbol]
124+
125+
def run() {
126+
while (true) {
127+
val item = q1.poll
128+
if (item.isPoison) {
129+
q2 add poison2
130+
return
131+
}
132+
else {
133+
try { visit(item) }
134+
catch {
135+
case ex: Throwable =>
136+
ex.printStackTrace()
137+
error(s"Error while emitting ${item.cunit.source}\n${ex.getMessage}")
138+
}
139+
}
140+
}
141+
}
142+
143+
/*
144+
* Checks for duplicate internal names case-insensitively,
145+
* builds ASM ClassNodes for mirror, plain, and bean classes;
146+
* enqueues them in queue-2.
147+
*
148+
*/
149+
def visit(item: Item1) {
150+
val Item1(arrivalPos, cd, cunit) = item
151+
val claszSymbol = cd.symbol
152+
153+
// GenASM checks this before classfiles are emitted, https://github.com/scala/scala/commit/e4d1d930693ac75d8eb64c2c3c69f2fc22bec739
154+
val lowercaseJavaClassName = claszSymbol.javaClassName.toLowerCase
155+
caseInsensitively.get(lowercaseJavaClassName) match {
156+
case None =>
157+
caseInsensitively.put(lowercaseJavaClassName, claszSymbol)
158+
case Some(dupClassSym) =>
159+
item.cunit.warning(
160+
claszSymbol.pos,
161+
s"Class ${claszSymbol.javaClassName} differs only in case from ${dupClassSym.javaClassName}. " +
162+
"Such classes will overwrite one another on case-insensitive filesystems."
163+
)
164+
}
165+
166+
// -------------- mirror class, if needed --------------
167+
val mirrorC =
168+
if (isStaticModule(claszSymbol) && isTopLevelModule(claszSymbol)) {
169+
if (claszSymbol.companionClass == NoSymbol) {
170+
mirrorCodeGen.genMirrorClass(claszSymbol, cunit)
171+
} else {
172+
log(s"No mirror class for module with linked class: ${claszSymbol.fullName}")
173+
null
174+
}
175+
} else null
176+
177+
// -------------- "plain" class --------------
178+
val pcb = new PlainClassBuilder(cunit)
179+
pcb.genPlainClass(cd)
180+
val outF = if (needsOutFolder) getOutFolder(claszSymbol, pcb.thisName, cunit) else null;
181+
val plainC = pcb.cnode
182+
183+
// -------------- bean info class, if needed --------------
184+
val beanC =
185+
if (claszSymbol hasAnnotation BeanInfoAttr) {
186+
beanInfoCodeGen.genBeanInfoClass(
187+
claszSymbol, cunit,
188+
fieldSymbols(claszSymbol),
189+
methodSymbols(cd)
190+
)
191+
} else null
192+
193+
// ----------- hand over to pipeline-2
194+
195+
val item2 =
196+
Item2(arrivalPos,
197+
mirrorC, plainC, beanC,
198+
outF)
199+
200+
q2 add item2 // at the very end of this method so that no Worker2 thread starts mutating before we're done.
201+
202+
} // end of method visit(Item1)
203+
204+
} // end of class BCodePhase.Worker1
205+
206+
/*
207+
* Pipeline that takes ClassNodes from queue-2. The unit of work depends on the optimization level:
208+
*
209+
* (a) no optimization involves:
210+
* - converting the plain ClassNode to byte array and placing it on queue-3
211+
*/
212+
class Worker2 {
213+
214+
def run() {
215+
while (true) {
216+
val item = q2.poll
217+
if (item.isPoison) {
218+
q3 add poison3
219+
return
220+
}
221+
else {
222+
try { addToQ3(item) }
223+
catch {
224+
case ex: Throwable =>
225+
ex.printStackTrace()
226+
error(s"Error while emitting ${item.plain.name}\n${ex.getMessage}")
227+
}
228+
}
229+
}
230+
}
231+
232+
private def addToQ3(item: Item2) {
233+
234+
def getByteArray(cn: asm.tree.ClassNode): Array[Byte] = {
235+
val cw = new CClassWriter(extraProc)
236+
cn.accept(cw)
237+
cw.toByteArray
238+
}
239+
240+
val Item2(arrivalPos, mirror, plain, bean, outFolder) = item
241+
242+
val mirrorC = if (mirror == null) null else SubItem3(mirror.name, getByteArray(mirror))
243+
val plainC = SubItem3(plain.name, getByteArray(plain))
244+
val beanC = if (bean == null) null else SubItem3(bean.name, getByteArray(bean))
245+
246+
q3 add Item3(arrivalPos, mirrorC, plainC, beanC, outFolder)
247+
248+
}
249+
250+
} // end of class BCodePhase.Worker2
251+
252+
var arrivalPos = 0
253+
254+
/*
255+
* A run of the BCodePhase phase comprises:
256+
*
257+
* (a) set-up steps (most notably supporting maps in `BCodeTypes`,
258+
* but also "the" writer where class files in byte-array form go)
259+
*
260+
* (b) building of ASM ClassNodes, their optimization and serialization.
261+
*
262+
* (c) tear down (closing the classfile-writer and clearing maps)
263+
*
264+
*/
265+
override def run() {
266+
267+
arrivalPos = 0 // just in case
268+
scalaPrimitives.init
269+
initBCodeTypes()
270+
271+
// initBytecodeWriter invokes fullName, thus we have to run it before the typer-dependent thread is activated.
272+
bytecodeWriter = initBytecodeWriter(cleanup.getEntryPoints)
273+
mirrorCodeGen = new JMirrorBuilder
274+
beanInfoCodeGen = new JBeanInfoBuilder
275+
276+
val needsOutfileForSymbol = bytecodeWriter.isInstanceOf[ClassBytecodeWriter]
277+
buildAndSendToDisk(needsOutfileForSymbol)
278+
279+
// closing output files.
280+
bytecodeWriter.close()
281+
282+
/* TODO Bytecode can be verified (now that all classfiles have been written to disk)
283+
*
284+
* (1) asm.util.CheckAdapter.verify()
285+
* public static void verify(ClassReader cr, ClassLoader loader, boolean dump, PrintWriter pw)
286+
* passing a custom ClassLoader to verify inter-dependent classes.
287+
* Alternatively,
288+
* - an offline-bytecode verifier could be used (e.g. Maxine brings one as separate tool).
289+
* - -Xverify:all
290+
*
291+
* (2) if requested, check-java-signatures, over and beyond the syntactic checks in `getGenericSignature()`
292+
*
293+
*/
294+
295+
// clearing maps
296+
clearBCodeTypes()
297+
}
298+
299+
/*
300+
* Sequentially:
301+
* (a) place all ClassDefs in queue-1
302+
* (b) dequeue one at a time from queue-1, convert it to ASM ClassNode, place in queue-2
303+
* (c) dequeue one at a time from queue-2, convert it to byte-array, place in queue-3
304+
* (d) serialize to disk by draining queue-3.
305+
*/
306+
private def buildAndSendToDisk(needsOutFolder: Boolean) {
307+
308+
feedPipeline1()
309+
(new Worker1(needsOutFolder)).run()
310+
(new Worker2).run()
311+
drainQ3()
312+
313+
}
314+
315+
/* Feed pipeline-1: place all ClassDefs on q1, recording their arrival position. */
316+
private def feedPipeline1() {
317+
super.run()
318+
q1 add poison1
319+
}
320+
321+
/* Pipeline that writes classfile representations to disk. */
322+
private def drainQ3() {
323+
324+
def sendToDisk(cfr: SubItem3, outFolder: scala.tools.nsc.io.AbstractFile) {
325+
if (cfr != null){
326+
val SubItem3(jclassName, jclassBytes) = cfr
327+
try {
328+
val outFile =
329+
if (outFolder == null) null
330+
else getFileForClassfile(outFolder, jclassName, ".class")
331+
bytecodeWriter.writeClass(jclassName, jclassName, jclassBytes, outFile)
332+
}
333+
catch {
334+
case e: FileConflictException =>
335+
error(s"error writing $jclassName: ${e.getMessage}")
336+
}
337+
}
338+
}
339+
340+
var moreComing = true
341+
// `expected` denotes the arrivalPos whose Item3 should be serialized next
342+
var expected = 0
343+
344+
while (moreComing) {
345+
val incoming = q3.poll
346+
moreComing = !incoming.isPoison
347+
if (moreComing) {
348+
val item = incoming
349+
val outFolder = item.outFolder
350+
sendToDisk(item.mirror, outFolder)
351+
sendToDisk(item.plain, outFolder)
352+
sendToDisk(item.bean, outFolder)
353+
expected += 1
354+
}
355+
}
356+
357+
// we're done
358+
assert(q1.isEmpty, s"Some ClassDefs remained in the first queue: $q1")
359+
assert(q2.isEmpty, s"Some classfiles remained in the second queue: $q2")
360+
assert(q3.isEmpty, s"Some classfiles weren't written to disk: $q3")
361+
362+
}
363+
364+
override def apply(cunit: CompilationUnit): Unit = {
365+
366+
def gen(tree: Tree) {
367+
tree match {
368+
case EmptyTree => ()
369+
case PackageDef(_, stats) => stats foreach gen
370+
case cd: ClassDef =>
371+
q1 add Item1(arrivalPos, cd, cunit)
372+
arrivalPos += 1
373+
}
374+
}
375+
376+
gen(cunit.body)
377+
}
378+
379+
} // end of class BCodePhase
380+
381+
} // end of class GenBCode

0 commit comments

Comments
 (0)