Skip to content

2.12.10 preview #60

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 21 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
81cad62
add some customization to jar writing
mkeskells Jul 25, 2019
876c762
add headers
mkeskells Jul 26, 2019
3fc8075
Douse an allocation hotspot in pattern matcher
retronym Aug 1, 2019
1fbbe8d
add documentation as requested
mkeskells Aug 1, 2019
ccab7c0
Disable expensive lookup of source file in classfile parser by default
retronym Aug 6, 2019
fc2ee1f
Benchmark for phase assembly
retronym Aug 5, 2019
1b4006d
Optimize compiler phase assembly
retronym Aug 1, 2019
e83ea44
A faster compiler phase assembly algorithm
retronym Aug 7, 2019
759b979
Log phase levels under scalac -Xshow-phases -Ydebug -Ylog:all
retronym Aug 7, 2019
be3730a
Be more explicit in our compiler phase ordering
retronym Aug 7, 2019
55308dc
Fix regression in classpath when JARs have 'a.b' entries beside 'a/b'
retronym Aug 8, 2019
d6083a4
Fix regression in large string interpolations with non-String typed s…
retronym Aug 6, 2019
bcdce52
Revert "Generate shallower ASTs in pattern translation"
retronym Aug 8, 2019
55414e6
Another take on avoiding garbage in classpath lookups
retronym Aug 9, 2019
05a7bed
Merge remote-tracking branch 'origin/pr/8327' into topic/2.12.10-preview
retronym Aug 9, 2019
624800d
Merge remote-tracking branch 'origin/pr/8316' into topic/2.12.10-preview
retronym Aug 9, 2019
379fd48
Merge remote-tracking branch 'origin/pr/8295' into topic/2.12.10-preview
retronym Aug 9, 2019
806a7aa
Merge remote-tracking branch 'origin/pr/8294' into topic/2.12.10-preview
retronym Aug 9, 2019
77fb686
Merge remote-tracking branch 'origin/pr/8268' into topic/2.12.10-preview
retronym Aug 9, 2019
db54208
Merge remote-tracking branch 'origin/pr/8315' into topic/2.12.10-preview
retronym Aug 9, 2019
5ee6e9b
Merge branch 'ticket/11664-alloc' into topic/2.12.10-preview
retronym Aug 9, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions src/compiler/scala/tools/nsc/Global.scala
Original file line number Diff line number Diff line change
Expand Up @@ -527,13 +527,6 @@ class Global(var currentSettings: Settings, reporter0: Reporter)
val runsRightAfter = None
} with UnCurry

// phaseName = "tailcalls"
object tailCalls extends {
val global: Global.this.type = Global.this
val runsAfter = List("uncurry")
val runsRightAfter = None
} with TailCalls

// phaseName = "fields"
object fields extends {
val global: Global.this.type = Global.this
Expand All @@ -546,10 +539,17 @@ class Global(var currentSettings: Settings, reporter0: Reporter)
val runsRightAfter = None
} with Fields

// phaseName = "tailcalls"
object tailCalls extends {
val global: Global.this.type = Global.this
val runsAfter = List("fields")
val runsRightAfter = None
} with TailCalls

// phaseName = "explicitouter"
object explicitOuter extends {
val global: Global.this.type = Global.this
val runsAfter = List("fields")
val runsAfter = List("tailcalls")
val runsRightAfter = None
} with ExplicitOuter

Expand Down Expand Up @@ -620,7 +620,7 @@ class Global(var currentSettings: Settings, reporter0: Reporter)
// phaseName = "jvm"
object genBCode extends {
val global: Global.this.type = Global.this
val runsAfter = List("cleanup")
val runsAfter = List("delambdafy")
val runsRightAfter = None
} with GenBCode

