/*
* ControlFlowNode.java
*
* Copyright (c) 2013 Mike Strobel
*
* This source code is based on Mono.Cecil from Jb Evain, Copyright (c) Jb Evain;
* and ILSpy/ICSharpCode from SharpDevelop, Copyright (c) AlphaSierraPapa.
*
* This source code is subject to terms and conditions of the Apache License, Version 2.0.
* A copy of the license can be found in the License.html file at the root of this distribution.
* By using this source code in any fashion, you are agreeing to be bound by the terms of the
* Apache License, Version 2.0.
*
* You must not remove this notice, or any other, from this software.
*/
package com.strobel.assembler.flowanalysis;
import com.strobel.annotations.NotNull;
import com.strobel.assembler.Collection;
import com.strobel.assembler.ir.ExceptionBlock;
import com.strobel.assembler.ir.ExceptionHandler;
import com.strobel.assembler.ir.Instruction;
import com.strobel.core.StringUtilities;
import com.strobel.core.VerifyArgument;
import com.strobel.decompiler.DecompilerHelpers;
import com.strobel.decompiler.PlainTextOutput;
import com.strobel.functions.Block;
import com.strobel.functions.Function;
import com.strobel.util.ContractUtils;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;
public final class ControlFlowNode {
private final int _blockIndex;
private final int _offset;
private final ControlFlowNodeType _nodeType;
private final ControlFlowNode _endFinallyNode;
private final List<ControlFlowNode> _dominatorTreeChildren = new Collection<>();
private final Set<ControlFlowNode> _dominanceFrontier = new LinkedHashSet<>();
private final List<ControlFlowEdge> _incoming = new Collection<>();
private final List<ControlFlowEdge> _outgoing = new Collection<>();
private boolean _visited;
private ControlFlowNode _copyFrom;
private ControlFlowNode _immediateDominator;
private Instruction _start;
private Instruction _end;
private ExceptionHandler _exceptionHandler;
private Object _userData;
public ControlFlowNode(final int blockIndex, final int offset, final ControlFlowNodeType nodeType) {
_blockIndex = blockIndex;
_offset = offset;
_nodeType = VerifyArgument.notNull(nodeType, "nodeType");
_endFinallyNode = null;
_start = null;
_end = null;
}
public ControlFlowNode(final int blockIndex, final Instruction start, final Instruction end) {
_blockIndex = blockIndex;
_start = VerifyArgument.notNull(start, "start");
_end = VerifyArgument.notNull(end, "end");
_offset = start.getOffset();
_nodeType = ControlFlowNodeType.Normal;
_endFinallyNode = null;
}
public ControlFlowNode(final int blockIndex, final ExceptionHandler exceptionHandler, final ControlFlowNode endFinallyNode) {
_blockIndex = blockIndex;
_exceptionHandler = VerifyArgument.notNull(exceptionHandler, "exceptionHandler");
_nodeType = exceptionHandler.isFinally() ? ControlFlowNodeType.FinallyHandler : ControlFlowNodeType.CatchHandler;
_endFinallyNode = endFinallyNode;
final ExceptionBlock handlerBlock = exceptionHandler.getHandlerBlock();
// _start = handlerBlock.getFirstInstruction();
// _end = handlerBlock.getLastInstruction();
_start = null;
_end = null;
_offset = handlerBlock.getFirstInstruction().getOffset(); //_start.getOffset();
}
public final int getBlockIndex() {
return _blockIndex;
}
public final int getOffset() {
return _offset;
}
public final ControlFlowNodeType getNodeType() {
return _nodeType;
}
public final ControlFlowNode getEndFinallyNode() {
return _endFinallyNode;
}
public final List<ControlFlowNode> getDominatorTreeChildren() {
return _dominatorTreeChildren;
}
public final Set<ControlFlowNode> getDominanceFrontier() {
return _dominanceFrontier;
}
public final List<ControlFlowEdge> getIncoming() {
return _incoming;
}
public final List<ControlFlowEdge> getOutgoing() {
return _outgoing;
}
public final boolean isVisited() {
return _visited;
}
public final boolean isReachable() {
return _immediateDominator != null || _nodeType == ControlFlowNodeType.EntryPoint;
}
public final ControlFlowNode getCopyFrom() {
return _copyFrom;
}
public final ControlFlowNode getImmediateDominator() {
return _immediateDominator;
}
public final Instruction getStart() {
return _start;
}
public final Instruction getEnd() {
return _end;
}
public final ExceptionHandler getExceptionHandler() {
return _exceptionHandler;
}
public final Object getUserData() {
return _userData;
}
public final void setVisited(final boolean visited) {
_visited = visited;
}
public final void setCopyFrom(final ControlFlowNode copyFrom) {
_copyFrom = copyFrom;
}
public final void setImmediateDominator(final ControlFlowNode immediateDominator) {
_immediateDominator = immediateDominator;
}
public final void setStart(final Instruction start) {
_start = start;
}
public final void setEnd(final Instruction end) {
_end = end;
}
public final void setExceptionHandler(final ExceptionHandler exceptionHandler) {
_exceptionHandler = exceptionHandler;
}
public final void setUserData(final Object userData) {
_userData = userData;
}
public final boolean succeeds(final ControlFlowNode other) {
if (other == null) {
return false;
}
for (int i = 0; i < _incoming.size(); i++) {
if (_incoming.get(i).getSource() == other) {
return true;
}
}
return false;
}
public final boolean precedes(final ControlFlowNode other) {
if (other == null) {
return false;
}
for (int i = 0; i < _outgoing.size(); i++) {
if (_outgoing.get(i).getTarget() == other) {
return true;
}
}
return false;
}
public final Iterable<ControlFlowNode> getPredecessors() {
return new Iterable<ControlFlowNode>() {
@NotNull
@Override
public final Iterator<ControlFlowNode> iterator() {
return new PredecessorIterator();
}
};
}
public final Iterable<ControlFlowNode> getSuccessors() {
return new Iterable<ControlFlowNode>() {
@NotNull
@Override
public final Iterator<ControlFlowNode> iterator() {
return new SuccessorIterator();
}
};
}
public final Iterable<Instruction> getInstructions() {
return new Iterable<Instruction>() {
@NotNull
@Override
public final Iterator<Instruction> iterator() {
return new InstructionIterator();
}
};
}
public final void traversePreOrder(
final Function<ControlFlowNode, Iterable<ControlFlowNode>> children,
final Block<ControlFlowNode> visitAction) {
if (_visited) {
return;
}
_visited = true;
visitAction.accept(this);
for (final ControlFlowNode child : children.apply(this)) {
child.traversePreOrder(children, visitAction);
}
}
public final void traversePostOrder(
final Function<ControlFlowNode, Iterable<ControlFlowNode>> children,
final Block<ControlFlowNode> visitAction) {
if (_visited) {
return;
}
_visited = true;
for (final ControlFlowNode child : children.apply(this)) {
child.traversePostOrder(children, visitAction);
}
visitAction.accept(this);
}
public final boolean dominates(final ControlFlowNode node) {
ControlFlowNode current = node;
while (current != null) {
if (current == this) {
return true;
}
current = current._immediateDominator;
}
return false;
}
@Override
public final String toString() {
final PlainTextOutput output = new PlainTextOutput();
switch (_nodeType) {
case Normal: {
output.write("Block #%d", _blockIndex);
if (_start != null) {
output.write(": %d to %d", _start.getOffset(), _end.getEndOffset());
}
break;
}
case CatchHandler:
case FinallyHandler: {
output.write("Block #%d: %s: ", _blockIndex, _nodeType);
DecompilerHelpers.writeExceptionHandler(output, _exceptionHandler);
break;
}
default: {
output.write("Block #%d: %s", _blockIndex, _nodeType);
break;
}
}
output.indent();
if (!_dominanceFrontier.isEmpty()) {
output.writeLine();
output.write("DominanceFrontier: ");
final int[] blockIndexes = new int[_dominanceFrontier.size()];
int i = 0;
for (final ControlFlowNode node : _dominanceFrontier) {
blockIndexes[i++] = node._blockIndex;
}
Arrays.sort(blockIndexes);
output.write(
StringUtilities.join(
", ",
new Iterable<String>() {
@NotNull
@Override
public Iterator<String> iterator() {
return new Iterator<String>() {
private int _position = 0;
@Override
public boolean hasNext() {
return _position < blockIndexes.length;
}
@Override
public String next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
return String.valueOf(blockIndexes[_position++]);
}
@Override
public void remove() {
throw ContractUtils.unreachable();
}
};
}
}
)
);
}
for (final Instruction instruction : getInstructions()) {
output.writeLine();
DecompilerHelpers.writeInstruction(output, instruction);
}
final Object userData = _userData;
if (userData != null) {
output.writeLine();
output.write(String.valueOf(userData));
}
output.unindent();
return output.toString();
}
// <editor-fold defaultstate="collapsed" desc="Iterators">
private final class PredecessorIterator implements Iterator<ControlFlowNode> {
private Iterator<ControlFlowEdge> _innerIterator;
@Override
public final boolean hasNext() {
if (_innerIterator == null) {
_innerIterator = _incoming.listIterator();
}
return _innerIterator.hasNext();
}
@Override
public final ControlFlowNode next() {
if (_innerIterator == null) {
_innerIterator = _incoming.listIterator();
}
return _innerIterator.next().getSource();
}
@Override
public final void remove() {
throw ContractUtils.unsupported();
}
}
private final class SuccessorIterator implements Iterator<ControlFlowNode> {
private Iterator<ControlFlowEdge> _innerIterator;
@Override
public final boolean hasNext() {
if (_innerIterator == null) {
_innerIterator = _outgoing.listIterator();
}
return _innerIterator.hasNext();
}
@Override
public final ControlFlowNode next() {
if (_innerIterator == null) {
_innerIterator = _outgoing.listIterator();
}
return _innerIterator.next().getTarget();
}
@Override
public final void remove() {
throw ContractUtils.unsupported();
}
}
private final class InstructionIterator implements Iterator<Instruction> {
private Instruction _next = _start;
@Override
public final boolean hasNext() {
return _next != null &&
_next.getOffset() <= _end.getOffset();
}
@Override
public final Instruction next() {
final Instruction next = _next;
if (next == null ||
next.getOffset() > _end.getOffset()) {
throw new NoSuchElementException();
}
_next = next.getNext();
return next;
}
@Override
public final void remove() {
throw ContractUtils.unsupported();
}
}
// </editor-fold>
}