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

org.opalj.br.instructions.TABLESWITCH.scala Maven / Gradle / Ivy

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

import org.opalj.collection.IntIterator
import org.opalj.collection.immutable.IntArraySet
import org.opalj.collection.immutable.IntArraySet1

import scala.collection.immutable.ArraySeq

/**
 * Access jump table by index and jump.
 *
 * @author Michael Eichberg
 */
trait TABLESWITCHLike extends CompoundConditionalBranchInstructionLike {

    /** The smallest '''case''' value. `high` ≥ `low` */
    def low: Int

    /** The largest '''case''' value. `high` ≥ `low` */
    def high: Int

    final override def opcode: Opcode = TABLESWITCH.opcode

    final override def mnemonic: String = "tableswitch"

    final override def indexOfNextInstruction(currentPC: PC, modifiedByWide: Boolean): Int = {
        currentPC + 1 + (3 - (currentPC % 4)) + 12 + (high - low + 1) * 4
    }

}

case class TABLESWITCH(
        defaultOffset: Int,
        low:           Int,
        high:          Int,
        jumpOffsets:   ArraySeq[Int]
) extends CompoundConditionalBranchInstruction with TABLESWITCHLike {

    final override def asTABLESWITCH: this.type = this

    final override def indexOfNextInstruction(currentPC: Int)(implicit code: Code): Int = {
        indexOfNextInstruction(currentPC, false)
    }

    def toLabeledInstruction(currentPC: PC): LabeledInstruction = {
        LabeledTABLESWITCH(
            InstructionLabel(currentPC + defaultOffset),
            low,
            high,
            jumpOffsets.map(jumpOffset => InstructionLabel(currentPC + jumpOffset))
        )
    }

    def caseValueOfJumpOffset(jumpOffset: Int): (List[Int], Boolean) = {
        var caseValues = List.empty[Int]
        var i = jumpOffsets.length - 1
        while (i >= 0) {
            if (jumpOffsets(i) == jumpOffset)
                caseValues = high - i :: caseValues
            i -= 1
        }
        (caseValues, jumpOffset == defaultOffset)
    }

    override def caseValues: IntIterator = {
        IntIterator.upTo(low, high).filter(cv => jumpOffsets(cv - low) != defaultOffset)
    }

    final def nextInstructions(
        currentPC:             PC,
        regularSuccessorsOnly: Boolean
    )(
        implicit
        code:           Code,
        classHierarchy: ClassHierarchy = ClassHierarchy.PreInitializedClassHierarchy
    ): List[PC] = {
        val defaultTarget = currentPC + defaultOffset
        var pcs = List(defaultTarget)
        var seen: IntArraySet = IntArraySet1(defaultTarget)
        jumpOffsets foreach { offset =>
            val newPC = currentPC + offset
            if (!seen.contains(newPC)) {
                seen += newPC
                pcs ::= newPC
            }
        }
        pcs
    }

    final override def isIsomorphic(thisPC: PC, otherPC: PC)(implicit code: Code): Boolean = {
        val paddingOffset = (thisPC % 4) - (otherPC % 4)

        code.instructions(otherPC) match {

            case TABLESWITCH(otherDefaultOffset, `low`, `high`, otherJumpOffsets) =>
                (this.defaultOffset + paddingOffset == otherDefaultOffset) && {
                    val tIt = this.jumpOffsets.iterator
                    val oIt = otherJumpOffsets.iterator
                    var doesMatch = true
                    while (doesMatch && tIt.hasNext) {
                        val tOffset = tIt.next()
                        val oOffset = oIt.next()
                        doesMatch = (tOffset + paddingOffset) == oOffset
                    }
                    doesMatch
                }

            case _ => false
        }
    }

    override def toString: String = {
        s"TABLESWITCH($low -> $high; "+
            (low to high).zip(jumpOffsets).map(e => s"${e._1}⤼${e._2}").mkString(",")+
            ";default⤼"+defaultOffset+
            ")"
    }

    override def toString(pc: PC): String = {
        s"TABLESWITCH($low -> $high; "+
            (low to high).zip(jumpOffsets).map { keyOffset =>
                val (key, offset) = keyOffset
                s"$key=${pc + offset}${if (offset >= 0) "↓" else "↑"}"
            }.mkString(", ") +
            s"; ifNoMatch=${defaultOffset + pc}${if (defaultOffset >= 0) "↓" else "↑"})"
    }

}

/**
 * Defines constants and factory methods.
 *
 * @author Malte Limmeroth
 */
object TABLESWITCH extends InstructionMetaInformation {

    final val opcode = 170

    /**
     * Creates [[LabeledTABLESWITCH]] instructions with `Symbols` as the branch targets.
     *
     * @param branchTargets The first target is chosen when the branch value has the value `low`.
     *                      The second target is chosen if the value is `low+1` etc.
     *
     */
    def apply(
        defaultBranchTarget: InstructionLabel,
        low:                 Int,
        high:                Int,
        branchTargets:       ArraySeq[InstructionLabel]
    ): LabeledTABLESWITCH = {
        require(
            branchTargets.size == high - low + 1,
            s"there have to be high-low+1 (${high - low + 1}) targets"
        )
        LabeledTABLESWITCH(defaultBranchTarget, low, high, branchTargets)
    }
}

/**
 * Represents a [[TABLESWITCH]] instruction with unresolved jump targets represented as `Symbols`.
 *
 * @author Malte Limmeroth
 */
case class LabeledTABLESWITCH(
        defaultBranchTarget: InstructionLabel,
        low:                 Int,
        high:                Int,
        jumpTargets:         ArraySeq[InstructionLabel]
) extends LabeledInstruction with TABLESWITCHLike {

    @throws[BranchoffsetOutOfBoundsException]("if the branchoffset is invalid")
    override def resolveJumpTargets(currentPC: PC, pcs: Map[InstructionLabel, PC]): TABLESWITCH = {
        TABLESWITCH(
            asShortBranchoffset(pcs(defaultBranchTarget) - currentPC),
            low,
            high,
            jumpTargets.map(target => asShortBranchoffset(pcs(target) - currentPC))
        )
    }

    override def branchTargets: Iterator[InstructionLabel] = {
        jumpTargets.iterator ++ Iterator(defaultBranchTarget)
    }

    def caseValueOfJumpTarget(jumpTarget: InstructionLabel): (List[Int], Boolean) = {
        var caseValues = List.empty[Int]
        var i = jumpTargets.length - 1
        while (i >= 0) {
            if (jumpTargets(i) == jumpTarget)
                caseValues ::= high - i
            i -= 1
        }
        (caseValues, jumpTarget == defaultBranchTarget)
    }

    override def caseValues: IntIterator = {
        IntIterator.upTo(low, high).filter(cv => jumpTargets(cv - low) != defaultBranchTarget)
    }

    final def isIsomorphic(thisPC: PC, otherPC: PC)(implicit code: Code): Boolean = {
        val other = code.instructions(otherPC)
        (this eq other) || (this == other)
    }

    override def toString(pc: Int): String = {
        (low to high).zip(jumpTargets).map { keyOffset =>
            val (key, target) = keyOffset
            s"$key=$target"
        }.mkString("TABLESWITCH(", ", ", "; ifNoMatch="+defaultBranchTarget+")")
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy