8000 Simplify backend pipeline, move code to context without Global · scala/scala@8b179cd · GitHub
[go: up one dir, main page]

Skip to content

Commit 8b179cd

Browse files
committed
Simplify backend pipeline, move code to context without Global
Removes the three work queues in the backend. Splits up the backend in two main components - CodeGen, which has a Global - PostProcessor, which has a BTypes (but no Global) CodeGen generates asm.ClassNodes and stores them in postProcessor.generatedClasses. The code generator is invoketd through BCodePhase.apply. The postProcessor then runs the optimizer, computes the InnerClass table and adds the lambdaDeserialize method if necessary. It finally serializes the classes into a byte array and writes them to disk. The implementation of classfile writing still depends on Global. It is passed in as an argument to the postProcessor. A later commit will move it to a context without Global and make it thread-safe.
1 parent 078ba7c commit 8b179cd

File tree

8 files changed

+297
-502
lines changed

8 files changed

+297
-502
lines changed

src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala

Lines changed: 2 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -239,57 +239,8 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
239239
}
240240

241241

242-
/*
243-
* must-single-thread
244-
*/
245-
def getFileForClassfile(base: AbstractFile, clsName: String, suffix: String): AbstractFile = {
246-
getFile(base, clsName, suffix)
247-
}
248-
249-
/*
250-
* must-single-thread
251-
*/
252-
def getOutFolder(csym: Symbol, cName: String, cunit: CompilationUnit): _root_.scala.tools.nsc.io.AbstractFile =
253-
_root_.scala.util.Try {
254-
outputDirectory(csym)
255-
}.recover {
256-
case ex: Throwable =>
257-
reporter.error(cunit.body.pos, s"Couldn't create file for class $cName\n${ex.getMessage}")
258-
null
259-
}.get
260-
261242
var pickledBytes = 0 // statistics
262243

263-
// -----------------------------------------------------------------------------------------
264-
// finding the least upper bound in agreement with the bytecode verifier (given two internal names handed by ASM)
265-
// Background:
266-
// http://gallium.inria.fr/~xleroy/publi/bytecode-verification-JAR.pdf
267-
// http://comments.gmane.org/gmane.comp.java.vm.languages/2293
268-
// https://github.com/scala/bug/issues/3872
269-
// -----------------------------------------------------------------------------------------
270-
271-
/* An `asm.ClassWriter` that uses `jvmWiseLUB()`
272-
* The internal name of the least common ancestor of the types given by inameA and inameB.
273-
* It's what ASM needs to know in order to compute stack map frames, http://asm.ow2.org/doc/developer-guide.html#controlflow
274-
*/
275-
final class CClassWriter(flags: Int) extends asm.ClassWriter(flags) {
276-
277-
/**
278-
* This method is used by asm when computing stack map frames. It is thread-safe: it depends
279-
* only on the BTypes component, which does not depend on global.
280-
* TODO @lry move to a different place where no global is in scope, on bTypes.
281-
*/
282-
override def getCommonSuperClass(inameA: String, inameB: String): String = {
283-
// All types that appear in a class node need to have their ClassBType cached, see [[cachedClassBType]].
284-
val a = cachedClassBType(inameA).get
285-
val b = cachedClassBType(inameB).get
286-
val lub = a.jvmWiseLUB(b).get
287-
val lubName = lub.internalName
288-
assert(lubName != "scala/Any")
289-
lubName // ASM caches the answer during the lifetime of a ClassWriter. We outlive that. Not sure whether caching on our side would improve things.
290-
}
291-
}
292-
293244
/*
294245
* must-single-thread
295246
*/
@@ -406,29 +357,6 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
406357
case AnnotationInfo(_, _, (_, LiteralAnnotArg(const)) :: Nil) => const.longValue
407358
}
408359

