scala.tools.nsc.backend.jvm.PostProcessor.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of scala-compiler Show documentation
Show all versions of scala-compiler Show documentation
Compiler for the Scala Programming Language
The newest version!
/*
* Scala (https://www.scala-lang.org)
*
* Copyright EPFL and Lightbend, Inc.
*
* Licensed under Apache License 2.0
* (http://www.apache.org/licenses/LICENSE-2.0).
*
* See the NOTICE file distributed with this work for
* additional information regarding copyright ownership.
*/
package scala.tools.nsc
package backend.jvm
import java.util.concurrent.ConcurrentHashMap
import scala.collection.mutable
import scala.reflect.internal.util.{NoPosition, Position, StringContextStripMarginOps}
import scala.reflect.io.AbstractFile
import scala.tools.asm.ClassWriter
import scala.tools.asm.tree.ClassNode
import scala.tools.nsc.backend.jvm.analysis.BackendUtils
import scala.tools.nsc.backend.jvm.opt._
/**
* Implements late stages of the backend that don't depend on a Global instance, i.e.,
* optimizations, post-processing and classfile serialization and writing.
*/
abstract class PostProcessor extends PerRunInit {
self =>
val bTypes: BTypes
import bTypes._
import frontendAccess.{backendReporting, compilerSettings, recordPerRunJavaMapCache}
val backendUtils : BackendUtils { val postProcessor: self.type } = new { val postProcessor: self.type = self } with BackendUtils
val byteCodeRepository : ByteCodeRepository { val postProcessor: self.type } = new { val postProcessor: self.type = self } with ByteCodeRepository
val localOpt : LocalOpt { val postProcessor: self.type } = new { val postProcessor: self.type = self } with LocalOpt
val inliner : Inliner { val postProcessor: self.type } = new { val postProcessor: self.type = self } with Inliner
val inlinerHeuristics : InlinerHeuristics { val postProcessor: self.type } = new { val postProcessor: self.type = self } with InlinerHeuristics
val closureOptimizer : ClosureOptimizer { val postProcessor: self.type } = new { val postProcessor: self.type = self } with ClosureOptimizer
val callGraph : CallGraph { val postProcessor: self.type } = new { val postProcessor: self.type = self } with CallGraph
val bTypesFromClassfile : BTypesFromClassfile { val postProcessor: self.type } = new { val postProcessor: self.type = self } with BTypesFromClassfile
val classfileWriters : ClassfileWriters { val postProcessor: self.type } = new { val postProcessor: self.type = self } with ClassfileWriters
var classfileWriter: classfileWriters.ClassfileWriter = _
// from lowercase to first-seen name and position thereof
private val caseInsensitively = recordPerRunJavaMapCache(new ConcurrentHashMap[String, (String, Position)])
def initialize(global: Global): Unit = {
this.initialize()
backendUtils.initialize()
inlinerHeuristics.initialize()
byteCodeRepository.initialize()
classfileWriter = classfileWriters.ClassfileWriter(global)
}
def sendToDisk(clazz: GeneratedClass, sourceFile: AbstractFile): Unit = {
val classNode = clazz.classNode
val internalName = classNode.name
val bytes = try {
if (!clazz.isArtifact) {
localOptimizations(classNode)
val indyLambdaBodyMethods = backendUtils.indyLambdaBodyMethods(internalName)
if (indyLambdaBodyMethods.nonEmpty)
backendUtils.addLambdaDeserialize(classNode, indyLambdaBodyMethods)
}
warnCaseInsensitiveOverwrite(clazz)
setInnerClasses(classNode)
serializeClass(classNode)
} catch {
case ex: InterruptedException => throw ex
case ex: Throwable =>
// TODO fail fast rather than continuing to write the rest of the class files?
if (frontendAccess.compilerSettings.debug) ex.printStackTrace()
backendReporting.error(NoPosition, s"Error while emitting $internalName\n${ex.getMessage}")
null
}
if (bytes != null) {
if (AsmUtils.traceSerializedClassEnabled && internalName.contains(AsmUtils.traceSerializedClassPattern))
AsmUtils.traceClass(bytes)
classfileWriter.writeClass(internalName, bytes, sourceFile)
}
}
private def warnCaseInsensitiveOverwrite(clazz: GeneratedClass): Unit = {
val name = clazz.classNode.name
val lowercaseJavaClassName = name.toLowerCase
val overwrites = caseInsensitively.putIfAbsent(lowercaseJavaClassName, (name, clazz.position))
if (overwrites ne null) {
val (dupName, dupPos) = overwrites
val locationAddendum =
if (dupPos.source.path != clazz.position.source.path)
s" (defined in ${dupPos.source.file.name})"
else ""
def nicify(name: String): String = name.replace('/', '.')
backendReporting.warning(
clazz.position,
sm"""Generated class ${nicify(name)} differs only in case from ${nicify(dupName)}$locationAddendum.
| Such classes will overwrite one another on case-insensitive filesystems."""
)
}
}
def runGlobalOptimizations(generatedUnits: Iterable[GeneratedCompilationUnit]): Unit = {
// add classes to the bytecode repo before building the call graph: the latter needs to
// look up classes and methods in the code repo.
if (compilerSettings.optAddToBytecodeRepository) {
for (u <- generatedUnits; c <- u.classes) {
byteCodeRepository.add(c.classNode, Some(u.sourceFile.canonicalPath))
}
if (compilerSettings.optBuildCallGraph) for (u <- generatedUnits; c <- u.classes if !c.isArtifact) {
// skip call graph for mirror / bean: we don't inline into them, and they are not referenced from other classes
callGraph.addClass(c.classNode)
}
if (compilerSettings.optInlinerEnabled)
inliner.runInlinerAndClosureOptimizer()
else if (compilerSettings.optClosureInvocations)
closureOptimizer.rewriteClosureApplyInvocations(None, mutable.Map.empty)
}
}
def localOptimizations(classNode: ClassNode): Unit = {
val stats = frontendAccess.unsafeStatistics
stats.timed(stats.methodOptTimer)(localOpt.methodOptimizations(classNode))
}
def setInnerClasses(classNode: ClassNode): Unit = {
classNode.innerClasses.clear()
val (declared, referred) = backendUtils.collectNestedClasses(classNode)
backendUtils.addInnerClasses(classNode, declared, referred)
}
def serializeClass(classNode: ClassNode): Array[Byte] = {
val cw = new ClassWriterWithBTypeLub(backendUtils.extraProc.get)
classNode.accept(cw)
cw.toByteArray
}
/**
* An asm ClassWriter that uses ClassBType.jvmWiseLUB to compute the common superclass of class
* types. This operation is used for computing stack map frames.
*/
final class ClassWriterWithBTypeLub(flags: Int) extends ClassWriter(flags) {
/**
* This method is invoked by asm during classfile writing when computing stack map frames.
*
* TODO: it might make sense to cache results per compiler run. The ClassWriter caches
* internally, but we create a new writer for each class. scala/scala-dev#322.
*/
override def getCommonSuperClass(inameA: String, inameB: String): String = {
// All types that appear in a class node need to have their ClassBType cached, see [[cachedClassBType]].
val a = cachedClassBType(inameA)
val b = cachedClassBType(inameB)
val lub = a.jvmWiseLUB(b).get
val lubName = lub.internalName
assert(lubName != "scala/Any")
lubName
}
}
}
/**
* The result of code generation. [[isArtifact]] is `true` for mirror and bean-info classes.
*/
case class GeneratedClass(classNode: ClassNode, sourceClassName: String, position: Position, isArtifact: Boolean)
case class GeneratedCompilationUnit(sourceFile: AbstractFile, classes: List[GeneratedClass])
© 2015 - 2024 Weber Informatics LLC | Privacy Policy