package jadx.core.dex.nodes;
import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.attributes.AType;
import jadx.core.dex.attributes.AttrNode;
import jadx.core.dex.attributes.nodes.IgnoreEdgeAttr;
import jadx.core.dex.attributes.nodes.LoopInfo;
import jadx.core.utils.BlockUtils;
import jadx.core.utils.EmptyBitSet;
import jadx.core.utils.InsnUtils;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
public class BlockNode extends AttrNode implements IBlock {
private int id;
private final int startOffset;
private final List<InsnNode> instructions = new ArrayList<InsnNode>(2);
private List<BlockNode> predecessors = new ArrayList<BlockNode>(1);
private List<BlockNode> successors = new ArrayList<BlockNode>(1);
private List<BlockNode> cleanSuccessors;
// all dominators
private BitSet doms = EmptyBitSet.EMPTY;
// dominance frontier
private BitSet domFrontier;
// immediate dominator
private BlockNode idom;
// blocks on which dominates this block
private List<BlockNode> dominatesOn = Collections.emptyList();
public BlockNode(int id, int offset) {
this.id = id;
this.startOffset = offset;
}
public void setId(int id) {
this.id = id;
}
public int getId() {
return id;
}
public List<BlockNode> getPredecessors() {
return predecessors;
}
public List<BlockNode> getSuccessors() {
return successors;
}
public List<BlockNode> getCleanSuccessors() {
return cleanSuccessors;
}
public void updateCleanSuccessors() {
cleanSuccessors = cleanSuccessors(this);
}
public void lock() {
cleanSuccessors = lockList(cleanSuccessors);
successors = lockList(successors);
predecessors = lockList(predecessors);
dominatesOn = lockList(dominatesOn);
}
List<BlockNode> lockList(List<BlockNode> list) {
if (list.isEmpty()) {
return Collections.emptyList();
}
return Collections.unmodifiableList(list);
}
/**
* Return all successor which are not exception handler or followed by loop back edge
*/
private static List<BlockNode> cleanSuccessors(BlockNode block) {
List<BlockNode> sucList = block.getSuccessors();
if (sucList.isEmpty()) {
return sucList;
}
List<BlockNode> toRemove = new LinkedList<BlockNode>();
for (BlockNode b : sucList) {
if (BlockUtils.isBlockMustBeCleared(b)) {
toRemove.add(b);
}
}
if (block.contains(AFlag.LOOP_END)) {
List<LoopInfo> loops = block.getAll(AType.LOOP);
for (LoopInfo loop : loops) {
toRemove.add(loop.getStart());
}
}
IgnoreEdgeAttr ignoreEdgeAttr = block.get(AType.IGNORE_EDGE);
if (ignoreEdgeAttr != null) {
toRemove.addAll(ignoreEdgeAttr.getBlocks());
}
if (toRemove.isEmpty()) {
return sucList;
}
List<BlockNode> result = new ArrayList<BlockNode>(sucList);
result.removeAll(toRemove);
return result;
}
@Override
public List<InsnNode> getInstructions() {
return instructions;
}
public int getStartOffset() {
return startOffset;
}
/**
* Check if 'block' dominated on this node
*/
public boolean isDominator(BlockNode block) {
return doms.get(block.getId());
}
/**
* Dominators of this node (exclude itself)
*/
public BitSet getDoms() {
return doms;
}
public void setDoms(BitSet doms) {
this.doms = doms;
}
public BitSet getDomFrontier() {
return domFrontier;
}
public void setDomFrontier(BitSet domFrontier) {
this.domFrontier = domFrontier;
}
/**
* Immediate dominator
*/
public BlockNode getIDom() {
return idom;
}
public void setIDom(BlockNode idom) {
this.idom = idom;
}
public List<BlockNode> getDominatesOn() {
return dominatesOn;
}
public void addDominatesOn(BlockNode block) {
if (dominatesOn.isEmpty()) {
dominatesOn = new LinkedList<BlockNode>();
}
dominatesOn.add(block);
}
public boolean isSynthetic() {
return contains(AFlag.SYNTHETIC);
}
public boolean isReturnBlock() {
return contains(AFlag.RETURN);
}
@Override
public int hashCode() {
return startOffset;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof BlockNode)) {
return false;
}
BlockNode other = (BlockNode) obj;
return id == other.id && startOffset == other.startOffset;
}
@Override
public String baseString() {
return Integer.toString(id);
}
@Override
public String toString() {
return "B:" + id + ":" + InsnUtils.formatOffset(startOffset);
}
}