org.opalj.hermes.queries.util.APIFeatureQuery.scala Maven / Gradle / Ivy
/* BSD 2-Clause License - see OPAL/LICENSE for details. */
package org.opalj
package hermes
package queries
package util
import scala.collection.mutable
import org.opalj.collection.immutable.Naught
import org.opalj.collection.immutable.Chain
import org.opalj.br.MethodWithBody
import org.opalj.br.ObjectType
import org.opalj.br.analyses.Project
import org.opalj.br.instructions.MethodInvocationInstruction
import org.opalj.da.ClassFile
/**
* A predefined query for finding simple API features. It supports - in particular -
* features that check for certain API calls. Subclasses are only required to define
* a `Chain` of `APIFeatures`.
*
* Example of an `apiFeature` declaration in a subclass:
* {{{
* override def apiFeatures: Chain[APIFeatures] = Chain[APIFeature](
* val Unsafe = ObjectType("sun/misc/Unsafe")
*
* StaticAPIMethod(Unsafe, "getUnsafe", MethodDescriptor("()Lsun/misc/Unsafe;")),
* APIFeatureGroup(
* Chain(InstanceAPIMethod(Unsafe, "allocateInstance")),
* "Unsafe - Alloc"
* ),
* APIFeatureGroup(
* Chain(
* InstanceAPIMethod(Unsafe, "arrayIndexScale"),
* InstanceAPIMethod(Unsafe, "arrayBaseOffset")
* ),
* "Unsafe - Array"
* )
* )
* }}}
*
* @author Michael Reif
*/
abstract class APIFeatureQuery(implicit hermes: HermesConfig) extends FeatureQuery {
def apiFeatures: Chain[APIFeature]
/**
* The unique ids of the computed features.
*/
override lazy val featureIDs: Seq[String] = apiFeatures.map(_.featureID).toSeq
/**
* Returns the set of all relevant receiver types.
*/
private[this] final lazy val apiTypes: Set[ObjectType] = {
apiFeatures.foldLeft(Set.empty[ObjectType])(_ ++ _.apiMethods.map(_.declClass))
}
/**
* Analyzes the project and extracts the feature information.
*
* @note '''Every query should regularly check that its thread is not interrupted!''' E.g.,
* using `Thread.currentThread().isInterrupted()`.
*/
override def apply[S](
projectConfiguration: ProjectConfiguration,
project: Project[S],
rawClassFiles: Traversable[(ClassFile, S)]
): TraversableOnce[Feature[S]] = {
val classHierarchy = project.classHierarchy
import classHierarchy.allSubtypes
import project.isProjectType
def getClassFileLocation(objectType: ObjectType): Option[ClassFileLocation[S]] = {
val classFile = project.classFile(objectType)
classFile.flatMap { cf ⇒ project.source(cf).map(src ⇒ ClassFileLocation(src, cf)) }
}
var occurrencesCount = apiFeatures.foldLeft(Map.empty[String, Int])(
(result, feature) ⇒ result + ((feature.featureID, 0))
)
// TODO Use LocationsContainer
val locations = mutable.Map.empty[String, Chain[Location[S]]]
for {
classFeature ← apiFeatures.collect { case ce: ClassExtension ⇒ ce }
featureID = classFeature.featureID
subtypes = allSubtypes(classFeature.declClass, reflexive = false).filter(isProjectType)
size = subtypes.size
if size > 0
} {
val count = occurrencesCount(featureID) + size
occurrencesCount += ((featureID, count))
for {
subtype ← subtypes
if project.isProjectType(subtype)
classFileLocation ← getClassFileLocation(subtype)
} {
locations += ((
featureID,
classFileLocation :&: locations.getOrElse(featureID, Naught)
))
}
}
// Checking method API features
for {
cf ← project.allProjectClassFiles
if !isInterrupted()
source ← project.source(cf)
m @ MethodWithBody(code) ← cf.methods
pcAndInvocation ← code collect { case mii: MethodInvocationInstruction ⇒ mii }
pc = pcAndInvocation.pc
mii = pcAndInvocation.value
declClass = mii.declaringClass
if declClass.isObjectType
if apiTypes.contains(declClass.asObjectType)
apiFeature ← apiFeatures
featureID = apiFeature.featureID
APIMethod ← apiFeature.apiMethods
if APIMethod.matches(mii)
} {
val l = InstructionLocation(source, m, pc)
locations += ((featureID, l :&: locations.getOrElse(featureID, Naught)))
val count = occurrencesCount(featureID) + 1
occurrencesCount = occurrencesCount + ((featureID, count))
}
apiFeatures.map { apiFeature ⇒
val featureID = apiFeature.featureID
Feature(
featureID,
occurrencesCount(featureID),
locations.getOrElse(featureID, Naught)
)
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy