Skip to content

Commit

Permalink
Fix build failure of the Scala 2.10 bridge sources
Browse files Browse the repository at this point in the history
The fix was to just copy over the main code and adapt it to Scala 2.10
compiler API.
  • Loading branch information
gkossakowski committed Mar 16, 2016
1 parent 49f8786 commit 469a3ac
Show file tree
Hide file tree
Showing 7 changed files with 348 additions and 136 deletions.
19 changes: 17 additions & 2 deletions internal/compiler-bridge/src-2.10/main/scala/xsbt/Analyzer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,23 @@ final class Analyzer(val global: CallbackGlobal) extends LocateClassFile {
for (iclass <- unit.icode) {
val sym = iclass.symbol
def addGenerated(separatorRequired: Boolean): Unit = {
for (classFile <- outputDirs map (fileForClass(_, sym, separatorRequired)) find (_.exists))
callback.generatedClass(sourceFile, classFile, className(sym, '.', separatorRequired))
for (classFile <- outputDirs map (fileForClass(_, sym, separatorRequired)) find (_.exists)) {
assert(sym.isClass, s"${sym.fullName} is not a class")
// we would like to use Symbol.isLocalClass but that relies on Symbol.owner which
// is lost at this point due to lambdalift
// the LocalNonLocalClass.isLocal can return None, which means, we're asking about
// the class it has not seen before. How's that possible given we're performing a lookup
// for every declared class in Dependency phase? We can have new classes introduced after
// Dependency phase has ran. For example, the implementation classes for traits.
val isLocalClass = localToNonLocalClass.isLocal(sym).getOrElse(true)
if (!isLocalClass) {
val srcClassName = className(sym)
val binaryClassName = flatclassName(sym, '.', separatorRequired)
callback.generatedNonLocalClass(sourceFile, classFile, binaryClassName, srcClassName)
} else {
callback.generatedLocalClass(sourceFile, classFile)
}
}
}
if (sym.isModuleClass && !sym.isImplClass) {
if (isTopLevelModule(sym) && sym.companionClass == NoSymbol)
Expand Down
31 changes: 31 additions & 0 deletions internal/compiler-bridge/src-2.10/main/scala/xsbt/ClassName.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package xsbt

/**
* Utility methods for creating (source|binary) class names for a Symbol.
*/
trait ClassName {
val global: CallbackGlobal
import global._

/**
* Creates a flat (binary) name for a class symbol `s`.
*/
protected def flatname(s: Symbol, separator: Char) =
atPhase(currentRun.flattenPhase.next) { s fullName separator }

/**
* Create a (source) name for a class symbol `s`.
*/
protected def className(s: Symbol): String = pickledName(s)

private def pickledName(s: Symbol): String =
atPhase(currentRun.picklerPhase) { s.fullName }

protected def isTopLevelModule(sym: Symbol): Boolean =
atPhase(currentRun.picklerPhase.next) {
sym.isModuleClass && !sym.isImplClass && !sym.isNestedClass
}

protected def flatclassName(s: Symbol, sep: Char, dollarRequired: Boolean): String =
flatname(s, sep) + (if (dollarRequired) "$" else "")
}
211 changes: 146 additions & 65 deletions internal/compiler-bridge/src-2.10/main/scala/xsbt/Dependency.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
*/
package xsbt

import scala.tools.nsc.{ io, symtab, Phase }
import io.{ AbstractFile, PlainFile, ZipArchive }
import symtab.Flags
import java.io.File

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

import java.io.File
import scala.tools.nsc.io.{ PlainFile, ZipArchive }
import scala.tools.nsc.Phase

object Dependency {
def name = "xsbt-dependency"
Expand All @@ -33,59 +33,150 @@ final class Dependency(val global: CallbackGlobal) extends LocateClassFile {
import global._

def newPhase(prev: Phase): Phase = new DependencyPhase(prev)
private class DependencyPhase(prev: Phase) extends GlobalPhase(prev) {
private class DependencyPhase(prev: Phase) extends Phase(prev) {
override def description = "Extracts dependency information"
def name = Dependency.name
def apply(unit: CompilationUnit): Unit = {
if (!unit.isJava) {
def run: Unit = {
for (unit <- currentRun.units if !unit.isJava) {
// build dependencies structure
val sourceFile = unit.source.file.file
if (global.callback.nameHashing) {
val dependencyExtractor = new ExtractDependenciesTraverser
dependencyExtractor.traverse(unit.body)

dependencyExtractor.topLevelDependencies foreach processDependency(context = DependencyByMemberRef)
dependencyExtractor.topLevelInheritanceDependencies foreach processDependency(context = DependencyByInheritance)
dependencyExtractor.memberRefDependencies foreach processDependency(context = DependencyByMemberRef)
dependencyExtractor.inheritanceDependencies foreach processDependency(context = DependencyByInheritance)
dependencyExtractor.localInheritanceDependencies foreach processDependency(context = LocalDependencyByInheritance)
processTopLevelImportDependencies(dependencyExtractor.topLevelImportDependencies)
} else {
unit.depends foreach processDependency(context = DependencyByMemberRef)
inheritedDependencies.getOrElse(sourceFile, Nil: Iterable[Symbol]) foreach processDependency(context = DependencyByInheritance)
throw new UnsupportedOperationException("Turning off name hashing is not supported in class-based dependency trackging.")
}
/**
/*
* Registers top level import dependencies as coming from a first top level class/trait/object declared
* in the compilation unit.
* If there's no top level template (class/trait/object def) declared in the compilation unit but `deps`
* is non-empty, a warning is issued.
*/
def processTopLevelImportDependencies(deps: Iterator[Symbol]): Unit = if (deps.nonEmpty) {
val classOrModuleDef = firstClassOrModuleDef(unit.body)
classOrModuleDef match {
case Some(classOrModuleDef) =>
val sym = classOrModuleDef.symbol
val firstClassSymbol = if (sym.isModule) sym.moduleClass else sym
deps foreach { dep =>
processDependency(context = DependencyByMemberRef)(ClassDependency(firstClassSymbol, dep))
}
case None =>
reporter.warning(
unit.position(0),
"""|Found top level imports but no class, trait or object is defined in the compilation unit.
|The incremental compiler cannot record the dependency information in such case.
|Some errors like unused import referring to a non-existent class might not be reported.""".stripMargin
)
}
}
/*
* Handles dependency on given symbol by trying to figure out if represents a term
* that is coming from either source code (not necessarily compiled in this compilation
* run) or from class file and calls respective callback method.
*/
def processDependency(context: DependencyContext)(on: Symbol) = {
def binaryDependency(file: File, className: String) = callback.binaryDependency(file, className, sourceFile, context)
val onSource = on.sourceFile
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)
val onSource = dep.to.sourceFile
if (onSource == null) {
classFile(on) match {
case Some((f, className, inOutDir)) =>
if (inOutDir && on.isJavaDefined) registerTopLevelSym(on)
classFile(dep.to) match {
case Some((f, binaryClassName, inOutDir)) =>
if (inOutDir && dep.to.isJavaDefined) registerTopLevelSym(dep.to)
f match {
case ze: ZipArchive#Entry => for (zip <- ze.underlyingSource; zipFile <- Option(zip.file)) binaryDependency(zipFile, className)
case pf: PlainFile => binaryDependency(pf.file, className)
case _ => ()
case ze: ZipArchive#Entry =>
for (zip <- ze.underlyingSource; zipFile <- Option(zip.file)) binaryDependency(zipFile, binaryClassName)
case pf: PlainFile => binaryDependency(pf.file, binaryClassName)
case _ => ()
}
case None => ()
}
} else if (onSource.file != sourceFile)
callback.sourceDependency(onSource.file, sourceFile, context)
} else if (onSource.file != sourceFile) {
val onClassName = className(dep.to)
callback.classDependency(onClassName, fromClassName, context)
}
}
}
}
}

private case class ClassDependency(from: Symbol, to: Symbol)

private class ExtractDependenciesTraverser extends Traverser {
private val _dependencies = collection.mutable.HashSet.empty[Symbol]
protected def addDependency(dep: Symbol): Unit = { if (dep ne NoSymbol) _dependencies += dep }
def dependencies: Iterator[Symbol] = _dependencies.iterator
def topLevelDependencies: Iterator[Symbol] = _dependencies.map(enclosingTopLevelClass).iterator
import scala.collection.mutable.HashSet
// are we traversing an Import node at the moment?
private var inImportNode = false

private val _memberRefDependencies = HashSet.empty[ClassDependency]
private val _inheritanceDependencies = HashSet.empty[ClassDependency]
private val _localInheritanceDependencies = HashSet.empty[ClassDependency]
private val _topLevelImportDependencies = HashSet.empty[Symbol]
private def enclOrModuleClass(s: Symbol): Symbol =
if (s.isModule) s.moduleClass else s.enclClass

/**
* 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 addClassDependency(deps: HashSet[ClassDependency], fromClass: Symbol, dep: Symbol): Unit = {
assert(
fromClass.isClass,
s"The ${fromClass.fullName} defined at ${fromClass.fullLocationString} is not a class symbol."
)
val depClass = enclOrModuleClass(dep)
if (fromClass.associatedFile != depClass.associatedFile) {
deps += ClassDependency(fromClass, depClass)
()
}
}

def addTopLevelImportDependency(dep: global.Symbol): Unit = {
val depClass = enclOrModuleClass(dep)
if (!dep.hasPackageFlag) {
_topLevelImportDependencies += depClass
()
}
}

private val _inheritanceDependencies = collection.mutable.HashSet.empty[Symbol]
protected def addInheritanceDependency(dep: Symbol): Unit = if (dep ne NoSymbol) _inheritanceDependencies += dep
def inheritanceDependencies: Iterator[Symbol] = _inheritanceDependencies.iterator
def topLevelInheritanceDependencies: Iterator[Symbol] = _inheritanceDependencies.map(enclosingTopLevelClass).iterator
private def addDependency(dep: Symbol): Unit = {
val (fromClass, _) = resolveDependencySource
if (fromClass == NoSymbol || fromClass.hasPackageFlag) {
if (inImportNode) addTopLevelImportDependency(dep)
else
debugwarn(s"No enclosing class. Discarding dependency on $dep (currentOwner = $currentOwner).")
} else {
addClassDependency(_memberRefDependencies, fromClass, dep)
}
}
private def addInheritanceDependency(dep: Symbol): Unit = {
val (fromClass, isLocal) = resolveDependencySource
if (isLocal)
addClassDependency(_localInheritanceDependencies, fromClass, dep)
else
addClassDependency(_inheritanceDependencies, fromClass, dep)
}
def memberRefDependencies: Iterator[ClassDependency] = _memberRefDependencies.iterator
def inheritanceDependencies: Iterator[ClassDependency] = _inheritanceDependencies.iterator
def topLevelImportDependencies: Iterator[Symbol] = _topLevelImportDependencies.iterator
def localInheritanceDependencies: Iterator[ClassDependency] = _localInheritanceDependencies.iterator

/*
* Some macros appear to contain themselves as original tree.
Expand All @@ -98,6 +189,7 @@ final class Dependency(val global: CallbackGlobal) extends LocateClassFile {

override def traverse(tree: Tree): Unit = tree match {
case Import(expr, selectors) =>
inImportNode = true
traverse(expr)
selectors.foreach {
case ImportSelector(nme.WILDCARD, _, null, _) =>
Expand All @@ -109,7 +201,7 @@ final class Dependency(val global: CallbackGlobal) extends LocateClassFile {
addDependency(lookupImported(name.toTermName))
addDependency(lookupImported(name.toTypeName))
}

inImportNode = false
/*
* Idents are used in number of situations:
* - to refer to local variable
Expand All @@ -126,7 +218,8 @@ final class Dependency(val global: CallbackGlobal) extends LocateClassFile {

case Template(parents, self, body) =>
// use typeSymbol to dealias type aliases -- we want to track the dependency on the real class in the alias's RHS
def flattenTypeToSymbols(tp: Type): List[Symbol] = if (tp eq null) Nil else tp match {
def flattenTypeToSymbols(tp: Type): List[Symbol] = if (tp eq null) Nil
else tp match {
// rt.typeSymbol is redundant if we list out all parents, TODO: what about rt.decls?
case rt: RefinedType => rt.parents.flatMap(flattenTypeToSymbols)
case _ => List(tp.typeSymbol)
Expand All @@ -144,48 +237,36 @@ final class Dependency(val global: CallbackGlobal) extends LocateClassFile {
traverseTrees(body)

// In some cases (eg. macro annotations), `typeTree.tpe` may be null. See sbt/sbt#1593 and sbt/sbt#1655.
case typeTree: TypeTree if typeTree.tpe != null => symbolsInType(typeTree.tpe) foreach addDependency

case MacroExpansionOf(original) if inspectedOriginalTrees.add(original) => traverse(original)
case typeTree: TypeTree if typeTree.tpe != null =>
symbolsInType(typeTree.tpe) foreach addDependency
case MacroExpansionOf(original) if inspectedOriginalTrees.add(original) =>
traverse(original)
case _: ClassDef | _: ModuleDef if tree.symbol != null && tree.symbol != NoSymbol =>
// make sure we cache lookups for all classes declared in the compilation unit; the recorded information
// will be used in Analyzer phase
val sym = if (tree.symbol.isModule) tree.symbol.moduleClass else tree.symbol
localToNonLocalClass.resolveNonLocal(sym)
super.traverse(tree)
case other => super.traverse(other)
}

private def symbolsInType(tp: Type): Set[Symbol] = {
val typeSymbolCollector =
new CollectTypeTraverser({
case tpe if (tpe != null) && !tpe.typeSymbolDirect.isPackage => tpe.typeSymbolDirect
new CollectTypeCollector({
case tpe if (tpe != null) && !tpe.typeSymbolDirect.hasPackageFlag => tpe.typeSymbolDirect
})

typeSymbolCollector.traverse(tp)
typeSymbolCollector.collected.toSet
typeSymbolCollector.collect(tp).toSet

}
}

/**
* Traverses given type and collects result of applying a partial function `pf`.
*
* NOTE: This class exists in Scala 2.10 as CollectTypeCollector but does not in earlier
* versions (like 2.9) of Scala compiler that incremental cmpiler supports so we had to
* reimplement that class here.
*/
private final class CollectTypeTraverser[T](pf: PartialFunction[Type, T]) extends TypeTraverser {
var collected: List[T] = Nil
def traverse(tpe: Type): Unit = {
if (pf.isDefinedAt(tpe))
collected = pf(tpe) :: collected
mapOver(tpe)
def firstClassOrModuleDef(tree: Tree): Option[Tree] = {
tree foreach {
case t @ ((_: ClassDef) | (_: ModuleDef)) => return Some(t)
case _ => ()
}
None
}

/** Copied straight from Scala 2.10 as it does not exist in Scala 2.9 compiler */
private final def debuglog(msg: => String): Unit = if (settings.debug.value) log(msg)

/**
* We capture enclosing classes only because that's what CompilationUnit.depends does and we don't want
* to deviate from old behaviour too much for now.
*
* NOTE: for Scala 2.8 and 2.9 this method is provided through SymbolCompat
*/
private def enclosingTopLevelClass(sym: Symbol): Symbol = sym.enclosingTopLevelClass

}
Loading

0 comments on commit 469a3ac

Please sign in to comment.