
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