409-
/*
410-
* Populates the InnerClasses JVM attribute with `refedInnerClasses`. See also the doc on inner
411-
* classes in BTypes.scala.
412-
*
413-
* `refedInnerClasses` may contain duplicates, need not contain the enclosing inner classes of
414-
* each inner class it lists (those are looked up and included).
415-
*
416-
* This method serializes in the InnerClasses JVM attribute in an appropriate order, not
417-
* necessarily that given by `refedInnerClasses`.
418-
*
419-
* can-multi-thread
420-
*/
421-
final def addInnerClasses(jclass: asm.ClassVisitor, refedInnerClasses: List[ClassBType]) {
422-
val allNestedClasses = refedInnerClasses.flatMap(_.enclosingNestedClassesChain.get).distinct
423-
424-
// sorting ensures nested classes are listed after their enclosing class thus satisfying the Eclipse Java compiler
425-
for (nestedClass <- allNestedClasses.sortBy(_.internalName.toString)) {
426-
// Extract the innerClassEntry - we know it exists, enclosingNestedClassesChain only returns nested classes.
427-
val Some(e) = nestedClass.innerClassAttributeEntry.get
428-
jclass.visitInnerClass(e.name, e.outerName, e.innerName, e.flags)
429-
}
430-
}
431-
432360
/*
433361
* Custom attribute (JVMS 4.7.1) "ScalaSig" used as marker only
434362
* i.e., the pickle is contained in a custom annotation, see:
@@ -1058,7 +986,7 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
1058986
val bType = mirrorClassClassBType(moduleClass)
1059987
val mirrorClass = new asm.tree.ClassNode
1060988
mirrorClass.visit(
1061-
classfileVersion,
989+
backendUtils.classfileVersion,
1062990
bType.info.get.flags,
1063991
bType.internalName,
1064992
null /* no java-generic-signature */,
@@ -1102,7 +1030,7 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
11021030

