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

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

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

import scala.xml.Node
import scala.xml.Text
import scala.collection.mutable.Builder
import com.typesafe.config.Config
import com.typesafe.config.ConfigFactory
import org.opalj.collection.immutable.IntTrieSet
import org.opalj.collection.immutable.BitArraySet
import org.opalj.collection.immutable.UIDSet
import org.opalj.log.LogContext
import org.opalj.log.GlobalLogContext
import org.opalj.log.OPALLogger.info
import org.opalj.bi.AccessFlags
import org.opalj.bi.AccessFlagsContexts

import scala.collection.immutable.ArraySeq

/**
 * In this representation of Java bytecode references to a Java class file's constant
 * pool and to attributes are replaced by direct references to the corresponding constant
 * pool entries. This facilitates developing analyses and fosters comprehension.
 *
 * Based on the fact that indirect
 * references to constant pool entries are resolved and replaced by direct references this
 * representation is called the resolved representation.
 *
 * This representation of Java bytecode is considered as OPAL's standard representation
 * for writing Scala based analyses. This representation is engineered such
 * that it facilitates writing analyses that use pattern matching.
 *
 * @author Michael Eichberg
 */
package object br {

    final val FrameworkName = "OPAL Bytecode Representation"

    {
        implicit val logContext: LogContext = GlobalLogContext
        try {
            assert(false) // <= test whether assertions are turned on or off...
            info(FrameworkName, "Production Build")
        } catch {
            case _: AssertionError => info(FrameworkName, "Development Build with Assertions")
        }
    }

    // We want to make sure that the class loader is used which potentially can
    // find the config files; the libraries (e.g., Typesafe Config) may have
    // been loaded using the parent class loader and, hence, may not be able to
    // find the config files at all.
    val BaseConfig: Config = ConfigFactory.load(this.getClass.getClassLoader)

    final val ConfigKeyPrefix = "org.opalj.br."

    type LiveVariables = Array[BitArraySet]

    type Attributes = ArraySeq[Attribute]
    val Attributes: ArraySeq.type = ArraySeq
    final def NoAttributes: Attributes = ArraySeq.empty

    type ElementValues = ArraySeq[ElementValue]
    type ElementValuePairs = ArraySeq[ElementValuePair]
    val ElementValuePairs: ArraySeq.type = ArraySeq
    final def NoElementValuePairs: ElementValuePairs = ArraySeq.empty

    type Annotations = ArraySeq[Annotation]
    def NoAnnotations: ArraySeq[Annotation] = ArraySeq.empty
    type TypeAnnotations = ArraySeq[TypeAnnotation]
    final def NoTypeAnnotations: ArraySeq[TypeAnnotation] = ArraySeq.empty

    type InnerClasses = ArraySeq[InnerClass]

    type Interfaces = ArraySeq[ObjectType]
    final def NoInterfaces: Interfaces = ArraySeq.empty

    type Methods = ArraySeq[Method]
    val Methods: ArraySeq.type = ArraySeq
    final def NoMethods: Methods = ArraySeq.empty
    type MethodTemplates = ArraySeq[MethodTemplate]
    final def NoMethodTemplates: MethodTemplates = ArraySeq.empty

    type Exceptions = ArraySeq[ObjectType]
    type ExceptionHandlers = ArraySeq[ExceptionHandler]
    final def NoExceptionHandlers: ExceptionHandlers = ArraySeq.empty

    type LineNumbers = ArraySeq[LineNumber]
    type LocalVariableTypes = ArraySeq[LocalVariableType]
    type LocalVariables = ArraySeq[LocalVariable]
    type BootstrapMethods = ArraySeq[BootstrapMethod]
    type BootstrapArguments = ArraySeq[BootstrapArgument]
    type ParameterAnnotations = ArraySeq[Annotations]
    final def NoParameterAnnotations: ParameterAnnotations = ArraySeq.empty
    type StackMapFrames = ArraySeq[StackMapFrame]
    type VerificationTypeInfoLocals = ArraySeq[VerificationTypeInfo]
    type VerificationTypeInfoStack = ArraySeq[VerificationTypeInfo]
    type MethodParameters = ArraySeq[MethodParameter]

    type Fields = ArraySeq[Field]
    final def NoFields: Fields = ArraySeq.empty

    type FieldTemplates = ArraySeq[FieldTemplate]
    final def NoFieldTemplates: FieldTemplates = ArraySeq.empty

    type Instructions = Array[instructions.Instruction]

    type MethodDescriptors = ArraySeq[MethodDescriptor]

    type InstructionLabels = ArraySeq[instructions.InstructionLabel]

    type ObjectTypes = ArraySeq[ObjectType]
    val ObjectTypes: ArraySeq.type = ArraySeq

    type FieldTypes = ArraySeq[FieldType]
    val FieldTypes: ArraySeq.type = ArraySeq
    final def NoFieldTypes: FieldTypes = ArraySeq.empty
    final def newFieldTypesBuilder(): Builder[FieldType, ArraySeq[FieldType]] = {
        ArraySeq.newBuilder[FieldType]
    }

    type Packages = ArraySeq[String]

    type Classes = ArraySeq[ObjectType]

    type RecordComponents = ArraySeq[RecordComponent]

    final type SourceElementID = Int

    final type Opcode = Int

    /**
     * A program counter identifies an instruction in a code array.
     *
     * A program counter is a value in the range `[0/*UShort.min*/, 65535/*UShort.max*/]`.
     *
     * @note This type alias serves comprehension purposes.
     */
    final type PC = bytecode.PC

    /**
     * A collection of program counters using an IntArraySet as its backing collection.
     *
     * Using PCs is in particular well suited for small(er) collections.
     *
     * @note This type alias serves comprehension purposes.
     */
    final type PCs = IntTrieSet

    final val NoPCs: IntTrieSet = IntTrieSet.empty

    /**
     * Converts a given list of annotations into a Java-like representation.
     */
    def annotationsToJava(
        annotations: Annotations,
        before:      String      = "",
        after:       String      = ""
    ): String = {

        val annotationToJava: Annotation => String = { annotation: Annotation =>
            val s = annotation.toJava
            if (s.length() > 50 && annotation.elementValuePairs.nonEmpty)
                annotation.annotationType.toJava+"(...)"
            else
                s
        }

        if (annotations.nonEmpty) {
            before + annotations.map(annotationToJava).mkString(" ") + after
        } else {
            ""
        }
    }

    /**
     * An upper type bound represents the available type information about a
     * reference value. It is always "just" an upper bound for a concrete type;
     * i.e., we know that the runtime type has to be a subtype (reflexive) of the
     * type identified by the upper bound.
     * Furthermore, an upper bound can identify multiple '''independent''' types. E.g.,
     * a type bound for array objects could be: `java.io.Serializable` and
     * `java.lang.Cloneable`. Here, independent means that no two types of the bound
     * are in a subtype relationship. Hence, an upper bound is always a special set where
     * the values are not equal and are not in an inheritance relation. However,
     * identifying independent types is a class hierarchy's responsibility.
     *
     * In general, an upper bound identifies a single class type and a set of independent
     * interface types that are known to be implemented by the current object. '''Even if
     * the type contains a class type''' it may just be a super class of the concrete type
     * and, hence, just represent an abstraction.
     */
    type UpperTypeBound = UIDSet[ReferenceType]

    /**
     * Creates an (X)HTML5 representation of the given Java type declaration.
     */
    def typeToXHTML(t: Type, abbreviateType: Boolean = true): Node = {
        t match {
            case ot: ObjectType =>
                if (abbreviateType)
                    
                        { ot.simpleName }
                    
                else
                    { ot.toJava }
            case at: ArrayType =>
                
                    { typeToXHTML(at.elementType, abbreviateType) }{ "[]" * at.dimensions }
                
            case bt: BaseType =>
                { bt.toJava }
            case VoidType =>
                void
            case CTIntType =>
                { "" }
        }
    }

    def classAccessFlagsToXHTML(accessFlags: Int): Node = {
        { AccessFlags.toString(accessFlags, AccessFlagsContexts.CLASS) }
    }

    def classAccessFlagsToString(accessFlags: Int): String = {
        AccessFlags.toString(accessFlags, AccessFlagsContexts.CLASS)
    }

    def typeToXHTML(accessFlags: Int, t: Type, abbreviateTypes: Boolean): Node = {

        val signature = typeToXHTML(t, abbreviateTypes)

        
            { classAccessFlagsToXHTML(accessFlags) }
            { signature }
        
    }

    /**
     * Creates an (X)HTML5 representation that resembles Java source code method signature.
     */
    def methodToXHTML(
        name:            String,
        descriptor:      MethodDescriptor,
        abbreviateTypes: Boolean          = true
    ): Node = {

        val parameterTypes =
            if (descriptor.parametersCount == 0)
                List(Text(""))
            else {
                val parameterTypes = descriptor.parameterTypes.map(typeToXHTML(_, abbreviateTypes))
                parameterTypes.tail.foldLeft(List(parameterTypes.head)) { (c, r) =>
                    r :: Text(", ") :: c
                }.reverse
            }

        
            { typeToXHTML(descriptor.returnType, abbreviateTypes) }
            { name }
            ({ parameterTypes })
        
    }

    def methodToXHTML(
        accessFlags:     Int,
        name:            String,
        descriptor:      MethodDescriptor,
        abbreviateTypes: Boolean
    ): Node = {

        val signature = methodToXHTML(name, descriptor, abbreviateTypes)

        
            { methodAccessFlagsToString(accessFlags) }
            { signature }
        
    }

    def methodAccessFlagsToString(accessFlags: Int): String = {
        AccessFlags.toString(accessFlags, AccessFlagsContexts.METHOD)
    }

    /**
     * Calculates the parameter index associated with a method's local variable register index.
     * The index of the first parameter is 0. If the method is not static the *this* reference
     * stored in register index `0` has the parameter index `-1`.
     *
     * @param  isStatic `true` if method is static and, hence, has no implicit parameter for `this`.
     * @return The parameter index for the specified register.
     */
    def registerIndexToParameterIndex(
        isStatic:      Boolean,
        descriptor:    MethodDescriptor,
        registerIndex: Int
    ): Int = {

        var parameterIndex = 0
        val parameterTypes = descriptor.parameterTypes
        var currentIndex = 0
        while (currentIndex < registerIndex) {
            currentIndex += parameterTypes(parameterIndex).computationalType.operandSize
            parameterIndex += 1
        }
        if (isStatic) parameterIndex else parameterIndex - 1
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy