/* BSD 2-Clause License - see OPAL/LICENSE for details. */
package org.opalj
import scala.collection.immutable.ArraySeq
import scala.collection.mutable.Builder
import scala.xml.Node
import scala.xml.Text
import com.typesafe.config.Config
import com.typesafe.config.ConfigFactory
import org.opalj.bi.AccessFlags
import org.opalj.bi.AccessFlagsContexts
import org.opalj.collection.immutable.BitArraySet
import org.opalj.collection.immutable.IntTrieSet
import org.opalj.collection.immutable.UIDSet
import org.opalj.log.GlobalLogContext
import org.opalj.log.LogContext
import org.opalj.log.OPALLogger.info
import org.opalj.util.elidedAssert
/**
* 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 simple 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 {
elidedAssert(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[ClassType]
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[ClassType]
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 ClassTypes = ArraySeq[ClassType]
val ClassTypes: 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[ClassType]
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 ct: ClassType =>
if (abbreviateType)
{ct.simpleName}
else
{ct.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
}
}