Expand Down
87 changes: 65 additions & 22 deletions src/compiler/scala/tools/nsc/PhaseAssembly.scala
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ trait PhaseAssembly {
* Aux data structure for solving the constraint system
* The dependency graph container with helper methods for node and edge creation
*/
private class DependencyGraph {
private[nsc] class DependencyGraph {

/** Simple edge with to and from refs */
case class Edge(var frm: Node, var to: Node, var hard: Boolean)
Expand All @@ -40,14 +40,18 @@ trait PhaseAssembly {
var phaseobj: Option[List[SubComponent]] = None
val after = new mutable.HashSet[Edge]()
var before = new mutable.HashSet[Edge]()
var visited = false
var visited: VisitStatus = NotVisited
var level = 0

def allPhaseNames(): String = phaseobj match {
case None => phasename
case Some(lst) => lst.map(_.phaseName).reduceLeft(_+","+_)
}
}
sealed abstract class VisitStatus
case object NotVisited extends VisitStatus
case object Visiting extends VisitStatus
case object Visited extends VisitStatus

val nodes = new mutable.HashMap[String,Node]()
val edges = new mutable.HashSet[Edge]()
Expand Down Expand Up @@ -104,32 +108,66 @@ trait PhaseAssembly {
/* Test if there are cycles in the graph, assign levels to the nodes
* and collapse hard links into nodes
*/
def collapseHardLinksAndLevels(node: Node, lvl: Int) {
if (node.visited) {
dump("phase-cycle")
throw new FatalError(s"Cycle in phase dependencies detected at ${node.phasename}, created phase-cycle.dot")
}

if (node.level < lvl) node.level = lvl

var hls = Nil ++ node.before.filter(_.hard)
while (hls.size > 0) {
for (hl <- hls) {
def collapseHardLinks() {
for (node <- nodes.valuesIterator.toList) {
val hardBefores = node.before.iterator.filter(_.hard).toList
for (hl <- hardBefores) {
node.phaseobj = Some(node.phaseobj.get ++ hl.frm.phaseobj.get)
node.before = hl.frm.before
nodes -= hl.frm.phasename
edges -= hl
for (edge <- node.before) edge.to = node
}
hls = Nil ++ node.before.filter(_.hard)
}
node.visited = true
}

for (edge <- node.before) {
collapseHardLinksAndLevels( edge.frm, lvl + 1)
/* Test if there are cycles in the graph, assign levels to the nodes
* and collapse hard links into nodes
*/
def assignLevelsAndDetectCycles(node: Node) {
val stack = mutable.ArrayStack[Node]()
def visitNode(node: Node): Unit = {
node.visited = Visiting
for (edge <- node.before) {
val from = edge.frm
from.visited match {
case NotVisited =>
visitNode(edge.frm)
case Visiting =>
dump("phase-cycle")
throw new FatalError(s"Cycle in phase dependencies detected at ${node.phasename}, created phase-cycle.dot")
case Visited =>
}
}
node.visited = Visited
stack.push(node)
}
try {
visitNode(node)
} finally {
nodes.values.foreach(_.visited = NotVisited)
}

node.visited = false
val topoSort: Map[Node, Int] = stack.zipWithIndex.toMap
val root = node
assert(stack.head == root, stack)
root.level = 1

// Nodes that have been collapsed into their hard-linked predecessor
val collapsed: Map[String, Node] = stack.iterator.flatMap(p => p.phaseobj.toList.flatMap(_.map(x => (x.phaseName, p)))).toMap
def followHard(node: Node): Node = collapsed.getOrElse(node.phasename, node)

// find the longest path to the root node to assign as the level.
stack.iterator.drop(1).foreach { node =>
var n = node
var level = 1
while (n != root && n.after.nonEmpty) {
n = n.after.maxBy(edge => topoSort.get(followHard(edge.to))).to
level += 1
}
node.level = level
}
def phaseDebug = stack.toSeq.groupBy(_.level).toList.sortBy(_._1).map { case (level, nodes) => (level, nodes.sortBy(_.phasename).map(_.phaseobj.map(_.map(_.phaseName).mkString(":")).mkString(" ")).mkString(" "))}.mkString("\n")
debuglog("Phase assembly levels: " + phaseDebug)
}

/* Find all edges in the given graph that are hard links. For each hard link we
Expand Down Expand Up @@ -222,19 +260,24 @@ trait PhaseAssembly {

dump(3)

// test for cycles, assign levels and collapse hard links into nodes
graph.collapseHardLinksAndLevels(graph.getNodeByPhase("parser"), 1)
// collapse hard links into nodes
graph.collapseHardLinks()

dump(4)

// test for cycles, assign levels
graph.assignLevelsAndDetectCycles(graph.getNodeByPhase("parser"))

dump(5)

// assemble the compiler
graph.compilerPhaseList()
}

/** Given the phases set, will build a dependency graph from the phases set
* Using the aux. method of the DependencyGraph to create nodes and edges.
*/
private def phasesSetToDepGraph(phsSet: mutable.HashSet[SubComponent]): DependencyGraph = {
private[nsc] def phasesSetToDepGraph(phsSet: Iterable[SubComponent]): DependencyGraph = {
val graph = new DependencyGraph()

for (phs <- phsSet) {
Expand Down
6 changes: 1 addition & 5 deletions src/compiler/scala/tools/nsc/ast/TreeDSL.scala
Original file line number Diff line number Diff line change
Expand Up @@ -136,11 +136,7 @@ trait TreeDSL {

def NEW(tpt: Tree, args: Tree*): Tree = New(tpt, List(args.toList))

def NOT(tree: Tree) = tree match {
case Select(qual, _) if tree.symbol eq Boolean_not => qual
case _ => Select(tree, Boolean_not)
}

def NOT(tree: Tree) = Select(tree, Boolean_not)
def AND(guards: Tree*) = if (guards.isEmpty) EmptyTree else guards reduceLeft gen.mkAnd

def IF(tree: Tree) = new IfStart(tree, EmptyTree)
Expand Down
33 changes: 29 additions & 4 deletions src/compiler/scala/tools/nsc/backend/jvm/ClassfileWriters.scala
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import scala.reflect.io.PlainNioFile
import scala.tools.nsc.Global
import scala.tools.nsc.backend.jvm.BTypes.InternalName
import scala.tools.nsc.io.AbstractFile
import scala.tools.nsc.plugins.{OutputFileWriter, Plugin}
import scala.tools.nsc.util.JarFactory

abstract class ClassfileWriters {
val postProcessor: PostProcessor
Expand All @@ -41,7 +43,7 @@ abstract class ClassfileWriters {
*
* Operations are threadsafe.
*/
sealed trait ClassfileWriter {
sealed trait ClassfileWriter extends OutputFileWriter {
/**
* Write a classfile
*/
Expand Down Expand Up @@ -98,16 +100,29 @@ abstract class ClassfileWriters {
private def getUnderlying(sourceFile: AbstractFile, outputDir: AbstractFile) = underlying.getOrElse(outputDir, {
throw new Exception(s"Cannot determine output directory for ${sourceFile} with output ${outputDir}. Configured outputs are ${underlying.keySet}")
})
private def getUnderlying(outputDir: AbstractFile) = underlying.getOrElse(outputDir, {
throw new Exception(s"Cannot determine output for ${outputDir}. Configured outputs are ${underlying.keySet}")
})

override def writeClass(className: InternalName, bytes: Array[Byte], sourceFile: AbstractFile): Unit = {
getUnderlying(sourceFile, sourceToOutput(sourceFile)).writeFile(classRelativePath(className), bytes)
}

override def writeFile(relativePath: String, data: Array[Byte], outputDir: AbstractFile): Unit = {
getUnderlying(outputDir).writeFile(relativePath, data)
}

override def close(): Unit = underlying.values.foreach(_.close())
}
private final class SingleClassWriter(underlying: FileWriter) extends ClassfileWriter {
override def writeClass(className: InternalName, bytes: Array[Byte], sourceFile: AbstractFile): Unit = {
underlying.writeFile(classRelativePath(className), bytes)
}

override def writeFile(relativePath: String, data: Array[Byte], outputDir: AbstractFile): Unit = {
underlying.writeFile(relativePath, data)
}

override def close(): Unit = underlying.close()
}

Expand All @@ -123,6 +138,10 @@ abstract class ClassfileWriters {
}
}

override def writeFile(relativePath: String, data: Array[Byte], outputDir: AbstractFile): Unit = {
basic.writeFile(relativePath, data, outputDir)
}

override def close(): Unit = {
basic.close()
asmp.foreach(_.close())
Expand All @@ -138,6 +157,10 @@ abstract class ClassfileWriters {
finally statistics.stopTimer(statistics.bcodeWriteTimer, snap)
}

override def writeFile(relativePath: String, data: Array[Byte], outputDir: AbstractFile): Unit = {
underlying.writeFile(relativePath, data, outputDir)
}

override def close(): Unit = underlying.close()
}
}
Expand All @@ -151,7 +174,8 @@ abstract class ClassfileWriters {
def apply(global: Global, file: AbstractFile, jarManifestMainClass: Option[String]): FileWriter = {
if (file hasExtension "jar") {
val jarCompressionLevel = global.settings.YjarCompressionLevel.value
new JarEntryWriter(file, jarManifestMainClass, jarCompressionLevel)
val jarFactory = Class.forName(global.settings.YjarFactory.value).asSubclass(classOf[JarFactory]).newInstance()
new JarEntryWriter(file, jarManifestMainClass, jarCompressionLevel, jarFactory, global.plugins)
} else if (file.isVirtual) {
new VirtualFileWriter(file)
} else if (file.isDirectory) {
Expand All @@ -162,7 +186,7 @@ abstract class ClassfileWriters {
}
}

private final class JarEntryWriter(file: AbstractFile, mainClass: Option[String], compressionLevel: Int) extends FileWriter {
private final class JarEntryWriter(file: AbstractFile, mainClass: Option[String], compressionLevel: Int, jarFactory: JarFactory, plugins: List[Plugin]) extends FileWriter {
//keep these imports local - avoid confusion with scala naming
import java.util.jar.Attributes.Name
import java.util.jar.{JarOutputStream, Manifest}
Expand All @@ -172,7 +196,8 @@ abstract class ClassfileWriters {
val jarWriter: JarOutputStream = {
val manifest = new Manifest()
mainClass foreach { c => manifest.getMainAttributes.put(Name.MAIN_CLASS, c) }
val jar = new JarOutputStream(new BufferedOutputStream(new FileOutputStream(file.file), 64000), manifest)
plugins foreach (_.augmentManifest(file, manifest))
val jar = jarFactory.createJarOutputStream(file, manifest)
jar.setLevel(compressionLevel)
if (storeOnly) jar.setMethod(ZipOutputStream.STORED)
jar
Expand Down
7 changes: 7 additions & 0 deletions src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ abstract class GenBCode extends SubComponent {
initialize()
super.run() // invokes `apply` for each compilation unit
generatedClassHandler.complete()
writeOtherFiles()
} finally {
this.close()
}
Expand All @@ -96,6 +97,12 @@ abstract class GenBCode extends SubComponent {
generatedClassHandler = GeneratedClassHandler(global)
statistics.stopTimer(statistics.bcodeInitTimer, initStart)
}
def writeOtherFiles(): Unit = {
global.plugins foreach {
plugin =>
plugin.writeAdditionalOutputs(postProcessor.classfileWriter)
}
}

private def close(): Unit = {
postProcessor.classfileWriter.close()
Expand Down
19 changes: 10 additions & 9 deletions src/compiler/scala/tools/nsc/classpath/AggregateClassPath.scala
Original file line number Diff line number Diff line change
Expand Up @@ -30,21 +30,21 @@ import scala.tools.nsc.util.ClassRepresentation
case class AggregateClassPath(aggregates: Seq[ClassPath]) extends ClassPath {
override def findClassFile(className: String): Option[AbstractFile] = {
val (pkg, simpleClassName) = PackageNameUtils.separatePkgAndClassNames(className)
aggregatesForPackage(pkg).iterator.map(_.findClassFile(className)).collectFirst {
aggregatesForPackage(PackageName(pkg)).iterator.map(_.findClassFile(className)).collectFirst {
case Some(x) => x
}
}
private[this] val packageIndex: collection.mutable.Map[String, Seq[ClassPath]] = collection.mutable.Map()
private def aggregatesForPackage(pkg: String): Seq[ClassPath] = packageIndex.synchronized {
packageIndex.getOrElseUpdate(pkg, aggregates.filter(_.hasPackage(pkg)))
private def aggregatesForPackage(pkg: PackageName): Seq[ClassPath] = packageIndex.synchronized {
packageIndex.getOrElseUpdate(pkg.dottedString, aggregates.filter(_.hasPackage(pkg)))
}

// This method is performance sensitive as it is used by SBT's ExtractDependencies phase.
override def findClass(className: String): Option[ClassRepresentation] = {
val (pkg, simpleClassName) = PackageNameUtils.separatePkgAndClassNames(className)

def findEntry(isSource: Boolean): Option[ClassRepresentation] = {
aggregatesForPackage(pkg).iterator.map(_.findClass(className)).collectFirst {
aggregatesForPackage(PackageName(pkg)).iterator.map(_.findClass(className)).collectFirst {
case Some(s: SourceFileEntry) if isSource => s
case Some(s: ClassFileEntry) if !isSource => s
}
Expand All @@ -66,19 +66,20 @@ case class AggregateClassPath(aggregates: Seq[ClassPath]) extends ClassPath {

override def asSourcePathString: String = ClassPath.join(aggregates map (_.asSourcePathString): _*)

override private[nsc] def packages(inPackage: String): Seq[PackageEntry] = {
override private[nsc] def packages(inPackage: PackageName): Seq[PackageEntry] = {
val aggregatedPackages = aggregates.flatMap(_.packages(inPackage)).distinct
aggregatedPackages
}

override private[nsc] def classes(inPackage: String): Seq[ClassFileEntry] =
override private[nsc] def classes(inPackage: PackageName): Seq[ClassFileEntry] =
getDistinctEntries(_.classes(inPackage))

override private[nsc] def sources(inPackage: String): Seq[SourceFileEntry] =
override private[nsc] def sources(inPackage: PackageName): Seq[SourceFileEntry] =
getDistinctEntries(_.sources(inPackage))

override private[nsc] def hasPackage(pkg: String) = aggregates.exists(_.hasPackage(pkg))
override private[nsc] def list(inPackage: String): ClassPathEntries = {
override private[nsc] def hasPackage(pkg: PackageName) = aggregates.exists(_.hasPackage(pkg))
override private[nsc] def list(inPackage: PackageName): ClassPathEntries = {

val (packages, classesAndSources) = aggregates.map { cp =>
try {
cp.list(inPackage)
Expand Down
Loading