11031031
val beanInfoClass = new asm.tree.ClassNode
11041032
beanInfoClass.visit(
1105-
classfileVersion,
1033+
backendUtils.classfileVersion,
11061034
beanInfoType.info.get.flags,
11071035
beanInfoType.internalName,
11081036
null, // no java-generic-signature

src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,18 +29,6 @@ abstract class BCodeIdiomatic {
2929
import bTypes._
3030
import coreBTypes._
3131

32-
val classfileVersion: Int = settings.target.value match {
33-
case "jvm-1.8" => asm.Opcodes.V1_8
34-
}
35-
36-
val majorVersion: Int = (classfileVersion & 0xFF)
37-
val emitStackMapFrame = (majorVersion >= 50)
38-
39-
val extraProc: Int = GenBCode.mkFlags(
40-
asm.ClassWriter.COMPUTE_MAXS,
41-
if (emitStackMapFrame) asm.ClassWriter.COMPUTE_FRAMES else 0
42-
)
43-
4432
lazy val JavaStringBuilderClassName = jlStringBuilderRef.internalName
4533

4634
val EMPTY_STRING_ARRAY = Array.empty[String]

src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {
132132
val flags = javaFlags(claszSymbol)
133133

134134
val thisSignature = getGenericSignature(claszSymbol, claszSymbol.owner)
135-
cnode.visit(classfileVersion, flags,
135+
cnode.visit(backendUtils.classfileVersion, flags,
136136
thisBType.internalName, thisSignature,
137137
superClass, interfaceNames.toArray)
138138

src/compiler/scala/tools/nsc/backend/jvm/BackendReporting.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import scala.util.control.ControlThrowable
1313
*/
1414
sealed abstract class BackendReporting {
1515
def inlinerWarning(pos: Position, message: String): Unit
16+
17+
def error(pos: Position, message: String): Unit
1618
}
1719

1820
final class BackendReportingImpl(val global: Global) extends BackendReporting {
@@ -21,6 +23,8 @@ final class BackendReportingImpl(val global: Global) extends BackendReporting {
2123
def inlinerWarning(pos: Position, message: String): Unit = {
2224
currentRun.reporting.inlinerWarning(pos, message)
2325
}
26+
27+
def error(pos: Position, message: String): Unit = reporter.error(pos, message)
2428
}
2529

2630
/**

src/compiler/scala/tools/nsc/backend/jvm/CodeGen.scala

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,94 @@
11
package scala.tools.nsc
22
package backend.jvm
33

4+
import scala.tools.asm.tree.ClassNode
5+
46
abstract class CodeGen[G <: Global](val global: G) {
5-
import global._
67
val bTypes: BTypesFromSymbols[global.type]
78

9+
import global._
10+
import bTypes._
11+
12+
private val caseInsensitively = perRunCaches.newMap[String, Symbol]()
13+
14+
private var mirrorCodeGen : CodeGenImpl.JMirrorBuilder = null
15+
private var beanInfoCodeGen : CodeGenImpl.JBeanInfoBuilder = null
16+
17+
def genUnit(unit: CompilationUnit): Unit = {
18+
import genBCode.postProcessor.generatedClasses
19+
20+
def genClassDef(cd: ClassDef): Unit = try {
21+
val sym = cd.symbol
22+
val sourceFile = unit.source.file
23+
generatedClasses += GeneratedClass(genClass(cd, unit), sourceFile, isArtifact = false)
24+
if (bTypes.isTopLevelModuleClass(sym)) {
25+
if (sym.companionClass == NoSymbol)
26+
generatedClasses += GeneratedClass(genMirrorClass(sym, unit), sourceFile, isArtifact = true)
27+
else
28+
log(s"No mirror class for module with linked class: ${sym.fullName}")
29+
}
30+
if (sym hasAnnotation coreBTypes.BeanInfoAttr)
31+
generatedClasses += GeneratedClass(genBeanInfoClass(cd, unit), sourceFile, isArtifact = true)
32+
} catch {
33+
case ex: Throwable =>
34+
ex.printStackTrace()
35+
error(s"Error while emitting ${unit.source}\n${ex.getMessage}")
36+
}
37+
38+
def genClassDefs(tree: Tree): Unit = tree match {
39+
case EmptyTree => ()
40+
case PackageDef(_, stats) => stats foreach genClassDefs
41+
case cd: ClassDef => genClassDef(cd)
42+
}
43+
44+
genClassDefs(unit.body)
45+
}
46+
47+
def genClass(cd: ClassDef, unit: CompilationUnit): ClassNode = {
48+
warnCaseInsensitiveOverwrite(cd)
49+
addSbtIClassShim(cd)
50+
val b = new CodeGenImpl.SyncAndTryBuilder(unit)
51+
b.genPlainClass(cd)
52+
b.cnode
53+
}
54+
55+
def genMirrorClass(classSym: Symbol, unit: CompilationUnit): ClassNode = {
56+
mirrorCodeGen.genMirrorClass(classSym, unit)
57+
}
58+
59+
def genBeanInfoClass(cd: ClassDef, unit: CompilationUnit): ClassNode = {
60+
val sym = cd.symbol
61+
beanInfoCodeGen.genBeanInfoClass(sym, unit, CodeGenImpl.fieldSymbols(sym), CodeGenImpl.methodSymbols(cd))
62+
}
63+
64+
private def warnCaseInsensitiveOverwrite(cd: ClassDef): Unit = {
65+
val sym = cd.symbol
66+
// GenASM checks this before classfiles are emitted, https://github.com/scala/scala/commit/e4d1d930693ac75d8eb64c2c3c69f2fc22bec739
67+
val lowercaseJavaClassName = sym.javaClassName.toLowerCase
68+
caseInsensitively.get(lowercaseJavaClassName) match {
69+
case None =>
70+
caseInsensitively.put(lowercaseJavaClassName, sym)
71+
case Some(dupClassSym) =>
72+
reporter.warning(
73+
sym.pos,
74+
s"Class ${sym.javaClassName} differs only in case from ${dupClassSym.javaClassName}. " +
75+
"Such classes will overwrite one another on case-insensitive filesystems."
76+
)
77+
}
78+
}
79+
80+
private def addSbtIClassShim(cd: ClassDef): Unit = {
81+
// shim for SBT, see https://github.com/sbt/sbt/issues/2076
82+
// TODO put this closer to classfile writing once we have closure elimination
83+
// TODO create a nicer public API to find out the correspondence between sourcefile and ultimate classfiles
84+
currentUnit.icode += new icodes.IClass(cd.symbol)
85+
}
86+
87+
def initialize(): Unit = {
88+
mirrorCodeGen = new CodeGenImpl.JMirrorBuilder()
89+
beanInfoCodeGen = new CodeGenImpl.JBeanInfoBuilder()
90+
}
91+
892
object CodeGenImpl extends BCodeSyncAndTry {
993
val global: CodeGen.this.global.type = CodeGen.this.global
1094
val bTypes: CodeGen.this.bTypes.type = CodeGen.this.bTypes

0 commit comments

Comments
 (0)
0