
org.opalj.br.cfg.CFGNode.scala Maven / Gradle / Ivy
The newest version!
/* BSD 2-Clause License - see OPAL/LICENSE for details. */
package org.opalj.br
package cfg
import scala.collection.mutable
import org.opalj.graphs.Node
/**
* The common super trait of all nodes belonging to a method's control flow graph.
*
* @author Erich Wittenbeck
* @author Michael Eichberg
*/
trait CFGNode extends Node {
def isBasicBlock: Boolean
def asBasicBlock: BasicBlock = throw new ClassCastException();
def isCatchNode: Boolean
def asCatchNode: CatchNode = throw new ClassCastException();
def isExitNode: Boolean
def isAbnormalReturnExitNode: Boolean
def isNormalReturnExitNode: Boolean
def isStartOfSubroutine: Boolean
//
// MANAGING PREDECESSORS
//
private[this] var _predecessors: Set[CFGNode] = Set.empty
def addPredecessor(predecessor: CFGNode): Unit = {
// if (predecessor eq this) throw new IllegalArgumentException()
_predecessors += predecessor
}
def addPredecessors(predecessor: IterableOnce[CFGNode]): Unit = {
// if (predecessor eq this) throw new IllegalArgumentException()
_predecessors ++= predecessor
}
private[cfg] def setPredecessors(predecessors: Set[CFGNode]): Unit = {
_predecessors = predecessors
}
def removePredecessor(predecessor: CFGNode): Unit = {
_predecessors -= predecessor
}
private[cfg] def clearPredecessors(): Unit = {
_predecessors = Set.empty
}
private[cfg] def updatePredecessor(oldBB: CFGNode, newBB: CFGNode): Unit = {
_predecessors = _predecessors - oldBB + newBB
}
def predecessors: Set[CFGNode] = _predecessors
//
// MANAGING SUCCESSORS
//
final override def hasSuccessors: Boolean = _successors.nonEmpty
/**
* Returns `true` if the last instruction of this basic block throws/may throw an exception;
* whether the exception is handled or not is not relevant!
*/
def mayThrowException: Boolean = {
_successors.exists(successor => successor.isCatchNode || successor.isAbnormalReturnExitNode)
}
final override def foreachSuccessor(f: Node => Unit): Unit = _successors foreach f
private[this] var _successors: Set[CFGNode] = Set.empty
def addSuccessor(successor: CFGNode): Unit = {
// if (successor eq this) throw new IllegalArgumentException(s"$this => $successor")
_successors = _successors + successor
}
private[cfg] def setSuccessors(successors: Set[CFGNode]): Unit = {
this._successors = successors
}
private[cfg] def clearSuccessors(): Unit = {
_successors = Set.empty
}
def successors: Set[CFGNode] = _successors
//
// GENERIC QUERY METHODS
//
private[cfg] def reachable(reachable: mutable.Set[CFGNode]): Unit = {
// the following
//_successors.
//filterNot(reachable.contains).
//foreach { d => reachable += d; d.reachable(reachable) }
var remainingSuccessors = this._successors
while (remainingSuccessors.nonEmpty) {
val successor = remainingSuccessors.head
remainingSuccessors = remainingSuccessors.tail
if (reachable.add(successor)) {
for {
nextSuccessor <- successor.successors
if !remainingSuccessors.contains(nextSuccessor)
if !reachable.contains(nextSuccessor)
} {
remainingSuccessors += nextSuccessor
}
}
}
}
/**
* Computes the set of all [[CFGNode]]s that are reachable from this one.
*
* @note The result is not cached.
*/
def reachable(reflexive: Boolean = false): mutable.Set[CFGNode] = {
val reachable = mutable.HashSet.empty[CFGNode]
if (reflexive) reachable += this
this.reachable(reachable)
reachable
}
/**
* Computes the (current) set of all [[BasicBlock]]s that belong to this current subroutine and
* which have no successors.
*
* @note The result is not cached.
* @note This method is primarily (exclusively?) intended to be used to complete a call
* graph containing subroutines.
*/
private[cfg] def subroutineFrontier(code: Code, bbs: Array[BasicBlock]): List[BasicBlock] = {
assert(this.isStartOfSubroutine)
var frontier: List[BasicBlock] = Nil
val seen = mutable.HashSet[CFGNode](this)
var worklist: List[CFGNode] = List(this)
while (worklist.nonEmpty) {
val bb = worklist.head
worklist = worklist.tail
val successors = bb.successors
if (successors.isEmpty) {
if (bb.isBasicBlock) frontier = bb.asBasicBlock :: frontier
} else {
successors.foreach { succBB =>
var nextBB = succBB
// The basic block to which a subroutine returns to cannot be the start
// of a subroutine because a subroutine's code is never reached via a
// normal control flow...(it may however contain a call to a subroutine!)
if (nextBB.isStartOfSubroutine) {
val jsrPC = bb.asBasicBlock.endPC /*the jsr instruction...*/
nextBB = bbs(code.pcOfNextInstruction(jsrPC))
}
if (!seen.contains(nextBB)) {
seen += nextBB
worklist = nextBB :: worklist
}
}
}
}
frontier
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy