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

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