Skip to content

Commit

Permalink
Speedup Dependency phase.
Browse files Browse the repository at this point in the history
- use associated file from symbol instead of querying for one
- cache current source file with all attributes
- use faster extracting symbols from type (no collections is created)
- invoke callback asynchronously (in executor with one thread).
  • Loading branch information
romanowski committed Jan 23, 2017
1 parent b77a2b3 commit 55a5d38
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 25 deletions.
89 changes: 68 additions & 21 deletions internal/compiler-bridge/src/main/scala/xsbt/Dependency.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
package xsbt

import java.io.File
import java.util.concurrent.{ TimeUnit, Executors }

import xsbti.AnalysisCallback
import xsbti.api.DependencyContext
import DependencyContext._

Expand Down Expand Up @@ -36,6 +38,26 @@ final class Dependency(val global: CallbackGlobal) extends LocateClassFile with
private class DependencyPhase(prev: Phase) extends GlobalPhase(prev) {
override def description = "Extracts dependency information"
def name = Dependency.name

val executor = Executors.newFixedThreadPool(1)

override def run(): Unit = {
super.run()
// Wait on all callback calls to finish
executor.shutdown()
executor.awaitTermination(20L, TimeUnit.MINUTES)
()
}

private def withCallback(op: AnalysisCallback => Unit): Unit = {
executor.submit(new Runnable {
override def run(): Unit = {
op(callback)
}
})
()
}

def apply(unit: CompilationUnit): Unit = {
if (!unit.isJava) {
// build dependencies structure
Expand Down Expand Up @@ -83,12 +105,11 @@ final class Dependency(val global: CallbackGlobal) extends LocateClassFile with
def processDependency(context: DependencyContext)(dep: ClassDependency): Unit = {
val fromClassName = className(dep.from)
def binaryDependency(file: File, onBinaryClassName: String) =
callback.binaryDependency(file, onBinaryClassName, fromClassName, sourceFile, context)
withCallback(_.binaryDependency(file, onBinaryClassName, fromClassName, sourceFile, context))
val onSource = dep.to.sourceFile
if (onSource == null) {
classFile(dep.to) match {
case Some((f, binaryClassName, inOutDir)) =>
if (inOutDir && dep.to.isJavaDefined) registerTopLevelSym(dep.to)
case Some((f, binaryClassName)) =>
f match {
case ze: ZipArchive#Entry =>
for (zip <- ze.underlyingSource; zipFile <- Option(zip.file)) binaryDependency(zipFile, binaryClassName)
Expand All @@ -99,7 +120,7 @@ final class Dependency(val global: CallbackGlobal) extends LocateClassFile with
}
} else if (onSource.file != sourceFile) {
val onClassName = className(dep.to)
callback.classDependency(onClassName, fromClassName, context)
withCallback(_.classDependency(onClassName, fromClassName, context))
}
}
}
Expand All @@ -120,20 +141,38 @@ final class Dependency(val global: CallbackGlobal) extends LocateClassFile with
private def enclOrModuleClass(s: Symbol): Symbol =
if (s.isModule) s.moduleClass else s.enclClass

case class DependencySource(owner: Symbol) {
val (fromClass: Symbol, isLocal: Boolean) = {
val fromClass = enclOrModuleClass(owner)
if (fromClass == NoSymbol || fromClass.hasPackageFlag)
(fromClass, false)
else {
val fromNonLocalClass = localToNonLocalClass.resolveNonLocal(fromClass)
assert(!(fromClass == NoSymbol || fromClass.hasPackageFlag))
(fromNonLocalClass, fromClass != fromNonLocalClass)
}
}
}

private var _currentDependencySource: DependencySource = null

/**
* Resolves dependency source by getting the enclosing class for `currentOwner`
* and then looking up the most inner enclosing class that is non local.
* The second returned value indicates if the enclosing class for `currentOwner`
* is a local class.
*/
private def resolveDependencySource: (Symbol, Boolean) = {
val fromClass = enclOrModuleClass(currentOwner)
if (fromClass == NoSymbol || fromClass.hasPackageFlag)
(fromClass, false)
else {
val fromNonLocalClass = localToNonLocalClass.resolveNonLocal(fromClass)
assert(!(fromClass == NoSymbol || fromClass.hasPackageFlag))
(fromNonLocalClass, fromClass != fromNonLocalClass)
private def resolveDependencySource(): DependencySource = {
def newOne(): DependencySource = {
val fresh = DependencySource(currentOwner)
_currentDependencySource = fresh
_currentDependencySource
}
_currentDependencySource match {
case null => newOne()
case cached if currentOwner == cached.owner =>
cached
case _ => newOne()
}
}
private def addClassDependency(deps: HashSet[ClassDependency], fromClass: Symbol, dep: Symbol): Unit = {
Expand All @@ -159,11 +198,11 @@ final class Dependency(val global: CallbackGlobal) extends LocateClassFile with
private def addTreeDependency(tree: Tree): Unit = {
addDependency(tree.symbol)
if (tree.tpe != null)
symbolsInType(tree.tpe).foreach(addDependency)
foreachSymbolInType(tree.tpe)(addDependency)
()
}
private def addDependency(dep: Symbol): Unit = {
val (fromClass, _) = resolveDependencySource
val fromClass = resolveDependencySource().fromClass
if (fromClass == NoSymbol || fromClass.hasPackageFlag) {
if (inImportNode) addTopLevelImportDependency(dep)
else
Expand All @@ -173,11 +212,11 @@ final class Dependency(val global: CallbackGlobal) extends LocateClassFile with
}
}
private def addInheritanceDependency(dep: Symbol): Unit = {
val (fromClass, isLocal) = resolveDependencySource
if (isLocal)
addClassDependency(_localInheritanceDependencies, fromClass, dep)
val dependencySource = resolveDependencySource()
if (dependencySource.isLocal)
addClassDependency(_localInheritanceDependencies, dependencySource.fromClass, dep)
else
addClassDependency(_inheritanceDependencies, fromClass, dep)
addClassDependency(_inheritanceDependencies, dependencySource.fromClass, dep)
}
def memberRefDependencies: Iterator[ClassDependency] = _memberRefDependencies.iterator
def inheritanceDependencies: Iterator[ClassDependency] = _inheritanceDependencies.iterator
Expand Down Expand Up @@ -236,10 +275,10 @@ final class Dependency(val global: CallbackGlobal) extends LocateClassFile with

debuglog("Parent types for " + tree.symbol + " (self: " + self.tpt.tpe + "): " + inheritanceTypes + " with symbols " + inheritanceSymbols.map(_.fullName))

inheritanceSymbols.foreach(addInheritanceDependency)
inheritanceSymbols.foreach(addSymbolFromParent)
inheritanceTypes.foreach(addSymbolsFromType)
addSymbolsFromType(self.tpt.tpe)

val allSymbols = (inheritanceTypes + self.tpt.tpe).flatMap(symbolsInType)
(allSymbols ++ inheritanceSymbols).foreach(addDependency)
traverseTrees(body)

// In some cases (eg. macro annotations), `typeTree.tpe` may be null. See sbt/sbt#1593 and sbt/sbt#1655.
Expand All @@ -256,6 +295,14 @@ final class Dependency(val global: CallbackGlobal) extends LocateClassFile with
super.traverse(tree)
case other => super.traverse(other)
}

val addSymbolFromParent: Symbol => Unit = { symbol =>
addInheritanceDependency(symbol)
addDependency(symbol)
}
val addSymbolsFromType: Type => Unit = { tpe =>
foreachSymbolInType(tpe)(addDependency)
}
}

def firstClassOrModuleDef(tree: Tree): Option[Tree] = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
*/
package xsbt

import scala.reflect.io.NoAbstractFile
import scala.tools.nsc.symtab.Flags
import scala.tools.nsc.io.AbstractFile

Expand All @@ -16,13 +17,13 @@ abstract class LocateClassFile extends ClassName {
import global._

private[this] final val classSeparator = '.'
protected def classFile(sym: Symbol): Option[(AbstractFile, String, Boolean)] =
protected def classFile(sym: Symbol): Option[(AbstractFile, String)] =
// package can never have a corresponding class file; this test does not
// catch package objects (that do not have this flag set)
if (sym hasFlag scala.tools.nsc.symtab.Flags.PACKAGE) None else {
import scala.tools.nsc.symtab.Flags
val binaryClassName = flatname(sym, classSeparator) + sym.moduleSuffix
findClass(binaryClassName).map { case (file, inOut) => (file, binaryClassName, inOut) } orElse {
val file = sym.associatedFile

if (file == NoAbstractFile) {
if (isTopLevelModule(sym)) {
val linked = sym.companionClass
if (linked == NoSymbol)
Expand All @@ -31,6 +32,8 @@ abstract class LocateClassFile extends ClassName {
classFile(linked)
} else
None
} else {
Some((file, flatname(sym, classSeparator) + sym.moduleSuffix))
}
}

Expand Down

0 comments on commit 55a5d38

Please sign in to comment.