/* * Bytecode Analysis Framework * Copyright (C) 2003-2005 University of Maryland * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package edu.umd.cs.findbugs.ba; import java.util.Locale; import java.util.Set; import javax.annotation.Nonnull; import org.apache.bcel.generic.InstructionHandle; import edu.umd.cs.findbugs.graph.AbstractEdge; /** * An edge of a control flow graph. * * @author David Hovemeyer * @see BasicBlock * @see CFG */ public class Edge extends AbstractEdge<Edge, BasicBlock> implements EdgeTypes, Debug { /* ---------------------------------------------------------------------- * Fields * ---------------------------------------------------------------------- */ private int type; private int flags; /* ---------------------------------------------------------------------- * Public methods * ---------------------------------------------------------------------- */ /** * Constructor. * * @param source source basic block * @param dest destination basic block */ public Edge(BasicBlock source, BasicBlock dest) { super(source, dest); } public int getId() { return getLabel(); } /** * Get the type of edge. */ public int getType() { return type; } /** * Set the type of edge. */ public void setType(int type) { this.type = type; } /** * Get the edge flags. */ public int getFlags() { return flags; } /** * Set the edge flags. */ public void setFlags(int flags) { this.flags = flags; } /** * Return if given edge flag is set. * * @param flag the edge flag * @return true if the flag is set, false otherwise */ public boolean isFlagSet(int flag) { return (this.flags & flag) != 0; } /** * Is the edge an exception edge? */ public boolean isExceptionEdge() { return type == HANDLED_EXCEPTION_EDGE || type == UNHANDLED_EXCEPTION_EDGE; } @Override public boolean equals(Object o) { if (o == null || this.getClass() != o.getClass()) return false; Edge other = (Edge) o; return this.getSource() == other.getSource() && this.getTarget() == other.getTarget() && this.getType() == other.getType(); } @Override public int hashCode() { return 2003 * getSource().getLabel() + getTarget().getLabel(); } /** * Compare with other edge. */ @Override public int compareTo(Edge other) { int cmp = super.compareTo(other); if (cmp != 0) return cmp; return type - other.type; } @Override public String toString() { return formatAsString(false); } public boolean isBackwardInBytecode() { BasicBlock source = getSource(); BasicBlock target = getTarget(); InstructionHandle sourceInstruction = source.getLastInstruction(); InstructionHandle targetInstruction = target.getFirstInstruction(); if (targetInstruction == null || sourceInstruction == null) return false; return targetInstruction.getPosition() < sourceInstruction.getPosition(); } public boolean sourceIsTopOfLoop(@Nonnull Set<Integer> positions) { if (positions == null) throw new NullPointerException("positions can't be null"); BasicBlock source = getSource(); InstructionHandle sourceInstruction = source.getLastInstruction(); if ( sourceInstruction == null) return false; return positions.contains(sourceInstruction.getPosition()); } /** * Return a string representation of the edge. */ public String formatAsString(boolean reverse) { BasicBlock source = getSource(); BasicBlock target = getTarget(); StringBuilder buf = new StringBuilder(); buf.append(reverse ? "REVERSE_EDGE(" : "EDGE("); buf.append(getLabel()); buf.append(") type "); buf.append(edgeTypeToString(type)); buf.append(" from block "); buf.append(reverse ? target.getLabel() : source.getLabel()); buf.append(" to block "); buf.append(reverse ? source.getLabel() : target.getLabel()); InstructionHandle sourceInstruction = source.getLastInstruction(); InstructionHandle targetInstruction = target.getFirstInstruction(); String exInfo = " -> "; if (targetInstruction == null && target.isExceptionThrower()) { targetInstruction = target.getExceptionThrower(); exInfo = " => "; } if (sourceInstruction != null && targetInstruction != null) { buf.append(" [bytecode "); buf.append(sourceInstruction.getPosition()); buf.append(exInfo); buf.append(targetInstruction.getPosition()); buf.append(']'); } else if (source.isExceptionThrower()) { if (type == FALL_THROUGH_EDGE) buf.append(" [successful check]"); else { buf.append(" [failed check for "); buf.append(source.getExceptionThrower().getPosition()); if (targetInstruction != null) { buf.append(" to "); buf.append(targetInstruction.getPosition()); } buf.append(']'); } } return buf.toString(); } /** * Get string representing given edge type. */ public static String edgeTypeToString(int edgeType) { switch (edgeType) { case FALL_THROUGH_EDGE: return "FALL_THROUGH"; case IFCMP_EDGE: return "IFCMP"; case SWITCH_EDGE: return "SWITCH"; case SWITCH_DEFAULT_EDGE: return "SWITCH_DEFAULT"; case JSR_EDGE: return "JSR"; case RET_EDGE: return "RET"; case GOTO_EDGE: return "GOTO"; case RETURN_EDGE: return "RETURN"; case UNHANDLED_EXCEPTION_EDGE: return "UNHANDLED_EXCEPTION"; case HANDLED_EXCEPTION_EDGE: return "HANDLED_EXCEPTION"; case START_EDGE: return "START"; case BACKEDGE_TARGET_EDGE: return "BACKEDGE_TARGET_EDGE"; case BACKEDGE_SOURCE_EDGE: return "BACKEDGE_SOURCE_EDGE"; case EXIT_EDGE: return "EXIT_EDGE"; } throw new IllegalStateException("unknown edge type: " + edgeType); } /** * Get numeric edge type from string representation. */ public static int stringToEdgeType(String s) { s = s.toUpperCase(Locale.ENGLISH); if (s.equals("FALL_THROUGH")) return FALL_THROUGH_EDGE; else if (s.equals("IFCMP")) return IFCMP_EDGE; else if (s.equals("SWITCH")) return SWITCH_EDGE; else if (s.equals("SWITCH_DEFAULT")) return SWITCH_DEFAULT_EDGE; else if (s.equals("JSR")) return JSR_EDGE; else if (s.equals("RET")) return RET_EDGE; else if (s.equals("GOTO")) return GOTO_EDGE; else if (s.equals("RETURN")) return RETURN_EDGE; else if (s.equals("UNHANDLED_EXCEPTION")) return UNHANDLED_EXCEPTION_EDGE; else if (s.equals("HANDLED_EXCEPTION")) return HANDLED_EXCEPTION_EDGE; else if (s.equals("START")) return START_EDGE; else if (s.equals("BACKEDGE_TARGET_EDGE")) return BACKEDGE_TARGET_EDGE; else if (s.equals("BACKEDGE_SOURCE_EDGE")) return BACKEDGE_SOURCE_EDGE; else if (s.equals("EXIT_EDGE")) return EXIT_EDGE; else throw new IllegalArgumentException("Unknown edge type: " + s); } } // vim:ts=4