preserve API information needed for detecting annotations on defs. fixes #232

This commit is contained in:
Mark Harrah 2011-10-19 22:23:47 -04:00
parent bec7d3fb28
commit 82ad44a701
9 changed files with 30 additions and 19 deletions

View File

@ -55,8 +55,9 @@ object APIUtil
} }
def minimizeClass(c: ClassLike): ClassLike = def minimizeClass(c: ClassLike): ClassLike =
{ {
val savedAnnotations = Discovery.defAnnotations(c.structure, (_: Any) => true).toArray[String]
val struct = minimizeStructure(c.structure, c.definitionType == DefinitionType.Module) val struct = minimizeStructure(c.structure, c.definitionType == DefinitionType.Module)
new ClassLike(c.definitionType, lzy(emptyType), lzy(struct), c.typeParameters, c.name, c.access, c.modifiers, c.annotations) new ClassLike(c.definitionType, lzy(emptyType), lzy(struct), savedAnnotations, c.typeParameters, c.name, c.access, c.modifiers, c.annotations)
} }
def minimizeStructure(s: Structure, isModule: Boolean): Structure = def minimizeStructure(s: Structure, isModule: Boolean): Structure =

View File

@ -47,8 +47,8 @@ object ClassToAPI
val name = c.getName val name = c.getName
val tpe = if(Modifier.isInterface(c.getModifiers)) Trait else ClassDef val tpe = if(Modifier.isInterface(c.getModifiers)) Trait else ClassDef
lazy val (static, instance) = structure(c, enclPkg, cmap) lazy val (static, instance) = structure(c, enclPkg, cmap)
val cls = new api.ClassLike(tpe, strict(Empty), lzy(instance), typeParameters(c.getTypeParameters), name, acc, mods, annots) val cls = new api.ClassLike(tpe, strict(Empty), lzy(instance), emptyStringArray, typeParameters(c.getTypeParameters), name, acc, mods, annots)
val stat = new api.ClassLike(Module, strict(Empty), lzy(static), emptyTypeParameterArray, name, acc, mods, annots) val stat = new api.ClassLike(Module, strict(Empty), lzy(static), emptyStringArray, emptyTypeParameterArray, name, acc, mods, annots)
val defs = cls :: stat :: Nil val defs = cls :: stat :: Nil
cmap(c.getName) = defs cmap(c.getName) = defs
defs defs
@ -68,6 +68,7 @@ object ClassToAPI
} }
def lzy[T <: AnyRef](t: => T): xsbti.api.Lazy[T] = xsbti.SafeLazy(t) def lzy[T <: AnyRef](t: => T): xsbti.api.Lazy[T] = xsbti.SafeLazy(t)
private val emptyStringArray = new Array[String](0)
private val emptyTypeArray = new Array[xsbti.api.Type](0) private val emptyTypeArray = new Array[xsbti.api.Type](0)
private val emptyAnnotationArray = new Array[xsbti.api.Annotation](0) private val emptyAnnotationArray = new Array[xsbti.api.Annotation](0)
private val emptyTypeParameterArray = new Array[xsbti.api.TypeParameter](0) private val emptyTypeParameterArray = new Array[xsbti.api.TypeParameter](0)

View File

