
org.opalj.br.instructions.InstructionLike.scala Maven / Gradle / Ivy
The newest version!
/* BSD 2-Clause License - see OPAL/LICENSE for details. */
package org.opalj
package br
package instructions
/**
* Common superclass of all instructions.
*
* In general, we distinguish between a finally assembled [[Instruction]] where all
* jump targets are resolved to concrete branchoffsets and [[LabeledInstruction]]s where the
* jump target(s) are identified using symbols which need to replaced by concrete branchoffsets
* before a class file object can be generated.
*
* @author Michael Eichberg
*/
trait InstructionLike {
/**
* The opcode of the instruction as defined by the JVM specification. The
* opcode is a value in the range [0..255].
*/
def opcode: Int
/**
* The mnemonic of the instruction as defined by the JVM specification.
*/
def mnemonic: String
def isControlTransferInstruction: Boolean = false
def isLoadConstantInstruction: Boolean = false
def isReturnInstruction: Boolean = false
def isMonitorInstruction: Boolean = false
def isAthrow: Boolean = false
def isRET: Boolean = false
/**
* The exceptions that may be thrown by the JVM at runtime if the execution of
* this instruction fails.
* I.e., these are neither exceptions that are explicitly created and then thrown
* by user code nor errors that may arise due to an invalid code base (in particular
* `LinkageError`s). However, `OutOfMemoryError`s are possible.
*
* @note The returned types always precisely describe the thrown exception;
* they are not upper bounds.
*
* All instructions – except of the [[InvocationInstruction]]s and the [[ATHROW$]] instruction –
* will always either succeed, throw a linkage time related exception or throw one of the
* specified exceptions.
*/
def jvmExceptions: List[ObjectType]
/**
* Returns `true` if the evaluation of the instruction may lead to some runtime exception.
* For example, in case of [[INVOKESTATIC]] `jvmExceptions` will return an empty list. However,
* in general the called method may throw an arbitrary exception.
*
* Errors (such as LinkageError) related to invalid projects are not considered.
*/
def mayThrowExceptions: Boolean
/**
* The index of the next instruction in the code array.
*/
def indexOfNextInstruction(currentPC: Int, modifiedByWide: Boolean): Int
/**
* Determines if this instruction is isomorphic to the given instruction.
*
* Two instructions are isomporphic if they access the same operand and register
* values and if the instructions have the same bytecode representation, except
* of (A) (potential) padding bytes and (B) the branch offset of JSR(_W) instructions.
* In the first case the branch offsets are corrected by the number of padding bytes and
* in the second case the absolute addresses are compared (i.e., whether both
* instructions call the same subroutine).
*
* For example, an `aload_0` instruction is only
* isomorphic to another `aload_0` instruction and is not isomorphic to an `aload(0)`
* instruction – though the runtime effect is the same. However, a [[LOOKUPSWITCH]]
* ([[TABLESWITCH]]) instruction is considered isomorphic to another respective
* instruction if the only difference is the number of padding bytes. Furthermore,
* two JSR(_W) instructions are isomorphic if and only if they jump to the same
* subroutine.
*
* @note The number of padding bytes is generally calculated by `(otherPC % 4) -
* (thisPC %4)` (=== `"padding other" - "padding this"`)
* and should be added to the branch offsets of this `(XYZ)switch` instruction
* when the branch targets are compared to the other instructions branchoffsets.
* {{{
* // "padding b" - "padding a"
* // === (3 - (bPC % 4)) - (3 - (aPC % 4))
* // === (aPC % 4) - (bPC %4)
* }}}
* @note this.isIsomorphic(`thisPC`,`thisPC`) is always `true`
*/
def isIsomorphic(thisPC: Int, otherPC: Int)(implicit code: Code): Boolean
/**
* The number of values that are popped from the operand stack. Here, long and
* double values are also counted as one value though they use two stack slots. E.g.,
* [[IADD]] (integer add) and [[LADD]] (long add) both pop two values and push
* one value.
*
* @note In case of some of the [[StackManagementInstruction]] the number of popped values is
* not fixed. In that case the number depends on the concrete layout of the
* operand stack. E.g., the [[POP2]] instruction may just pop one
* ''categeory 2'' value (of type `long` or `double`) or two ''category 1''
* values.
*
* @param ctg A function that returns the computational type category of
* the value on the operand stack with a given value index. E.g., The top value on
* the operand stack has index '0' and may occupy one (for category 1 values)
* or two stack slots (for category 2 values.)
*/
def numberOfPoppedOperands(ctg: Int => ComputationalTypeCategory): Int
/**
* The number of values that are put onto the operand stack. Here, long and
* double values are also counted as one value though they use two stack slots. E.g.,
* [[IADD]] (integer add) and [[LADD]] (long add) both pop two values and push
* one value.
*
* @note In case of some of the [[StackManagementInstruction]] this number is
* not fixed. In that case the number depends on the concrete layout of the
* operand stack. E.g., the [[DUP2]]
* instruction may just duplicate one ''categeory 2'' value (result is 1)
* (of type long or double) or two ''category 1'' values (result is 2).
*
* @param ctg A function that returns the computational type category of
* the value on the operand stack with a given value index. The top value on
* the operand stack has index '0' and may occupy one (for category 1 values)
* or two stack slots (for category 2 values.)
*/
def numberOfPushedOperands(ctg: Int => ComputationalTypeCategory): Int
/**
* The number of stack slots pushed or popped by this instruction.
*
* @note Overall, each [[DUP]] instruction always pushes the same number of stack slots.
* Only the number of values that are processed may depend on the stack layout.
*/
def stackSlotsChange: Int
/**
* Returns `true` if this instruction reads/uses a local variable.
*/
def readsLocal: Boolean
/**
* The index of the local (variable)/register that is read is returned. This
* method is only defined if [[readsLocal]] returns `true`.
*/
@throws[UnsupportedOperationException]("thrown if no local variable is read")
def indexOfReadLocal: Int
/**
* Returns `true` if this instruction writes/updates a local variable.
*/
def writesLocal: Boolean
/**
* The index of the local (variable)/register that is written. This
* method is only defined if [[writesLocal]] returns `true`.
*/
@throws[UnsupportedOperationException]("thrown if no local variable is written")
def indexOfWrittenLocal: Int
/**
* Returns the location – [[Stack]], [[Register]] or [[NoExpression]] – where the value
* computed by this instruction is stored. In this case an instruction is only considered
* to be an expression if it puts a value on the stack or in a register that is the result of
* some kind of computation; i.e., just copying, duplicating or moving a value between the
* stack and the registers is not considered to be an expression.
*
* @note The CHECKCAST instruction is special in the sense that it just inspects the
* top-most value.
*/
def expressionResult: ExpressionResultLocation
/**
* Returns a string representation of this instruction. If this instruction is a
* (conditional) jump instruction, then the PCs of the target instructions are
* given absolute address.
*
* @param currentPC The program counter of this instruction. Used to resolve relative
* jump targets.
*/
def toString(currentPC: Int): String
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy