2009-11-16 14:46:47 +01:00
|
|
|
/* sbt -- Simple Build Tool
|
2011-03-11 22:57:15 +01:00
|
|
|
* Copyright 2008, 2009, 2010, 2011 Mark Harrah
|
2009-11-16 14:46:47 +01:00
|
|
|
*/
|
|
|
|
|
package xsbt
|
|
|
|
|
|
|
|
|
|
import java.io.File
|
|
|
|
|
import scala.tools.nsc.{io, plugins, symtab, Global, Phase}
|
|
|
|
|
import io.{AbstractFile, PlainFile, ZipArchive}
|
|
|
|
|
import plugins.{Plugin, PluginComponent}
|
|
|
|
|
import symtab.Flags
|
|
|
|
|
import scala.collection.mutable.{HashMap, HashSet, ListBuffer}
|
2010-06-16 02:38:18 +02:00
|
|
|
import xsbti.api.{ClassLike, DefinitionType, PathComponent, SimpleType}
|
2009-11-16 14:46:47 +01:00
|
|
|
|
|
|
|
|
object API
|
|
|
|
|
{
|
|
|
|
|
val name = "xsbt-api"
|
2010-06-16 02:38:18 +02:00
|
|
|
// for 2.7 compatibility: this class was removed in 2.8
|
|
|
|
|
type ImplicitMethodType = AnyRef
|
2009-11-16 14:46:47 +01:00
|
|
|
}
|
2011-02-09 02:30:15 +01:00
|
|
|
// imports ImplicitMethodType, which will preserve source compatibility in 2.7 for defDef
|
|
|
|
|
import API._
|
|
|
|
|
|
2010-06-16 02:38:18 +02:00
|
|
|
final class API(val global: Global, val callback: xsbti.AnalysisCallback) extends Compat
|
2009-11-16 14:46:47 +01:00
|
|
|
{
|
|
|
|
|
import global._
|
|
|
|
|
def error(msg: String) = throw new RuntimeException(msg)
|
|
|
|
|
|
|
|
|
|
def newPhase(prev: Phase) = new ApiPhase(prev)
|
|
|
|
|
class ApiPhase(prev: Phase) extends Phase(prev)
|
|
|
|
|
{
|
|
|
|
|
override def description = "Extracts the public API from source files."
|
|
|
|
|
def name = API.name
|
2009-11-23 04:54:17 +01:00
|
|
|
def run: Unit =
|
|
|
|
|
{
|
2010-07-02 12:57:03 +02:00
|
|
|
val start = System.currentTimeMillis
|
|
|
|
|
currentRun.units.foreach(processUnit)
|
|
|
|
|
val stop = System.currentTimeMillis
|
|
|
|
|
println("API phase took : " + ((stop - start)/1000.0) + " s")
|
2009-11-23 04:54:17 +01:00
|
|
|
}
|
2011-04-27 03:19:56 +02:00
|
|
|
def processUnit(unit: CompilationUnit) = if(!unit.isJava) processScalaUnit(unit)
|
|
|
|
|
def processScalaUnit(unit: CompilationUnit)
|
2009-11-16 14:46:47 +01:00
|
|
|
{
|
|
|
|
|
val sourceFile = unit.source.file.file
|
2010-10-23 03:55:16 +02:00
|
|
|
println("Traversing " + sourceFile)
|
2009-11-16 14:46:47 +01:00
|
|
|
val traverser = new TopLevelHandler(sourceFile)
|
|
|
|
|
traverser.apply(unit.body)
|
|
|
|
|
val packages = traverser.packages.toArray[String].map(p => new xsbti.api.Package(p))
|
|
|
|
|
val source = new xsbti.api.Source(packages, traverser.definitions.toArray[xsbti.api.Definition])
|
2010-10-23 03:55:16 +02:00
|
|
|
forceStructures()
|
2010-10-30 23:46:56 +02:00
|
|
|
clearCaches()
|
2009-11-16 14:46:47 +01:00
|
|
|
callback.api(sourceFile, source)
|
2010-06-16 02:38:18 +02:00
|
|
|
}
|
2009-11-16 14:46:47 +01:00
|
|
|
}
|
2010-10-23 03:55:16 +02:00
|
|
|
|
2010-10-30 23:46:56 +02:00
|
|
|
// this cache reduces duplicate work both here and when persisting
|
|
|
|
|
// caches on other structures had minimal effect on time and cache size
|
|
|
|
|
// (tried: Definition, Modifier, Path, Id, String)
|
2010-11-10 02:49:23 +01:00
|
|
|
private[this] val typeCache = new HashMap[(Symbol,Type), xsbti.api.Type]
|
2010-10-30 23:46:56 +02:00
|
|
|
// these caches are necessary for correctness
|
2010-10-23 03:55:16 +02:00
|
|
|
private[this] val structureCache = new HashMap[Symbol, xsbti.api.Structure]
|
2010-11-10 02:49:23 +01:00
|
|
|
private[this] val classLikeCache = new HashMap[(Symbol,Symbol), xsbti.api.ClassLike]
|
2010-10-23 03:55:16 +02:00
|
|
|
private[this] val pending = new HashSet[xsbti.api.Lazy[_]]
|
|
|
|
|
|
2010-10-30 23:46:56 +02:00
|
|
|
// to mitigate "temporary leaks" like that caused by NoPhase in 2.8.0,
|
|
|
|
|
// this ensures this class is not retaining objects
|
|
|
|
|
private def clearCaches()
|
|
|
|
|
{
|
|
|
|
|
typeCache.clear()
|
|
|
|
|
structureCache.clear()
|
|
|
|
|
classLikeCache.clear()
|
|
|
|
|
}
|
|
|
|
|
|
2010-10-23 03:55:16 +02:00
|
|
|
// call back to the xsbti.SafeLazy class in main sbt code to construct a SafeLazy instance
|
|
|
|
|
// we pass a thunk, whose class is loaded by the interface class loader (this class's loader)
|
|
|
|
|
// SafeLazy ensures that once the value is forced, the thunk is nulled out and so
|
|
|
|
|
// references to the thunk's classes are not retained. Specifically, it allows the interface classes
|
2011-03-11 22:57:15 +01:00
|
|
|
// (those in this subproject) to be garbage collected after compilation.
|
2010-10-23 03:55:16 +02:00
|
|
|
private[this] val safeLazy = Class.forName("xsbti.SafeLazy").getMethod("apply", classOf[xsbti.F0[_]])
|
|
|
|
|
private def lzy[S <: AnyRef](s: => S): xsbti.api.Lazy[S] =
|
|
|
|
|
{
|
|
|
|
|
val z = safeLazy.invoke(null, Message(s)).asInstanceOf[xsbti.api.Lazy[S]]
|
|
|
|
|
pending += z
|
|
|
|
|
z
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// force all lazy structures. This is necessary so that we see the symbols/types at this phase and
|
|
|
|
|
// so that we don't hold on to compiler objects and classes
|
|
|
|
|
private def forceStructures(): Unit =
|
|
|
|
|
if(pending.isEmpty)
|
|
|
|
|
structureCache.clear()
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
val toProcess = pending.toList
|
|
|
|
|
pending.clear()
|
|
|
|
|
toProcess foreach { _.get() }
|
|
|
|
|
forceStructures()
|
|
|
|
|
}
|
|
|
|
|
|
2010-06-16 02:38:18 +02:00
|
|
|
private def thisPath(sym: Symbol) = path(pathComponents(sym, Constants.thisPath :: Nil))
|
2009-11-16 14:46:47 +01:00
|
|
|
private def path(components: List[PathComponent]) = new xsbti.api.Path(components.toArray[PathComponent])
|
|
|
|
|
private def pathComponents(sym: Symbol, postfix: List[PathComponent]): List[PathComponent] =
|
|
|
|
|
{
|
2010-07-02 12:57:03 +02:00
|
|
|
if(sym == NoSymbol || sym.isRoot || sym.isEmptyPackageClass || sym.isRootPackage) postfix
|
2010-01-08 03:41:20 +01:00
|
|
|
else pathComponents(sym.owner, new xsbti.api.Id(simpleName(sym)) :: postfix)
|
2009-11-16 14:46:47 +01:00
|
|
|
}
|
2010-11-10 02:49:23 +01:00
|
|
|
private def simpleType(in: Symbol, t: Type): SimpleType =
|
|
|
|
|
processType(in, t) match
|
2009-11-16 14:46:47 +01:00
|
|
|
{
|
|
|
|
|
case s: SimpleType => s
|
2011-05-25 14:07:34 +02:00
|
|
|
case x => warning("Not a simple type:\n\tType: " + t + " (class " + t.getClass + ")\n\tTransformed: " + x.getClass); Constants.emptyType
|
2009-11-16 14:46:47 +01:00
|
|
|
}
|
2010-11-10 02:49:23 +01:00
|
|
|
private def types(in: Symbol, t: List[Type]): Array[xsbti.api.Type] = t.toArray[Type].map(processType(in, _))
|
|
|
|
|
private def projectionType(in: Symbol, pre: Type, sym: Symbol) =
|
2009-11-16 14:46:47 +01:00
|
|
|
{
|
2010-01-08 03:41:20 +01:00
|
|
|
if(pre == NoPrefix)
|
|
|
|
|
{
|
2010-10-23 03:55:16 +02:00
|
|
|
if(sym.isLocalClass || sym.isRoot || sym.isRootPackage) Constants.emptyType
|
|
|
|
|
else if(sym.isTypeParameterOrSkolem || isExistential(sym)) reference(sym)
|
|
|
|
|
else {
|
2010-11-10 02:49:23 +01:00
|
|
|
// this appears to come from an existential type in an inherited member- not sure why isExistential is false here
|
|
|
|
|
/*println("Warning: Unknown prefixless type: " + sym + " in " + sym.owner + " in " + sym.enclClass)
|
|
|
|
|
println("\tFlags: " + sym.flags + ", istype: " + sym.isType + ", absT: " + sym.isAbstractType + ", alias: " + sym.isAliasType + ", nonclass: " + isNonClassType(sym))*/
|
2010-10-23 03:55:16 +02:00
|
|
|
reference(sym)
|
|
|
|
|
}
|
2010-01-08 03:41:20 +01:00
|
|
|
}
|
2009-11-16 14:46:47 +01:00
|
|
|
else if(sym.isRoot || sym.isRootPackage) Constants.emptyType
|
2010-11-10 02:49:23 +01:00
|
|
|
else new xsbti.api.Projection(simpleType(in, pre), sym.nameString)
|
2009-11-16 14:46:47 +01:00
|
|
|
}
|
|
|
|
|
|
2010-10-23 03:55:16 +02:00
|
|
|
private def reference(sym: Symbol): xsbti.api.ParameterRef = new xsbti.api.ParameterRef(sym.id)
|
|
|
|
|
|
|
|
|
|
|
2010-11-10 02:49:23 +01:00
|
|
|
private def annotations(in: Symbol, as: List[AnnotationInfo]): Array[xsbti.api.Annotation] = as.toArray[AnnotationInfo].map(annotation(in,_))
|
|
|
|
|
private def annotation(in: Symbol, a: AnnotationInfo) =
|
2011-05-24 00:40:03 +02:00
|
|
|
new xsbti.api.Annotation(processType(in, a.atp),
|
2010-01-24 06:11:43 +01:00
|
|
|
if(a.assocs.isEmpty) Array(new xsbti.api.AnnotationArgument("", a.args.mkString("(", ",", ")"))) // what else to do with a Tree?
|
|
|
|
|
else a.assocs.map { case (name, value) => new xsbti.api.AnnotationArgument(name.toString, value.toString) }.toArray[xsbti.api.AnnotationArgument]
|
|
|
|
|
)
|
2010-11-10 02:49:23 +01:00
|
|
|
private def annotated(in: Symbol, as: List[AnnotationInfo], tpe: Type) = new xsbti.api.Annotated(simpleType(in, tpe), annotations(in, as))
|
2009-11-16 14:46:47 +01:00
|
|
|
|
2010-10-23 03:55:16 +02:00
|
|
|
private def viewer(s: Symbol) = (if(s.isModule) s.moduleClass else s).thisType
|
|
|
|
|
private def printMember(label: String, in: Symbol, t: Type) = println(label + " in " + in + " : " + t + " (debug: " + debugString(t) + " )")
|
|
|
|
|
private def defDef(in: Symbol, s: Symbol) =
|
2009-11-16 14:46:47 +01:00
|
|
|
{
|
|
|
|
|
def build(t: Type, typeParams: Array[xsbti.api.TypeParameter], valueParameters: List[xsbti.api.ParameterList]): xsbti.api.Def =
|
|
|
|
|
{
|
|
|
|
|
// 2.8 compatibility
|
|
|
|
|
implicit def symbolsToParameters(syms: List[Symbol]): xsbti.api.ParameterList =
|
|
|
|
|
{
|
2009-11-25 05:01:05 +01:00
|
|
|
val isImplicitList = syms match { case head :: _ => isImplicit(head); case _ => false }
|
2009-11-16 14:46:47 +01:00
|
|
|
new xsbti.api.ParameterList(syms.map(parameterS).toArray, isImplicitList)
|
|
|
|
|
}
|
|
|
|
|
// 2.7 compatibility
|
|
|
|
|
implicit def typesToParameters(syms: List[Type]): xsbti.api.ParameterList =
|
|
|
|
|
{
|
2009-11-25 05:01:05 +01:00
|
|
|
val isImplicitList = t.isInstanceOf[ImplicitMethodType]
|
2009-11-16 14:46:47 +01:00
|
|
|
new xsbti.api.ParameterList(syms.map(parameterT).toArray, isImplicitList)
|
|
|
|
|
}
|
|
|
|
|
t match
|
|
|
|
|
{
|
|
|
|
|
case PolyType(typeParams0, base) =>
|
|
|
|
|
assert(typeParams.isEmpty)
|
|
|
|
|
assert(valueParameters.isEmpty)
|
2010-11-10 02:49:23 +01:00
|
|
|
build(base, typeParameters(in, typeParams0), Nil)
|
2009-11-16 14:46:47 +01:00
|
|
|
case MethodType(params, resultType) => // in 2.7, params is of type List[Type], in 2.8 it is List[Symbol]
|
|
|
|
|
build(resultType, typeParams, (params: xsbti.api.ParameterList) :: valueParameters)
|
2011-02-09 02:30:15 +01:00
|
|
|
case Nullary(resultType) => // 2.9 and later
|
|
|
|
|
build(resultType, typeParams, valueParameters)
|
2009-11-16 14:46:47 +01:00
|
|
|
case returnType =>
|
2011-02-15 00:59:54 +01:00
|
|
|
val t2 = processType(in, dropConst(returnType))
|
|
|
|
|
new xsbti.api.Def(valueParameters.reverse.toArray, t2, typeParams, simpleName(s), getAccess(s), getModifiers(s), annotations(in,s))
|
2009-11-16 14:46:47 +01:00
|
|
|
}
|
|
|
|
|
}
|
2010-10-23 03:55:16 +02:00
|
|
|
def parameterS(s: Symbol): xsbti.api.MethodParameter =
|
|
|
|
|
makeParameter(s.nameString, s.info, s.info.typeSymbol, s)
|
|
|
|
|
|
|
|
|
|
def parameterT(t: Type): xsbti.api.MethodParameter =
|
|
|
|
|
makeParameter("", t, t.typeSymbol, NoSymbol)
|
|
|
|
|
|
2010-09-28 00:48:12 +02:00
|
|
|
// paramSym is only for 2.8 and is to determine if the parameter has a default
|
|
|
|
|
def makeParameter(name: String, tpe: Type, ts: Symbol, paramSym: Symbol): xsbti.api.MethodParameter =
|
2009-11-16 14:46:47 +01:00
|
|
|
{
|
|
|
|
|
import xsbti.api.ParameterModifier._
|
|
|
|
|
val (t, special) =
|
|
|
|
|
if(ts == definitions.RepeatedParamClass)// || s == definitions.JavaRepeatedParamClass)
|
|
|
|
|
(tpe.typeArgs(0), Repeated)
|
|
|
|
|
else if(ts == definitions.ByNameParamClass)
|
|
|
|
|
(tpe.typeArgs(0), ByName)
|
|
|
|
|
else
|
|
|
|
|
(tpe, Plain)
|
2010-11-10 02:49:23 +01:00
|
|
|
new xsbti.api.MethodParameter(name, processType(in, t), hasDefault(paramSym), special)
|
2009-11-16 14:46:47 +01:00
|
|
|
}
|
2010-10-23 03:55:16 +02:00
|
|
|
val t = viewer(in).memberInfo(s)
|
|
|
|
|
build(t, Array(), Nil)
|
2009-11-16 14:46:47 +01:00
|
|
|
}
|
|
|
|
|
private def hasDefault(s: Symbol) =
|
|
|
|
|
{
|
|
|
|
|
// 2.7 compatibility
|
|
|
|
|
implicit def flagsWithDefault(f: AnyRef): WithDefault = new WithDefault
|
2010-09-28 00:48:12 +02:00
|
|
|
class WithDefault { val DEFAULTPARAM = 0x00000000 }
|
|
|
|
|
s != NoSymbol && s.hasFlag(Flags.DEFAULTPARAM)
|
2009-11-16 14:46:47 +01:00
|
|
|
}
|
2011-02-15 00:59:54 +01:00
|
|
|
private def fieldDef[T](in: Symbol, s: Symbol, keepConst: Boolean, create: (xsbti.api.Type, String, xsbti.api.Access, xsbti.api.Modifiers, Array[xsbti.api.Annotation]) => T): T =
|
2010-10-23 03:55:16 +02:00
|
|
|
{
|
2011-02-09 02:30:15 +01:00
|
|
|
val t = dropNullary(viewer(in).memberType(s))
|
2011-02-15 00:59:54 +01:00
|
|
|
val t2 = if(keepConst) t else dropConst(t)
|
|
|
|
|
create(processType(in, t2), simpleName(s), getAccess(s), getModifiers(s), annotations(in, s))
|
|
|
|
|
}
|
|
|
|
|
private def dropConst(t: Type): Type = t match {
|
|
|
|
|
case ConstantType(constant) => constant.tpe
|
|
|
|
|
case _ => t
|
2010-10-23 03:55:16 +02:00
|
|
|
}
|
2011-02-09 02:30:15 +01:00
|
|
|
private def dropNullary(t: Type): Type = t match {
|
|
|
|
|
case Nullary(un) => un
|
|
|
|
|
case _ => t
|
|
|
|
|
}
|
2010-01-24 06:11:43 +01:00
|
|
|
|
2010-10-23 03:55:16 +02:00
|
|
|
private def typeDef(in: Symbol, s: Symbol): xsbti.api.TypeMember =
|
2009-11-23 04:54:17 +01:00
|
|
|
{
|
|
|
|
|
val (typeParams, tpe) =
|
2010-10-23 03:55:16 +02:00
|
|
|
viewer(in).memberInfo(s) match
|
2009-11-23 04:54:17 +01:00
|
|
|
{
|
2010-11-10 02:49:23 +01:00
|
|
|
case PolyType(typeParams0, base) => (typeParameters(in, typeParams0), base)
|
2009-11-23 04:54:17 +01:00
|
|
|
case t => (Array[xsbti.api.TypeParameter](), t)
|
|
|
|
|
}
|
2010-01-08 03:41:20 +01:00
|
|
|
val name = simpleName(s)
|
2009-11-23 04:54:17 +01:00
|
|
|
val access = getAccess(s)
|
|
|
|
|
val modifiers = getModifiers(s)
|
2010-11-10 02:49:23 +01:00
|
|
|
val as = annotations(in, s)
|
2009-11-23 04:54:17 +01:00
|
|
|
|
|
|
|
|
if(s.isAliasType)
|
2010-11-10 02:49:23 +01:00
|
|
|
new xsbti.api.TypeAlias(processType(in, tpe), typeParams, name, access, modifiers, as)
|
2009-11-23 04:54:17 +01:00
|
|
|
else if(s.isAbstractType)
|
|
|
|
|
{
|
|
|
|
|
val bounds = tpe.bounds
|
2010-11-10 02:49:23 +01:00
|
|
|
new xsbti.api.TypeDeclaration(processType(in, bounds.lo), processType(in, bounds.hi), typeParams, name, access, modifiers, as)
|
2009-11-23 04:54:17 +01:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
error("Unknown type member" + s)
|
|
|
|
|
}
|
2009-11-16 14:46:47 +01:00
|
|
|
|
2010-11-10 02:49:23 +01:00
|
|
|
private def structure(in: Symbol, s: Symbol): xsbti.api.Structure = structure(viewer(in).memberInfo(s), s, true)
|
2010-10-23 03:55:16 +02:00
|
|
|
private def structure(info: Type): xsbti.api.Structure = structure(info, info.typeSymbol, false)
|
|
|
|
|
private def structure(info: Type, s: Symbol, inherit: Boolean): xsbti.api.Structure =
|
|
|
|
|
structureCache.getOrElseUpdate( s, mkStructure(info, s, inherit))
|
|
|
|
|
|
2010-11-10 02:49:23 +01:00
|
|
|
private def removeConstructors(ds: List[Symbol]): List[Symbol] = ds filter { !_.isConstructor}
|
|
|
|
|
|
2010-10-23 03:55:16 +02:00
|
|
|
private def mkStructure(info: Type, s: Symbol, inherit: Boolean): xsbti.api.Structure =
|
2009-11-23 04:54:17 +01:00
|
|
|
{
|
2010-09-18 03:30:47 +02:00
|
|
|
val (declared, inherited) = info.members.reverse.partition(_.owner == s)
|
2010-10-23 03:55:16 +02:00
|
|
|
val baseTypes = info.baseClasses.tail.map(info.baseType)
|
2010-11-10 02:49:23 +01:00
|
|
|
val ds = if(s.isModuleClass) removeConstructors(declared) else declared
|
|
|
|
|
val is = if(inherit) removeConstructors(inherited) else Nil
|
|
|
|
|
mkStructure(s, baseTypes, ds, is)
|
2009-11-23 04:54:17 +01:00
|
|
|
}
|
2010-10-23 03:55:16 +02:00
|
|
|
|
|
|
|
|
private def mkStructure(s: Symbol, bases: List[Type], declared: List[Symbol], inherited: List[Symbol]): xsbti.api.Structure =
|
2010-11-10 02:49:23 +01:00
|
|
|
new xsbti.api.Structure(lzy(types(s, bases)), lzy(processDefinitions(s, declared)), lzy(processDefinitions(s, inherited)))
|
2010-10-23 03:55:16 +02:00
|
|
|
private def processDefinitions(in: Symbol, defs: List[Symbol]): Array[xsbti.api.Definition] =
|
|
|
|
|
defs.toArray.flatMap( (d: Symbol) => definition(in, d))
|
|
|
|
|
private def definition(in: Symbol, sym: Symbol): Option[xsbti.api.Definition] =
|
2009-11-16 14:46:47 +01:00
|
|
|
{
|
2011-02-15 00:59:54 +01:00
|
|
|
def mkVar = Some(fieldDef(in, sym, false, new xsbti.api.Var(_,_,_,_,_)))
|
|
|
|
|
def mkVal = Some(fieldDef(in, sym, true, new xsbti.api.Val(_,_,_,_,_)))
|
2010-11-10 02:49:23 +01:00
|
|
|
if(sym.isClass || sym.isModule)
|
|
|
|
|
if(ignoreClass(sym)) None else Some(classLike(in, sym))
|
2010-09-18 04:14:48 +02:00
|
|
|
else if(isNonClassType(sym))
|
2010-10-23 03:55:16 +02:00
|
|
|
Some(typeDef(in, sym))
|
2010-09-18 04:14:48 +02:00
|
|
|
else if(sym.isVariable)
|
2010-10-23 03:55:16 +02:00
|
|
|
if(isSourceField(sym)) mkVar else None
|
2010-09-18 04:14:48 +02:00
|
|
|
else if(sym.isStable)
|
2010-10-23 03:55:16 +02:00
|
|
|
if(isSourceField(sym)) mkVal else None
|
2010-09-18 04:14:48 +02:00
|
|
|
else if(sym.isSourceMethod && !sym.isSetter)
|
2010-10-23 03:55:16 +02:00
|
|
|
if(sym.isGetter) mkVar else Some(defDef(in, sym))
|
|
|
|
|
else
|
|
|
|
|
None
|
2010-09-18 04:14:48 +02:00
|
|
|
}
|
2010-11-10 02:49:23 +01:00
|
|
|
private def ignoreClass(sym: Symbol): Boolean =
|
2011-02-09 02:30:15 +01:00
|
|
|
sym.isLocalClass || sym.isAnonymousClass || fullName(sym).endsWith(LocalChild)
|
2010-11-10 02:49:23 +01:00
|
|
|
|
2010-09-18 04:14:48 +02:00
|
|
|
// This filters private[this] vals/vars that were not in the original source.
|
|
|
|
|
// The getter will be used for processing instead.
|
|
|
|
|
private def isSourceField(sym: Symbol): Boolean =
|
|
|
|
|
{
|
|
|
|
|
val getter = sym.getter(sym.enclClass)
|
|
|
|
|
// the check `getter eq sym` is a precaution against infinite recursion
|
|
|
|
|
// `isParamAccessor` does not exist in all supported versions of Scala, so the flag check is done directly
|
|
|
|
|
(getter == NoSymbol && !sym.hasFlag(Flags.PARAMACCESSOR)) || (getter eq sym)
|
2009-11-16 14:46:47 +01:00
|
|
|
}
|
|
|
|
|
private def getModifiers(s: Symbol): xsbti.api.Modifiers =
|
|
|
|
|
{
|
|
|
|
|
import Flags._
|
2010-10-23 03:55:16 +02:00
|
|
|
new xsbti.api.Modifiers(s.hasFlag(ABSTRACT) || s.hasFlag(DEFERRED), s.hasFlag(OVERRIDE),
|
|
|
|
|
s.isFinal, s.hasFlag(SEALED), isImplicit(s), s.hasFlag(LAZY))
|
2009-11-16 14:46:47 +01:00
|
|
|
}
|
2010-10-30 23:46:56 +02:00
|
|
|
|
2009-11-16 14:46:47 +01:00
|
|
|
private def isImplicit(s: Symbol) = s.hasFlag(Flags.IMPLICIT)
|
|
|
|
|
private def getAccess(c: Symbol): xsbti.api.Access =
|
|
|
|
|
{
|
|
|
|
|
if(c.isPublic) Constants.public
|
|
|
|
|
else if(c.isPrivateLocal) Constants.privateLocal
|
|
|
|
|
else if(c.isProtectedLocal) Constants.protectedLocal
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
val within = c.privateWithin
|
2010-01-08 03:41:20 +01:00
|
|
|
val qualifier = if(within == NoSymbol) Constants.unqualified else new xsbti.api.IdQualifier(fullName(within))
|
2010-09-18 03:29:29 +02:00
|
|
|
if(c.hasFlag(Flags.PROTECTED)) new xsbti.api.Protected(qualifier)
|
|
|
|
|
else new xsbti.api.Private(qualifier)
|
2009-11-16 14:46:47 +01:00
|
|
|
}
|
|
|
|
|
}
|
2010-11-10 02:49:23 +01:00
|
|
|
private def processType(in: Symbol, t: Type): xsbti.api.Type = typeCache.getOrElseUpdate((in, t), makeType(in, t))
|
|
|
|
|
private def makeType(in: Symbol, t: Type): xsbti.api.Type =
|
2009-11-16 14:46:47 +01:00
|
|
|
{
|
2010-10-23 03:55:16 +02:00
|
|
|
def dealias(t: Type) = t match { case TypeRef(_, sym, _) if sym.isAliasType => t.normalize; case _ => t }
|
|
|
|
|
|
|
|
|
|
dealias(t) match
|
2009-11-16 14:46:47 +01:00
|
|
|
{
|
|
|
|
|
case NoPrefix => Constants.emptyType
|
|
|
|
|
case ThisType(sym) => new xsbti.api.Singleton(thisPath(sym))
|
2010-11-10 02:49:23 +01:00
|
|
|
case SingleType(pre, sym) => projectionType(in, pre, sym)
|
2011-02-15 00:59:54 +01:00
|
|
|
case ConstantType(constant) => new xsbti.api.Constant(processType(in, constant.tpe), constant.stringValue)
|
2009-11-16 14:46:47 +01:00
|
|
|
case TypeRef(pre, sym, args) =>
|
2010-11-10 02:49:23 +01:00
|
|
|
val base = projectionType(in, pre, sym)
|
|
|
|
|
if(args.isEmpty) base else new xsbti.api.Parameterized(base, types(in, args))
|
2011-05-25 14:07:34 +02:00
|
|
|
case SuperType(thistpe: Type, supertpe: Type) => warning("sbt-api: Super type (not implemented): this=" + thistpe + ", super=" + supertpe); Constants.emptyType
|
2010-11-10 02:49:23 +01:00
|
|
|
case at: AnnotatedType => annotatedType(in, at)
|
2010-10-23 03:55:16 +02:00
|
|
|
case rt: CompoundType => structure(rt)
|
2010-11-10 02:49:23 +01:00
|
|
|
case ExistentialType(tparams, result) => new xsbti.api.Existential(processType(in, result), typeParameters(in, tparams))
|
2011-05-25 13:57:14 +02:00
|
|
|
case NoType => Constants.emptyType // this can happen when there is an error that will be reported by a later phase
|
2010-11-10 02:49:23 +01:00
|
|
|
case PolyType(typeParams, resultType) => new xsbti.api.Polymorphic(processType(in, resultType), typeParameters(in, typeParams))
|
2011-05-25 14:07:34 +02:00
|
|
|
case Nullary(resultType) => warning("sbt-api: Unexpected nullary method type " + in + " in " + in.owner); Constants.emptyType
|
|
|
|
|
case _ => warning("sbt-api: Unhandled type " + t.getClass + " : " + t); Constants.emptyType
|
2009-11-16 14:46:47 +01:00
|
|
|
}
|
|
|
|
|
}
|
2010-11-10 02:49:23 +01:00
|
|
|
private def typeParameters(in: Symbol, s: Symbol): Array[xsbti.api.TypeParameter] = typeParameters(in, s.typeParams)
|
|
|
|
|
private def typeParameters(in: Symbol, s: List[Symbol]): Array[xsbti.api.TypeParameter] = s.map(typeParameter(in,_)).toArray[xsbti.api.TypeParameter]
|
|
|
|
|
private def typeParameter(in: Symbol, s: Symbol): xsbti.api.TypeParameter =
|
2009-11-16 14:46:47 +01:00
|
|
|
{
|
|
|
|
|
val varianceInt = s.variance
|
|
|
|
|
import xsbti.api.Variance._
|
2010-11-10 02:49:23 +01:00
|
|
|
val annots = annotations(in, s)
|
2009-11-16 14:46:47 +01:00
|
|
|
val variance = if(varianceInt < 0) Contravariant else if(varianceInt > 0) Covariant else Invariant
|
2010-11-10 02:49:23 +01:00
|
|
|
viewer(in).memberInfo(s) match
|
2009-11-16 14:46:47 +01:00
|
|
|
{
|
2010-11-10 02:49:23 +01:00
|
|
|
case TypeBounds(low, high) => new xsbti.api.TypeParameter( s.id, annots, typeParameters(in, s), variance, processType(in, low), processType(in, high) )
|
|
|
|
|
case PolyType(typeParams, base) => new xsbti.api.TypeParameter( s.id, annots, typeParameters(in, typeParams), variance, processType(in, base.bounds.lo), processType(in, base.bounds.hi))
|
2009-11-16 14:46:47 +01:00
|
|
|
case x => error("Unknown type parameter info: " + x.getClass)
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-03-11 22:57:15 +01:00
|
|
|
private def selfType(in: Symbol, s: Symbol): xsbti.api.Type = processType(in, s.thisSym.typeOfThis)
|
2010-10-23 03:55:16 +02:00
|
|
|
|
2010-11-10 02:49:23 +01:00
|
|
|
private def classLike(in: Symbol, c: Symbol): ClassLike = classLikeCache.getOrElseUpdate( (in,c), mkClassLike(in, c))
|
2010-10-23 03:55:16 +02:00
|
|
|
private def mkClassLike(in: Symbol, c: Symbol): ClassLike =
|
2009-11-16 14:46:47 +01:00
|
|
|
{
|
2010-01-08 03:41:20 +01:00
|
|
|
val name = fullName(c)
|
2009-11-16 14:46:47 +01:00
|
|
|
val isModule = c.isModuleClass || c.isModule
|
2010-11-10 02:49:23 +01:00
|
|
|
val struct = if(isModule) c.moduleClass else c
|
2009-11-16 14:46:47 +01:00
|
|
|
val defType =
|
|
|
|
|
if(c.isTrait) DefinitionType.Trait
|
|
|
|
|
else if(isModule)
|
|
|
|
|
{
|
|
|
|
|
if(c.isPackage) DefinitionType.PackageModule
|
|
|
|
|
else DefinitionType.Module
|
|
|
|
|
}
|
|
|
|
|
else DefinitionType.ClassDef
|
2010-11-10 02:49:23 +01:00
|
|
|
new xsbti.api.ClassLike(defType, lzy(selfType(in, c)), lzy(structure(in, struct)), typeParameters(in, c), name, getAccess(c), getModifiers(c), annotations(in, c))
|
2009-11-16 14:46:47 +01:00
|
|
|
}
|
|
|
|
|
private final class TopLevelHandler(sourceFile: File) extends TopLevelTraverser
|
|
|
|
|
{
|
|
|
|
|
val packages = new HashSet[String]
|
|
|
|
|
val definitions = new ListBuffer[xsbti.api.Definition]
|
2010-10-23 03:55:16 +02:00
|
|
|
def `class`(c: Symbol): Unit = definitions += classLike(c.owner, c)
|
2009-11-16 14:46:47 +01:00
|
|
|
/** Record packages declared in the source file*/
|
|
|
|
|
def `package`(p: Symbol)
|
|
|
|
|
{
|
|
|
|
|
if( (p eq null) || p == NoSymbol || p.isRoot || p.isRootPackage || p.isEmptyPackageClass || p.isEmptyPackage)
|
|
|
|
|
()
|
|
|
|
|
else
|
|
|
|
|
{
|
2010-01-08 03:41:20 +01:00
|
|
|
packages += fullName(p)
|
2009-11-16 14:46:47 +01:00
|
|
|
`package`(p.enclosingPackage)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
private object Constants
|
|
|
|
|
{
|
2010-01-01 00:55:35 +01:00
|
|
|
val local = new xsbti.api.ThisQualifier
|
2009-11-16 14:46:47 +01:00
|
|
|
val public = new xsbti.api.Public
|
|
|
|
|
val privateLocal = new xsbti.api.Private(local)
|
|
|
|
|
val protectedLocal = new xsbti.api.Protected(local)
|
|
|
|
|
val unqualified = new xsbti.api.Unqualified
|
|
|
|
|
val emptyPath = new xsbti.api.Path(Array())
|
|
|
|
|
val thisPath = new xsbti.api.This
|
|
|
|
|
val emptyType = new xsbti.api.EmptyType
|
|
|
|
|
}
|
|
|
|
|
private abstract class TopLevelTraverser extends Traverser
|
|
|
|
|
{
|
|
|
|
|
def `class`(s: Symbol)
|
|
|
|
|
def `package`(s: Symbol)
|
|
|
|
|
override def traverse(tree: Tree)
|
|
|
|
|
{
|
|
|
|
|
tree match
|
|
|
|
|
{
|
|
|
|
|
case (_: ClassDef | _ : ModuleDef) if isTopLevel(tree.symbol) => `class`(tree.symbol)
|
|
|
|
|
case p: PackageDef =>
|
|
|
|
|
`package`(p.symbol)
|
|
|
|
|
super.traverse(tree)
|
|
|
|
|
case _ =>
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
def isTopLevel(sym: Symbol): Boolean =
|
|
|
|
|
(sym ne null) && (sym != NoSymbol) && !sym.isImplClass && !sym.isNestedClass && sym.isStatic &&
|
2010-07-15 01:24:50 +02:00
|
|
|
!sym.hasFlag(Flags.SYNTHETIC) && !sym.hasFlag(Flags.JAVA)
|
2009-11-16 14:46:47 +01:00
|
|
|
}
|
2009-11-25 05:01:05 +01:00
|
|
|
|
|
|
|
|
// In 2.8, attributes is renamed to annotations
|
|
|
|
|
implicit def compat(a: AnyRef): WithAnnotations = new WithAnnotations(a)
|
|
|
|
|
class WithAnnotations(a: AnyRef) { def attributes = a.getClass.getMethod("annotations").invoke(a).asInstanceOf[List[AnnotationInfo]] }
|
|
|
|
|
|
2010-11-10 02:49:23 +01:00
|
|
|
private def annotations(in: Symbol, s: Symbol): Array[xsbti.api.Annotation] =
|
2010-01-24 06:11:43 +01:00
|
|
|
atPhase(currentRun.typerPhase) {
|
2010-09-18 04:14:48 +02:00
|
|
|
val base = if(s.hasFlag(Flags.ACCESSOR)) s.accessed else NoSymbol
|
|
|
|
|
val b = if(base == NoSymbol) s else base
|
|
|
|
|
// annotations from bean methods are not handled because:
|
|
|
|
|
// a) they are recorded as normal source methods anyway
|
|
|
|
|
// b) there is no way to distinguish them from user-defined methods
|
|
|
|
|
val associated = List(b, b.getter(b.enclClass), b.setter(b.enclClass)).filter(_ != NoSymbol)
|
2010-11-10 02:49:23 +01:00
|
|
|
associated.flatMap( ss => annotations(in, ss.attributes) ).removeDuplicates.toArray ;
|
2010-01-24 06:11:43 +01:00
|
|
|
}
|
2010-11-10 02:49:23 +01:00
|
|
|
private def annotatedType(in: Symbol, at: AnnotatedType): xsbti.api.Type =
|
2010-01-08 03:41:20 +01:00
|
|
|
{
|
|
|
|
|
val annots = at.attributes
|
2010-11-10 02:49:23 +01:00
|
|
|
if(annots.isEmpty) processType(in, at.underlying) else annotated(in, annots, at.underlying)
|
2010-01-08 03:41:20 +01:00
|
|
|
}
|
2010-06-16 02:38:18 +02:00
|
|
|
private def fullName(s: Symbol): String = nameString(s)
|
2011-03-11 22:57:15 +01:00
|
|
|
private def simpleName(s: Symbol): String =
|
|
|
|
|
{
|
|
|
|
|
val n = s.originalName
|
|
|
|
|
val n2 = if(n.toString == "<init>") n else n.decode
|
|
|
|
|
n2.toString.trim
|
|
|
|
|
}
|
2010-10-23 03:55:16 +02:00
|
|
|
}
|