@ -19,17 +19,14 @@ class Discovery(baseClasses: Set[String], annotations: Set[String])
} }
def discover(c: ClassLike): Discovered = def discover(c: ClassLike): Discovered =
{ {
val onClass = findAnnotations(c.annotations) val onClass = Discovery.findAnnotations(c.annotations, annotations)
val onDefs = defAnnotations(c.structure.declared) ++ defAnnotations(c.structure.inherited) val onDefs = Discovery.defAnnotations(c.structure, annotations) ++ c.savedAnnotations.filter(annotations)
val module = isModule(c) val module = isModule(c)
new Discovered( bases(c.name, c.structure.parents), onClass ++ onDefs, module && hasMainMethod(c), module ) new Discovered( bases(c.name, c.structure.parents), onClass ++ onDefs, module && hasMainMethod(c), module )
} }
def bases(own: String, c: Seq[Type]): Set[String] = def bases(own: String, c: Seq[Type]): Set[String] =
(own +: c.flatMap(simpleName)).filter(baseClasses).toSet (own +: c.flatMap(simpleName)).filter(baseClasses).toSet
def findAnnotations(as: Seq[Annotation]): Set[String] =
as.flatMap { a => simpleName(a.base).filter(annotations) }.toSet
def defAnnotations(defs: Seq[Definition]): Set[String] =
findAnnotations( defs.flatMap { case d: Def if isPublic(d) => d.annotations.toSeq; case _ => Nil } )
} }
object Discovery object Discovery
{ {
@ -41,6 +38,13 @@ object Discovery
def applications(definitions: Seq[Definition]): Seq[(Definition, Discovered)] = def applications(definitions: Seq[Definition]): Seq[(Definition, Discovered)] =
apply(Set.empty, Set.empty)( definitions ) apply(Set.empty, Set.empty)( definitions )
def findAnnotations(as: Seq[Annotation], pred: String => Boolean): Set[String] =
as.flatMap { a => simpleName(a.base).filter(pred) }.toSet
def defAnnotations(s: Structure, pred: String => Boolean): Set[String] =
defAnnotations(s.declared, pred) ++ defAnnotations(s.inherited, pred)
def defAnnotations(defs: Seq[Definition], pred: String => Boolean): Set[String] =
findAnnotations( defs.flatMap { case d: Def if isPublic(d) => d.annotations.toSeq; case _ => Nil }, pred )
def isConcrete(a: Definition): Boolean = isConcrete(a.modifiers) def isConcrete(a: Definition): Boolean = isConcrete(a.modifiers)
def isConcrete(m: Modifiers) = !m.isAbstract def isConcrete(m: Modifiers) = !m.isAbstract
def isPublic(a: Definition): Boolean = isPublic(a.access) def isPublic(a: Definition): Boolean = isPublic(a.access)

View File

@ -58,6 +58,8 @@ final class API(val global: Global, val callback: xsbti.AnalysisCallback) extend
private[this] val classLikeCache = new HashMap[(Symbol,Symbol), xsbti.api.ClassLike] private[this] val classLikeCache = new HashMap[(Symbol,Symbol), xsbti.api.ClassLike]
private[this] val pending = new HashSet[xsbti.api.Lazy[_]] private[this] val pending = new HashSet[xsbti.api.Lazy[_]]
private[this] val emptyStringArray = new Array[String](0)
// to mitigate "temporary leaks" like that caused by NoPhase in 2.8.0, // to mitigate "temporary leaks" like that caused by NoPhase in 2.8.0,
// this ensures this class is not retaining objects // this ensures this class is not retaining objects
private def clearCaches() private def clearCaches()
@ -345,7 +347,7 @@ final class API(val global: Global, val callback: xsbti.AnalysisCallback) extend
else DefinitionType.Module else DefinitionType.Module
} }
else DefinitionType.ClassDef else DefinitionType.ClassDef
new xsbti.api.ClassLike(defType, lzy(selfType(in, c)), lzy(structure(in, struct)), typeParameters(in, c), name, getAccess(c), getModifiers(c), annotations(in, c)) new xsbti.api.ClassLike(defType, lzy(selfType(in, c)), lzy(structure(in, struct)), emptyStringArray, typeParameters(in, c), name, getAccess(c), getModifiers(c), annotations(in, c))
} }
private final class TopLevelHandler(sourceFile: File) extends TopLevelTraverser private final class TopLevelHandler(sourceFile: File) extends TopLevelTraverser
{ {

View File

@ -127,7 +127,7 @@ object ApplicationsTest extends Specification
} }
} }
def applications(callback: xsbti.TestCallback): Seq[(File, String)] = def applications(callback: xsbti.TestCallback): Seq[(File, String)] =
for( (file, api) <- callback.apis.toSeq; for( (file, api) <- CallbackTest.apis(callback);
x = println("\n" + file + ":\n" + (api.definitions.flatMap { case c: xsbti.api.ClassLike => c.structure.inherited.filter(_.name == "main"); case _ => Nil }).map(xsbt.api.DefaultShowAPI.apply).mkString("\n")); x = println("\n" + file + ":\n" + (api.definitions.flatMap { case c: xsbti.api.ClassLike => c.structure.inherited.filter(_.name == "main"); case _ => Nil }).map(xsbt.api.DefaultShowAPI.apply).mkString("\n"));
application <- applications(api)) application <- applications(api))
yield (file, application) yield (file, application)

View File

@ -54,7 +54,7 @@ object DetectAnnotations extends Specification
} }
} }
def subclasses(callback: xsbti.TestCallback): Seq[(File, String, Set[String], Boolean)] = def subclasses(callback: xsbti.TestCallback): Seq[(File, String, Set[String], Boolean)] =
for( (file, src) <- callback.apis.toSeq; (definition, discovered) <- Discovery(Set.empty, annotationNames)(src.definitions) if !discovered.isEmpty ) yield for( (file, src) <- CallbackTest.apis(callback); (definition, discovered) <- Discovery(Set.empty, annotationNames)(src.definitions) if !discovered.isEmpty ) yield
(file, definition.name, discovered.annotations, discovered.isModule) (file, definition.name, discovered.annotations, discovered.isModule)
def annotationNames = Set("c.A", "B", "d.C") def annotationNames = Set("c.A", "B", "d.C")

View File

@ -37,7 +37,7 @@ object DetectSubclasses extends Specification
} }
} }
def subclasses(callback: xsbti.TestCallback): Seq[(File, String, Set[String], Boolean)] = def subclasses(callback: xsbti.TestCallback): Seq[(File, String, Set[String], Boolean)] =
for( (file, src) <- callback.apis.toSeq; (definition, discovered) <- Discovery(subclassNames, Set.empty)(src.definitions) if !discovered.isEmpty ) yield for( (file, src) <- CallbackTest.apis(callback); (definition, discovered) <- Discovery(subclassNames, Set.empty)(src.definitions) if !discovered.isEmpty ) yield
(file, definition.name, discovered.baseClasses, discovered.isModule) (file, definition.name, discovered.baseClasses, discovered.isModule)
def subclassNames = Set( "a.Super", "Super2", "x.Super3", "Super4") def subclassNames = Set( "a.Super", "Super2", "x.Super3", "Super4")
} }

View File

@ -36,4 +36,6 @@ object CallbackTest
withTemporaryDirectory { outputDir => withTemporaryDirectory { outputDir =>
TestCompile(scalaVersion, sources, outputDir, Nil) { case (callback, instance, log) => f(callback, outputDir, instance, log) } TestCompile(scalaVersion, sources, outputDir, Nil) { case (callback, instance, log) => f(callback, outputDir, instance, log) }
} }
def apis(callback: xsbti.TestCallback) =
callback.apis.toSeq map { case (src, api) => (src, xsbt.api.APIUtil.minimize(api)) }
} }

View File

@ -16,6 +16,7 @@ Definition
definitionType: DefinitionType definitionType: DefinitionType
selfType: ~Type selfType: ~Type
structure: ~Structure structure: ~Structure
savedAnnotations: String*
TypeMember TypeMember
TypeAlias TypeAlias
tpe: Type tpe: Type