/* 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
}
}