
org.opalj.br.MethodDescriptor.scala Maven / Gradle / Ivy
The newest version!
/* BSD 2-Clause License - see OPAL/LICENSE for details. */
package org.opalj
package br
import scala.annotation.switch
import scala.collection.Seq
import scala.collection.immutable.ArraySeq
import scala.math.Ordered
/**
* A method descriptor represents the parameters that the method takes and
* the value that it returns.
*
* @note The `equals(Any):Boolean` method takes the number of parameters and types
* into account. I.e., two method descriptor objects are equal if they have
* the same number of parameters and each parameter has the same [[Type]].
*
* @author Michael Eichberg
*/
sealed abstract class MethodDescriptor
extends ConstantValue[MethodDescriptor]
with (Int => FieldType)
with Ordered[MethodDescriptor] {
def parameterTypes: FieldTypes
def apply(parameterIndex: Int): FieldType = {
parameterTypes(parameterIndex)
}
def parameterType(index: Int): FieldType
def parametersCount: Int
final def copy(
parameterTypes: FieldTypes = this.parameterTypes,
returnType: Type = this.returnType
): MethodDescriptor = {
MethodDescriptor(parameterTypes, returnType)
}
/**
* The number of registers required to store the method parameters.
*
* @note An additional register may be required for storing the self
* reference `this`.
*/
def requiredRegisters: Int = parameterTypes.foldLeft(0)(_ + _.operandSize)
def returnType: Type
def toJVMDescriptor: String = {
parameterTypes.iterator
.map[String](_.toJVMTypeName)
.mkString("(", "", ")"+returnType.toJVMTypeName)
}
def value: this.type = this
override def runtimeValueType: ObjectType = ObjectType.MethodType
def valueToString: String = toUMLNotation
/**
* Returns a Java like view when a MethodDescriptor is used as a [[BootstrapArgument]].
*/
def toJava: String = {
val parameterTypes =
this.parameterTypes.iterator.map[String](_.toJava).mkString("(", ",", ")")
s"MethodDescriptor(${returnType.toJava},$parameterTypes)"
}
def equalParameters(other: MethodDescriptor): Boolean
/**
* Selects the indexes of the parameters that pass the filter function.
*
* @note This index is not necessarily identical to the value used to identify
* the origin of value (a parameter passed to a method).
*/
def selectParameter(f: FieldType => Boolean): Seq[Int] = {
var i = 0
val max = parametersCount
var indexes: Seq[Int] = Nil
while (i < max) {
if (f(parameterType(i))) {
indexes = indexes :+ i
}
i += 1
}
indexes
}
/**
* @return `true` iff a parameter – except of the last one – is a computational type category
* 2 value; i.e., is a long or double value. If all values are category 1 values, then
* the parameters are store in the first n registers/local variables.
*
*/
def hasComputationalTypeCategory2ValueInInit: Boolean
//
//
// SUPPORT FOR SPECIAL REPRESENTATIONS
//
//
def toJava(methodName: String): String = {
parameterTypes.view.map(_.toJava).mkString(s"${returnType.toJava} $methodName(", ",", ")")
}
def toJava(declaringClassName: String, methodName: String): String = {
s"$declaringClassName{ ${toJava(methodName)} }"
}
def toUMLNotation: String = {
"("+{
if (parameterTypes.isEmpty)
""
else
parameterTypes.tail.foldLeft(parameterTypes.head.toJava)(_+", "+_.toJava)
}+"): "+returnType.toJava
}
override def <(other: MethodDescriptor): Boolean = {
val thisParametersCount = this.parametersCount
val otherParametersCount = other.parametersCount
(thisParametersCount < otherParametersCount) || (
thisParametersCount == otherParametersCount &&
{
var i = 0
val iMax = this.parametersCount
while (i < iMax) {
if (this.parameterTypes(i) < other.parameterTypes(i))
return true;
else if (other.parameterTypes(i) < this.parameterTypes(i))
return false;
else // the types are identical
i += 1
}
this.returnType < other.returnType
}
)
}
override def toString: String = "MethodDescriptor("+toUMLNotation+")"
}
//
// To optimize the overall memory consumption and to facilitate the storage of
// method descriptors in sets, we have specialized the MethodDescriptor.
// (Done after a study of the heap memory usage.)
//
private object NoArgumentAndNoReturnValueMethodDescriptor extends MethodDescriptor {
override def returnType: VoidType = VoidType
override def parameterTypes: FieldTypes = NoFieldTypes
override def parameterType(index: Int): FieldType = throw new IndexOutOfBoundsException()
override def parametersCount: Int = 0
override def hasComputationalTypeCategory2ValueInInit: Boolean = false
override def equalParameters(other: MethodDescriptor): Boolean = {
other.parametersCount == 0
}
override def compare(other: MethodDescriptor): Int = {
if (other == NoArgumentAndNoReturnValueMethodDescriptor)
0
else
-1
}
// the default equals and hashCode implementations are a perfect fit
}
private final class NoArgumentMethodDescriptor(val returnType: Type) extends MethodDescriptor {
override def parameterTypes: FieldTypes = NoFieldTypes
override def parameterType(index: Int): FieldType = throw new IndexOutOfBoundsException()
override def parametersCount: Int = 0
override def hasComputationalTypeCategory2ValueInInit: Boolean = false
override def equalParameters(other: MethodDescriptor): Boolean =
other.parametersCount == 0
override def hashCode: Int = returnType.hashCode
override def equals(other: Any): Boolean = {
other match {
case that: NoArgumentMethodDescriptor => that.returnType eq this.returnType
case _ => false
}
}
override def compare(other: MethodDescriptor): Int = {
(other.parametersCount: @switch) match {
case 0 => this.returnType.compare(other.returnType)
case _ => -1
}
}
}
private final class SingleArgumentMethodDescriptor(
val parameterType: FieldType,
val returnType: Type
) extends MethodDescriptor {
override def parameterTypes: FieldTypes = ArraySeq(parameterType)
override def parameterType(index: Int): FieldType = {
if (index == 0)
parameterType
else
throw new IndexOutOfBoundsException()
}
override def parametersCount: Int = 1
override def hasComputationalTypeCategory2ValueInInit: Boolean = false
override def equalParameters(other: MethodDescriptor): Boolean = {
(other.parametersCount == 1) && (other.parameterType(0) == parameterType)
}
override lazy val hashCode: Int = (returnType.hashCode() * 61) + parameterType.hashCode
override def equals(other: Any): Boolean = {
other match {
case that: SingleArgumentMethodDescriptor =>
(that.parameterType eq this.parameterType) && (that.returnType eq this.returnType)
case _ =>
false
}
}
override def compare(other: MethodDescriptor): Int = {
(other.parametersCount: @switch) match {
case 0 => 1
case 1 =>
val c = parameterType compare other.parameterType(0)
if (c != 0)
c
else
this.returnType.compare(other.returnType)
case _ => -1
}
}
}
private final class TwoArgumentsMethodDescriptor(
val firstParameterType: FieldType,
val secondParameterType: FieldType,
val returnType: Type
) extends MethodDescriptor {
override def parameterTypes: FieldTypes = ArraySeq(firstParameterType, secondParameterType)
override def parameterType(index: Int): FieldType = {
(index: @switch) match {
case 0 => firstParameterType
case 1 => secondParameterType
case _ => throw new IndexOutOfBoundsException()
}
}
override def parametersCount: Int = 2
override def hasComputationalTypeCategory2ValueInInit: Boolean = {
firstParameterType.computationalType.categoryId == 2
}
override def compare(other: MethodDescriptor): Int = {
(other.parametersCount: @switch) match {
case 0 | 1 => 1
case 2 =>
var c = firstParameterType compare other.parameterType(0)
if (c != 0)
c
else {
c = secondParameterType compare other.parameterType(1)
if (c != 0) {
c
} else {
this.returnType.compare(other.returnType)
}
}
case _ => -1
}
}
override def equalParameters(other: MethodDescriptor): Boolean = {
(other.parametersCount == 2) &&
(other.parameterType(0) == firstParameterType) &&
(other.parameterType(1) == secondParameterType)
}
override lazy val hashCode: Int = {
(firstParameterType.hashCode * 13 + secondParameterType.hashCode) * 61 +
returnType.hashCode()
}
override def equals(other: Any): Boolean = {
other match {
case that: TwoArgumentsMethodDescriptor =>
(that.firstParameterType eq this.firstParameterType) &&
(that.secondParameterType eq this.secondParameterType) &&
(that.returnType eq this.returnType)
case _ =>
false
}
}
}
private final class MultiArgumentsMethodDescriptor(
val parameterTypes: FieldTypes,
val returnType: Type
) extends MethodDescriptor {
override def parameterType(index: Int): FieldType = parameterTypes(index)
override def parametersCount: Int = parameterTypes.size
override def hasComputationalTypeCategory2ValueInInit: Boolean = {
var i = 0
val max = parameterTypes.size - 1
while (i < max) {
if (parameterTypes(i).computationalType.categoryId == 2)
return true;
i += 1
}
false
}
override def compare(other: MethodDescriptor): Int = {
val thisParametersCount = this.parametersCount
val otherParametersCount = other.parametersCount
if (thisParametersCount < otherParametersCount)
-1
else if (thisParametersCount > otherParametersCount)
1
else {
var i = 0
while (i < thisParametersCount) {
val comparisonResult = this.parameterTypes(i).compare(other.parameterTypes(i))
if (comparisonResult != 0)
return comparisonResult;
else // the types are identical
i += 1
}
this.returnType.compare(other.returnType)
}
}
override def equalParameters(other: MethodDescriptor): Boolean = {
other.parameterTypes == this.parameterTypes
}
override lazy val hashCode: Int = (returnType.hashCode * 13) + parameterTypes.hashCode
override def equals(other: Any): Boolean = {
other match {
case that: MethodDescriptor =>
(this.returnType eq that.returnType) &&
this.parametersCount == that.parametersCount &&
{
var i = parametersCount
while (i > 0) {
i = i - 1
if (this.parameterTypes(i) ne that.parameterTypes(i))
return false;
}
true
}
case _ =>
false
}
}
}
object HasNoArgsAndReturnsVoid {
def unapply(md: MethodDescriptor): Boolean = {
md match {
case NoArgumentAndNoReturnValueMethodDescriptor => true
case _ => false
}
}
}
object NoArgumentMethodDescriptor {
def unapply(md: MethodDescriptor): Option[Type] = {
md match {
case md: NoArgumentMethodDescriptor => Some(md.returnType)
case _ => None
}
}
}
/**
* Extractor for method descriptors defining a single parameter type and some return type.
*
* @author Michael Eichberg
*/
object SingleArgumentMethodDescriptor {
def apply(parameterType: FieldType, returnType: Type = VoidType): MethodDescriptor = {
new SingleArgumentMethodDescriptor(parameterType, returnType)
}
def unapply(md: MethodDescriptor): Option[(FieldType, Type)] = {
md match {
case md: SingleArgumentMethodDescriptor => Some((md.parameterType, md.returnType))
case _ => None
}
}
}
/**
* Extractor for method descriptors defining a single parameter type.
*
* @author Michael Eichberg
*/
object TheArgument {
/**
* Returns `Some(FieldType)` of the first paramter type if the given method
* descriptor just defines a single paramter.
*
* @author Michael Eichberg
*/
def unapply(md: MethodDescriptor): Option[FieldType] = {
if (md.parametersCount == 1) {
Some(md.parameterType(0))
} else {
None
}
}
}
object TwoArgumentsMethodDescriptor {
def unapply(md: MethodDescriptor): Option[(FieldType, FieldType, Type)] = {
md match {
case md: TwoArgumentsMethodDescriptor =>
Some((md.firstParameterType, md.secondParameterType, md.returnType))
case _ =>
None
}
}
}
/**
* Defines extractor and factory methods for MethodDescriptors.
*
* @author Michael Eichberg
*/
object MethodDescriptor {
def unapply(md: MethodDescriptor): Option[(FieldTypes, Type)] = {
Some((md.parameterTypes, md.returnType))
}
final val NoArgsAndReturnVoid: MethodDescriptor = NoArgumentAndNoReturnValueMethodDescriptor
final def DefaultConstructorDescriptor: MethodDescriptor = {
NoArgumentAndNoReturnValueMethodDescriptor
}
/**
* The signatures of a signature polymorphic method.
* Basically, the signature is one of:
* {{{
* (params: Object[]) : Object
* (params: Object[]) : void
* (params: Object[]) : boolean
* }}}
*/
final val SignaturePolymorphicMethodObject: MethodDescriptor = {
new SingleArgumentMethodDescriptor(ArrayType.ArrayOfObject, ObjectType.Object)
}
final val SignaturePolymorphicMethodVoid: MethodDescriptor = {
new SingleArgumentMethodDescriptor(ArrayType.ArrayOfObject, VoidType)
}
final val SignaturePolymorphicMethodBoolean: MethodDescriptor = {
new SingleArgumentMethodDescriptor(ArrayType.ArrayOfObject, BooleanType)
}
final val JustReturnsBoolean: MethodDescriptor = new NoArgumentMethodDescriptor(BooleanType)
final val JustReturnsByte: MethodDescriptor = new NoArgumentMethodDescriptor(ByteType)
final val JustReturnsShort: MethodDescriptor = new NoArgumentMethodDescriptor(ShortType)
final val JustReturnsChar: MethodDescriptor = new NoArgumentMethodDescriptor(CharType)
final val JustReturnsInteger: MethodDescriptor = new NoArgumentMethodDescriptor(IntegerType)
final val JustReturnsFloat: MethodDescriptor = new NoArgumentMethodDescriptor(FloatType)
final val JustReturnsDouble: MethodDescriptor = new NoArgumentMethodDescriptor(DoubleType)
final val JustReturnsLong: MethodDescriptor = new NoArgumentMethodDescriptor(LongType)
final val JustReturnsObject: MethodDescriptor = {
new NoArgumentMethodDescriptor(ObjectType.Object)
}
final val JustReturnsClass: MethodDescriptor = {
new NoArgumentMethodDescriptor(ObjectType.Class)
}
final val JustReturnsString: MethodDescriptor = {
new NoArgumentMethodDescriptor(ObjectType.String)
}
final val JustTakesObject: MethodDescriptor = apply(ObjectType.Object, VoidType)
def JustTakes(parameterType: FieldType): MethodDescriptor = {
new SingleArgumentMethodDescriptor(parameterType, VoidType)
}
final val ReadObjectDescriptor = {
MethodDescriptor(ObjectType("java/io/ObjectInputStream"), VoidType)
}
final val WriteObjectDescriptor = {
MethodDescriptor(ObjectType("java/io/ObjectOutputStream"), VoidType)
}
final val ReadObjectInputDescriptor = {
MethodDescriptor(ObjectType("java/io/ObjectInput"), VoidType)
}
final val WriteObjectOutputDescriptor = {
MethodDescriptor(ObjectType("java/io/ObjectOutput"), VoidType)
}
/**
* Descriptor of the method `java.lang.invoke.LambdaMetafactory.metafactory`.
*/
final val LambdaMetafactoryDescriptor = {
MethodDescriptor(
ArraySeq(
ObjectType.MethodHandles$Lookup,
ObjectType.String,
ObjectType.MethodType,
ObjectType.MethodType,
ObjectType.MethodHandle,
ObjectType.MethodType
),
ObjectType.CallSite
)
}
/**
* Descriptor of the method `java.lang.invoke.LambdaMetafactory.altMetafactory`.
*/
final val LambdaAltMetafactoryDescriptor = {
MethodDescriptor(
ArraySeq(
ObjectType.MethodHandles$Lookup,
ObjectType.String,
ObjectType.MethodType,
ArrayType.ArrayOfObject
),
ObjectType.CallSite
)
}
/**
* Descriptor of the method `scala.runtime.LambdaDeserializer.bootstrap`.
*/
final val ScalaLambdaDeserializeDescriptor = {
MethodDescriptor(
ArraySeq(
ObjectType.MethodHandles$Lookup,
ObjectType.String,
ObjectType.MethodType,
ArrayType.ArrayOfMethodHandle
),
ObjectType.CallSite
)
}
/**
* Descriptor of the method `scala.runtime.SymbolLiteral.bootstrap`.
*/
final val ScalaSymbolLiteralDescriptor = {
MethodDescriptor(
ArraySeq(
ObjectType.MethodHandles$Lookup,
ObjectType.String,
ObjectType.MethodType,
ObjectType.String
),
ObjectType.CallSite
)
}
/**
* Descriptor of the method `scala.runtime.StructuralCallSite.bootstrap`.
*/
final val ScalaStructuralCallSiteDescriptor = {
MethodDescriptor(
ArraySeq(
ObjectType.MethodHandles$Lookup,
ObjectType.String,
ObjectType.MethodType,
ObjectType.MethodType
),
ObjectType.CallSite
)
}
/**
* Descriptor of the method `java.lang.invoke.ConstantBootstraps.primitiveClass`.
*/
final val ConstantBootstrapsPrimitiveClassDescriptor = {
MethodDescriptor(
ArraySeq(
ObjectType.MethodHandles$Lookup,
ObjectType.String,
ObjectType.Class
),
ObjectType.Class
)
}
/**
* Descriptor of the methods `java.lang.invoke.MethodHandles$Lookup.findVarHandle` and
* `java.lang.invoke.MethodHandles$Lookup.findStaticVarHandle`.
*/
final val FindVarHandleDescriptor = {
MethodDescriptor(
ArraySeq(
ObjectType.Class,
ObjectType.String,
ObjectType.Class
),
ObjectType.VarHandle
)
}
def withNoArgs(returnType: Type): MethodDescriptor = {
(returnType.id: @scala.annotation.switch) match {
case VoidType.id => NoArgumentAndNoReturnValueMethodDescriptor
case BooleanType.id => JustReturnsBoolean
case ByteType.id => JustReturnsByte
case ShortType.id => JustReturnsShort
case CharType.id => JustReturnsChar
case IntegerType.id => JustReturnsInteger
case LongType.id => JustReturnsLong
case FloatType.id => JustReturnsFloat
case DoubleType.id => JustReturnsDouble
case ObjectType.ObjectId => JustReturnsObject
case ObjectType.StringId => JustReturnsString
case ObjectType.ClassId => JustReturnsClass
case _ => new NoArgumentMethodDescriptor(returnType)
}
}
def apply(parameterType: FieldType, returnType: Type): MethodDescriptor = {
new SingleArgumentMethodDescriptor(parameterType, returnType)
}
def apply(parameterTypes: FieldTypes, returnType: Type): MethodDescriptor = {
(parameterTypes.size: @annotation.switch) match {
case 0 =>
withNoArgs(returnType)
case 1 =>
new SingleArgumentMethodDescriptor(parameterTypes(0), returnType)
case 2 =>
new TwoArgumentsMethodDescriptor(parameterTypes(0), parameterTypes(1), returnType)
case _ =>
new MultiArgumentsMethodDescriptor(parameterTypes, returnType)
}
}
def apply(md: String): MethodDescriptor = {
var index = 1 // we are not interested in the leading '('
val parameterTypesBuilder = newFieldTypesBuilder()
while (md.charAt(index) != ')') {
val (ft, nextIndex) = parseParameterType(md, index)
parameterTypesBuilder += ft
index = nextIndex
}
val returnType = ReturnType(md.substring(index + 1))
apply(parameterTypesBuilder.result(), returnType)
}
private[this] def parseParameterType(md: String, startIndex: Int): (FieldType, Int) = {
val td = md.charAt(startIndex)
(td: @scala.annotation.switch) match {
case 'L' =>
val endIndex = md.indexOf(';', startIndex + 1)
( // this is the return tuple
ObjectType(md.substring(startIndex + 1, endIndex)),
endIndex + 1
)
case '[' =>
val (ft, index) = parseParameterType(md, startIndex + 1)
( // this is the return tuple
ArrayType(ft),
index
)
case _ =>
( // this is the return tuple
FieldType(td.toString),
startIndex + 1
)
}
}
}
/**
* Extractor for JVM method descriptors (for example, "(I[Ljava/lang/Object;])V").
*/
object JVMMethodDescriptor {
def unapply(md: MethodDescriptor): Some[String] = Some(md.toJVMDescriptor)
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy