
org.opalj.br.analyses.MethodDeclarationContext.scala Maven / Gradle / Ivy
The newest version!
/* BSD 2-Clause License - see OPAL/LICENSE for details. */
package org.opalj
package br
package analyses
import org.opalj.bi.VisibilityModifier
import org.opalj.bi.ACC_PUBLIC
import org.opalj.bi.ACC_PROTECTED
import org.opalj.bi.ACC_PRIVATE
/**
* Encapsulates the information about a '''non-abstract''', '''non-static''' method which is
* '''not an initializer''' (`<(cl)init>`) and which is required when determining potential call
* targets.
*
* @note A class may have -- w.r.t. a given package name -- at most one package
* visible method which has a specific name and descriptor combination.
* For methods with protected or public visibility a class always has at
* most one method with a given name and descriptor.
*
* @note Equality is defined based on the name, descriptor and declaring package
* of a method (the concrete declaring class is not considered!).
*
* @author Michael Eichberg
*/
final class MethodDeclarationContext(val method: Method) extends Ordered[MethodDeclarationContext] {
assert(!method.isStatic)
assert(!method.isInitializer)
def declaringClassType: ObjectType = method.declaringClassFile.thisType
def packageName: String = declaringClassType.packageName
def isPublic: Boolean = method.isPublic
def name: String = method.name
def descriptor: MethodDescriptor = method.descriptor
override def equals(other: Any): Boolean = {
other match {
case that: MethodDeclarationContext =>
this.packageName == that.packageName && {
val thisMethod = this.method
val thatMethod = that.method
thisMethod.name == thatMethod.name &&
thisMethod.descriptor == thatMethod.descriptor
}
case _ =>
false
}
}
/**
* The hash code is equal to the ``"hashCode of the descriptor of the underlying method"
* * 113 + "the hashCode of the package of the declaring class"``.
*/
override def hashCode: Int = method.descriptor.hashCode * 113 + packageName.hashCode()
override def toString: String = {
val packageName = if (this.packageName == "") "" else this.packageName
s"MethodDeclarationContext($packageName, ${method.signatureToJava()})"
}
/**
* Compares this `MethodDeclarationContext` with the given one. Defines a total order w.r.t.
* the name, descriptor and declaring package of a method. (The declaring class is not
* considered and, therefore, two `MethodDeclarationContext`s may be considered equal
* even though the underlying method is not the same one.)
*/
def compare(that: MethodDeclarationContext): Int = {
val result = this.method compare that.method
if (result == 0)
this.packageName compareTo that.packageName
else
result
}
def compareWithPublicMethod(thatMethod: Method): Int = {
assert(thatMethod.isPublic, s"${thatMethod.toJava} is not public")
this.method compare thatMethod
}
/**
* Compares this method (declaration context) with an (implicit) method which has the
* given `name` and `descriptor` and which is defined in the given `package`
* unless this method is protected or public. In that case the
* `packageName` is not compared and "0" (<=> equal) is returned.
* Hence, this `compare` method is well suited to make a lookup for a matching
* method declaration context in a sorted array of method declaration
* contexts.
*/
def compareAccessibilityAware(
packageName: String, // only considered if name and descriptor already match...
name: String,
descriptor: MethodDescriptor
): Int = {
val method = this.method
val result = method.compare(name, descriptor)
if (result == 0 && method.hasDefaultVisibility) {
this.packageName compareTo packageName
} else {
result
}
}
def compareAccessibilityAware(
declaringClass: ObjectType,
m: Method
): Int = {
compareAccessibilityAware(declaringClass.packageName, m.name, m.descriptor)
}
/**
* Returns `true` if this method directly overrides the given method.
*
* (Note: indirect overriding can only be determined if all intermediate methods
* are known; only in that case it is possible to test if, e.g., a public method
* in package x indirectly overrides a package visible method in a package y.)
*
* @note The overrides relation is reflexive as defined by the JVM specification
* (Section: "Overriding").
*
* @param that The [[MethodDeclarationContext]] object of another method which is defined by
* the same class as this method or a superclass thereof. If the
* other method is defined by some other class with which this class
* is not in a sub-/supertype relation, the result is not defined.
*/
def directlyOverrides(that: MethodDeclarationContext): Boolean = {
// mc and ma are used as in the JVM spec
val mc = this.method
val ma = that.method
mc == ma || (
mc.name == ma.name &&
mc.descriptor == ma.descriptor &&
canDirectlyOverride(ma.visibilityModifier, that.packageName)
)
}
/**
* Returns true if a method with the same signature as this method that is defined in
* the given package directly overrides this encapsultated method. This property
* always holds if this method has public or protected visiblity. If this method
* has package visibility, the other (implicit) method has to be defined in
* this method's package.
*/
def isDirectlyOverriddenBy(packageName: String): Boolean = {
!this.method.hasDefaultVisibility || this.packageName == packageName
}
/**
* Performs the accessibility check required when we need to determine
* if this method (`mc`) overrides another method (`ma`).
*
* @note This method must be defined by a class which is a subtype of the
* declaring class of the other method.
*/
def canDirectlyOverride(
visibility: Option[VisibilityModifier],
packageName: String
): Boolean = {
visibility match {
case Some(ACC_PUBLIC) | Some(ACC_PROTECTED) => true
case Some(ACC_PRIVATE) => false
case None => this.packageName == packageName
}
}
}
/**
* Definition of factory and extractor methods for [[MethodDeclarationContext]] objects.
*/
object MethodDeclarationContext {
def apply(method: Method): MethodDeclarationContext = {
new MethodDeclarationContext(method)
}
def unapply(mdc: MethodDeclarationContext): Some[Method] = Some(mdc.method)
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy