All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opalj.br.VirtualSourceElement.scala Maven / Gradle / Ivy

The newest version!
/* BSD 2-Clause License - see OPAL/LICENSE for details. */
package org.opalj
package br

import scala.math.Ordered

/**
 * A `VirtualSourceElement` is the representation of some source element that may be
 * detached from the concrete source element that represents the implementation; that is,
 * the virtual source element may not have a reference to the concrete element.
 *
 * @author Michael Eichberg
 * @author Marco Torsello
 */
sealed abstract class VirtualSourceElement
    extends SourceElement
    with Ordered[VirtualSourceElement] {

    override def attributes: Attributes = NoAttributes

    final override def isVirtual = true

    /**
     * The "natural order" is VirtualClasses < VirtualFields < VirtualMethods.
     */
    override def compare(that: VirtualSourceElement): Int

    /**
     * Returns the declared/declaring class type of this `VirtualSourceElement`.
     * If this `VirtualSourceElement` is a [[VirtualClass]], the returned type is the
     * declared class else it is the declaring class.
     */
    def classType: ReferenceType

    def toJava: String

    /**
     * Returns the best line number information available.
     */
    def getLineNumber(project: ClassFileRepository): Option[Int]

}

/**
 * Defines common helper functions related to [[VirtualSourceElement]]s.
 */
object VirtualSourceElement {

    def asVirtualSourceElements(
        classFiles:     Iterable[ClassFile],
        includeMethods: Boolean             = true,
        includeFields:  Boolean             = true
    ): Set[VirtualSourceElement] = {
        var sourceElements: Set[VirtualSourceElement] = Set.empty

        classFiles foreach { classFile =>
            val classType = classFile.thisType
            sourceElements += classFile.asVirtualClass
            if (includeMethods)
                classFile.methods.foreach(sourceElements += _.asVirtualMethod(classType))
            if (includeFields)
                classFile.fields.foreach(sourceElements += _.asVirtualField(classType))
        }
        sourceElements
    }

}

/**
 * Represents a class for which we have found some references but have not analyzed
 * any class file or do not want to keep the reference to the underlying class file.
 *
 * @author Michael Eichberg
 */
final case class VirtualClass(thisType: ObjectType) extends VirtualSourceElement {

    override def isClass: Boolean = true

    override def classType: ObjectType = thisType

    override def toJava: String = thisType.toJava

    // Recall that the class may not be the only one defined in a source file!
    override def getLineNumber(project: ClassFileRepository): Option[Int] = None

    override def compare(that: VirtualSourceElement): Int = {
        //x < 0 when this < that; x == 0 when this == that; x > 0 when this > that
        that match {
            case VirtualClass(thatType) => thisType.compare(thatType)
            case _                      => -1
        }
    }

    override def hashCode: Int = thisType.id

    /**
     * Two objects of type `VirtualClass` are considered equal if they represent
     * the same type.
     */
    override def equals(other: Any): Boolean = {
        other match {
            case that: VirtualClass => this.thisType eq that.thisType
            case _                  => false
        }
    }
}

/**
 * @author Michael Eichberg
 */
sealed abstract class VirtualClassMember extends VirtualSourceElement

/**
 * Represents a field of a virtual class.
 *
 * @author Michael Eichberg
 */
final case class VirtualField(
        declaringClassType: ObjectType,
        name:               String,
        fieldType:          FieldType
) extends VirtualClassMember {

    override def isField: Boolean = true

    override def classType: ObjectType = declaringClassType

    override def toJava: String = declaringClassType.toJava+"{ "+fieldType.toJava+" "+name+"; }"

    override def getLineNumber(project: ClassFileRepository): Option[Int] = None

    override def compare(that: VirtualSourceElement): Int = {
        // x < 0 when this < that; x == 0 when this == that; x > 0 when this > that
        that match {
            case _: VirtualClass =>
                1
            case that: VirtualField =>
                if (this.declaringClassType eq that.declaringClassType) {
                    this.name.compareTo(that.name) match {
                        case 0 => this.fieldType.compare(that.fieldType)
                        case x => x
                    }
                } else {
                    if (this.declaringClassType.id < that.declaringClassType.id)
                        -1
                    else
                        1
                }
            case _ /*VirtualMethod*/ =>
                -1
        }
    }

    override def hashCode: Int = {
        (((declaringClassType.id * 41) + name.hashCode()) * 41) + fieldType.id
    }

    override def equals(other: Any): Boolean = {
        other match {
            case that: VirtualField =>
                (this.declaringClassType eq that.declaringClassType) &&
                    (this.fieldType eq that.fieldType) &&
                    this.name == that.name
            case _ => false
        }
    }
}

/**
 * Represents a method of a virtual class.
 *
 * @author Michael Eichberg
 */
sealed class VirtualMethod(
        val declaringClassType: ReferenceType,
        val name:               String,
        val descriptor:         MethodDescriptor
) extends VirtualClassMember {

    override def isMethod: Boolean = true

    override def classType: ReferenceType = declaringClassType

    override def toJava: String = declaringClassType.toJava+"{ "+descriptor.toJava(name)+"; }"

    override def getLineNumber(project: ClassFileRepository): Option[Int] = {
        if (declaringClassType.isArrayType)
            return None;

        project.classFile(declaringClassType.asObjectType).flatMap { cf =>
            cf.findMethod(name, descriptor).flatMap(m => m.body.flatMap(b => b.firstLineNumber))
        }
    }

    override def compare(that: VirtualSourceElement): Int = {
        // x < 0 when this < that; x == 0 when this == that; x > 0 when this > that
        that match {
            case that: VirtualMethod =>
                if (this.declaringClassType eq that.declaringClassType) {
                    this.name.compareTo(that.name) match {
                        case 0 => this.descriptor.compare(that.descriptor)
                        case x => x
                    }
                } else {
                    if (this.declaringClassType.id < that.declaringClassType.id)
                        -1
                    else
                        1
                }
            case _ =>
                1
        }
    }

    override def hashCode: Int = {
        (((declaringClassType.id * 41) + name.hashCode()) * 41) + descriptor.hashCode()
    }

    override def equals(other: Any): Boolean = {
        other match {
            case that: VirtualMethod =>
                (this.declaringClassType eq that.declaringClassType) &&
                    this.descriptor == that.descriptor &&
                    this.name == that.name
            case _ => false
        }
    }

    override def toString: String = {
        s"VirtualMethod($declaringClassType,$name,$descriptor)"
    }
}

object VirtualMethod {

    def apply(
        declaringClassType: ReferenceType,
        name:               String,
        descriptor:         MethodDescriptor
    ): VirtualMethod = {
        new VirtualMethod(declaringClassType, name, descriptor)
    }

    def unapply(virtualMethod: VirtualMethod): Option[(ReferenceType, String, MethodDescriptor)] = {
        Some((
            virtualMethod.declaringClassType,
            virtualMethod.name,
            virtualMethod.descriptor
        ))
    }
}

final case class VirtualForwardingMethod(
        override val declaringClassType: ReferenceType,
        override val name:               String,
        override val descriptor:         MethodDescriptor,
        target:                          Method
) extends VirtualMethod(declaringClassType, name, descriptor) {

    override def toJava: String = declaringClassType.toJava+"{ "+descriptor.toJava(name)+" }"

    override def hashCode: Opcode = (target.hashCode() * 41) + super.hashCode

    override def equals(other: Any): Boolean = other match {
        case that: VirtualForwardingMethod =>
            (this.target eq that.target) &&
                super.equals(other)
        case _ => false
    }

}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy