Refactor mkStructure

Specialize two implementations for each value of the `inherit` boolean argument.
Also use a more direct way of distinguishing declared and inherited members.

backwards compat for source-dependencies/inherited-dependencies
This commit is contained in:
Adriaan Moors 2015-12-02 23:43:57 -08:00 committed by Eugene Yokota
parent 9346e2bb4d
commit 8c73b2f221
1 changed files with 43 additions and 19 deletions

View File

@ -329,27 +329,51 @@ class ExtractAPI[GlobalType <: CallbackGlobal](val global: GlobalType,
error("Unknown type member" + s)
}
private def structure(in: Symbol, s: Symbol): xsbti.api.Structure = structure(viewer(in).memberInfo(s), s, true)
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))
private def structure(info: Type, s: Symbol): xsbti.api.Structure = structureCache.getOrElseUpdate(s, mkStructure(info, s))
private def structureWithInherited(info: Type, s: Symbol): xsbti.api.Structure = structureCache.getOrElseUpdate(s, mkStructureWithInherited(info, s))
private def removeConstructors(ds: List[Symbol]): List[Symbol] = ds filter { !_.isConstructor }
private def mkStructure(info: Type, s: Symbol, inherit: Boolean): xsbti.api.Structure = {
val (declared, inherited) = info.members.reverse.partition(_.owner == s)
// Note that the ordering of classes in `baseClasses` is important.
// It would be easier to just say `val baseTypes = baseTypeSeq`, but that does not seem
// to take linearization into account.
// Also, we take info.parents when we're not interested in the full linearization,
// which side steps issues with baseType when f-bounded existential types and refined types mix
// (and we get cyclic types which cause a stack overflow in showAPI)
val baseTypes = if (inherit) info.baseClasses.tail.map(info.baseType) else info.parents
val ds = if (s.isModuleClass) removeConstructors(declared) else declared
val is = if (inherit) removeConstructors(inherited) else Nil
mkStructure(s, baseTypes, ds, is)
/**
* Create structure as-is, without embedding ancestors
*
* (for refinement types, and ClassInfoTypes encountered outside of a definition???).
*/
private def mkStructure(info: Type, s: Symbol): xsbti.api.Structure = {
// We're not interested in the full linearization, so we can just use `parents`,
// which side steps issues with baseType when f-bounded existential types and refined types mix
// (and we get cyclic types which cause a stack overflow in showAPI).
//
// The old algorithm's semantics for inherited dependencies include all types occurring as a parent anywhere in a type,
// so that, in `class C { def foo: A }; class A extends B`, C is considered to have an "inherited dependency" on `A` and `B`!!!
val parentTypes = if (global.callback.nameHashing()) info.parents else linearizedAncestorTypes(info)
val decls = info.decls.toList
val declsNoModuleCtor = if (s.isModuleClass) removeConstructors(decls) else decls
mkStructure(s, parentTypes, declsNoModuleCtor, Nil)
}
/**
* Track all ancestors and inherited members for a class's API.
*
* A class's hash does not include hashes for its parent classes -- only the symbolic names --
* so we must ensure changes propagate somehow.
*
* TODO: can we include hashes for parent classes instead? This seems a bit messy.
*/
private def mkStructureWithInherited(info: Type, s: Symbol): xsbti.api.Structure = {
val ancestorTypes = linearizedAncestorTypes(info)
val decls = info.decls.toList
val declsNoModuleCtor = if (s.isModuleClass) removeConstructors(decls) else decls
val declSet = decls.toSet
val inherited = info.nonPrivateMembers.toList.filterNot(declSet) // private members are not inherited
mkStructure(s, ancestorTypes, declsNoModuleCtor, inherited)
}
// Note that the ordering of classes in `baseClasses` is important.
// It would be easier to just say `baseTypeSeq.toList.tail`,
// but that does not take linearization into account.
def linearizedAncestorTypes(info: Type): List[Type] = info.baseClasses.tail.map(info.baseType)
// If true, this template is publicly visible and should be processed as a public inheritance dependency.
// Local classes and local refinements will never be traversed by the api phase, so we don't need to check for that.
private[this] def isPublicStructure(s: Symbol): Boolean =
@ -473,7 +497,7 @@ class ExtractAPI[GlobalType <: CallbackGlobal](val global: GlobalType,
if (unrolling ne withoutRecursiveRefs)
reporter.warning(sym.pos, "sbt-api: approximated refinement ref" + t + " (== " + unrolling + ") to " + withoutRecursiveRefs + "\nThis is currently untested, please report the code you were compiling.")
structure(withoutRecursiveRefs)
structure(withoutRecursiveRefs, sym)
case tr @ TypeRef(pre, sym, args) =>
val base = projectionType(in, pre, sym)
if (args.isEmpty)
@ -486,7 +510,7 @@ class ExtractAPI[GlobalType <: CallbackGlobal](val global: GlobalType,
case SuperType(thistpe: Type, supertpe: Type) =>
warning("sbt-api: Super type (not implemented): this=" + thistpe + ", super=" + supertpe); Constants.emptyType
case at: AnnotatedType => annotatedType(in, at)
case rt: CompoundType => structure(rt)
case rt: CompoundType => structure(rt, rt.typeSymbol)
case t: ExistentialType => makeExistentialType(in, t)
case NoType => Constants.emptyType // this can happen when there is an error that will be reported by a later phase
case PolyType(typeParams, resultType) => new xsbti.api.Polymorphic(processType(in, resultType), typeParameters(in, typeParams))
@ -556,7 +580,7 @@ class ExtractAPI[GlobalType <: CallbackGlobal](val global: GlobalType,
} else DefinitionType.ClassDef
new xsbti.api.ClassLike(
defType, lzy(selfType(in, sym)), lzy(structure(in, sym)), emptyStringArray, typeParameters(in, sym), // look at class symbol
defType, lzy(selfType(in, sym)), lzy(structureWithInherited(viewer(in).memberInfo(sym), sym)), emptyStringArray, typeParameters(in, sym), // look at class symbol
c.fullName, getAccess(c), getModifiers(c), annotations(in, c)) // use original symbol (which is a term symbol when `c.isModule`) for `name` and other non-classy stuff
}