Skip to content

Commit

Permalink
Optimize annotation extraction
Browse files Browse the repository at this point in the history
  - hand inline enteringPhase to avoid closure allocation
  - avoid looking up getter/setters for non fields
  - optimize for common case of no annotations
  • Loading branch information
retronym committed Feb 22, 2018
1 parent 0321c71 commit be05038
Showing 1 changed file with 63 additions and 28 deletions.
91 changes: 63 additions & 28 deletions internal/compiler-bridge/src/main/scala/xsbt/ExtractAPI.scala
Original file line number Diff line number Diff line change
Expand Up @@ -199,33 +199,62 @@ class ExtractAPI[GlobalType <: Global](
// The compiler only pickles static annotations, so only include these in the API.
// This way, the API is not sensitive to whether we compiled from source or loaded from classfile.
// (When looking at the sources we see all annotations, but when loading from classes we only see the pickled (static) ones.)
private def mkAnnotations(in: Symbol, as: List[AnnotationInfo]): Array[xsbti.api.Annotation] =
staticAnnotations(as).toArray.map { a =>
xsbti.api.Annotation.of(
processType(in, a.atp),
if (a.assocs.isEmpty)
Array(xsbti.api.AnnotationArgument.of("", a.args.mkString("(", ",", ")"))) // what else to do with a Tree?
else
a.assocs
.map {
case (name, value) =>
xsbti.api.AnnotationArgument.of(name.toString, value.toString)
}
.toArray[xsbti.api.AnnotationArgument]
)
}
private def mkAnnotations(in: Symbol, as: List[AnnotationInfo]): Array[xsbti.api.Annotation] = {
if (in == NoSymbol) ExtractAPI.emptyAnnotationArray
else
staticAnnotations(as) match {
case Nil => ExtractAPI.emptyAnnotationArray
case staticAs =>
staticAs.map { a =>
xsbti.api.Annotation.of(
processType(in, a.atp),
if (a.assocs.isEmpty)
Array(xsbti.api.AnnotationArgument.of("", a.args.mkString("(", ",", ")"))) // what else to do with a Tree?
else
a.assocs
.map {
case (name, value) =>
xsbti.api.AnnotationArgument.of(name.toString, value.toString)
}
.toArray[xsbti.api.AnnotationArgument]
)
}.toArray
}
}

private def annotations(in: Symbol, s: Symbol): Array[xsbti.api.Annotation] =
enteringPhase(currentRun.typerPhase) {
// HOT method, hand optimized to reduce allocations and needless creation of Names with calls to getterIn/setterIn
// on non-fields.
private def annotations(in: Symbol, s: Symbol): Array[xsbti.api.Annotation] = {
val saved = phase
phase = currentRun.typerPhase
try {
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.getterIn(b.enclClass), b.setterIn(b.enclClass)).filter(_ != NoSymbol)
associated.flatMap(ss => mkAnnotations(in, ss.annotations)).distinct.toArray
if (b.hasGetter) {
val annotations = collection.mutable.LinkedHashSet[xsbti.api.Annotation]()
def add(sym: Symbol) = {
val anns = mkAnnotations(in, sym.annotations)
var i = 0
while (i < anns.length) {
annotations += anns(i)
i += 1
}
}
add(b)
add(b.getterIn(b.enclClass))
add(b.setterIn(b.enclClass))
annotations.toArray.distinct
} else {
if (b.annotations.isEmpty) ExtractAPI.emptyAnnotationArray
else mkAnnotations(in, b.annotations)
}
} finally {
phase = saved
}
}

private def viewer(s: Symbol) = (if (s.isModule) s.moduleClass else s).thisType

Expand Down Expand Up @@ -715,13 +744,19 @@ class ExtractAPI[GlobalType <: Global](
n2.trim
}

private def staticAnnotations(annotations: List[AnnotationInfo]): List[AnnotationInfo] = {
// compat stub for 2.8/2.9
class IsStatic(ann: AnnotationInfo) {
def isStatic: Boolean =
ann.atp.typeSymbol isNonBottomSubClass definitions.StaticAnnotationClass
private def staticAnnotations(annotations: List[AnnotationInfo]): List[AnnotationInfo] =
if (annotations == Nil) Nil
else {
// compat stub for 2.8/2.9
class IsStatic(ann: AnnotationInfo) {
def isStatic: Boolean =
ann.atp.typeSymbol isNonBottomSubClass definitions.StaticAnnotationClass
}
implicit def compat(ann: AnnotationInfo): IsStatic = new IsStatic(ann)
annotations.filter(_.isStatic)
}
implicit def compat(ann: AnnotationInfo): IsStatic = new IsStatic(ann)
annotations.filter(_.isStatic)
}
}

object ExtractAPI {
private val emptyAnnotationArray = new Array[xsbti.api.Annotation](0)
}

0 comments on commit be05038

Please sign in to comment.