de.unisb.cs.st.javaslicer.controlflowanalysis.ControlFlowGraph2.java Source code

Java tutorial

Introduction

Here is the source code for de.unisb.cs.st.javaslicer.controlflowanalysis.ControlFlowGraph2.java

Source

/** License information:
 *    Component: javaslicer-core
 *    Package:   de.unisb.cs.st.javaslicer.controlflowanalysis
 *    Class:     ControlFlowGraph
 *    Filename:  javaslicer-core/src/main/java/de/unisb/cs/st/javaslicer/controlflowanalysis/ControlFlowGraph.java
 *
 * This file is part of the JavaSlicer tool, developed by Clemens Hammacher at Saarland University.
 * See http://www.st.cs.uni-saarland.de/javaslicer/ for more information.
 *
 * JavaSlicer is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * JavaSlicer 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with JavaSlicer. If not, see <http://www.gnu.org/licenses/>.
 */
package de.unisb.cs.st.javaslicer.controlflowanalysis;

import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Queue;

import org.objectweb.asm.Opcodes;

import de.hammacher.util.UniqueQueue;
//import de.hammacher.util.graph.Graph;
import de.unisb.cs.st.javaslicer.common.classRepresentation.Instruction;
import de.unisb.cs.st.javaslicer.common.classRepresentation.InstructionType;
import de.unisb.cs.st.javaslicer.common.classRepresentation.ReadMethod;
import de.unisb.cs.st.javaslicer.common.classRepresentation.TryCatchBlock;
import de.unisb.cs.st.javaslicer.common.classRepresentation.instructions.AbstractInstruction;
import de.unisb.cs.st.javaslicer.common.classRepresentation.instructions.JumpInstruction2;
import de.unisb.cs.st.javaslicer.common.classRepresentation.instructions.LabelMarker;
import de.unisb.cs.st.javaslicer.common.classRepresentation.instructions.LookupSwitchInstruction2;
import de.unisb.cs.st.javaslicer.common.classRepresentation.instructions.TableSwitchInstruction2;
import de.unisb.cs.st.javaslicer.common.classRepresentation.instructions.VarInstruction;

/**
 * A representation of the <b>control flow graph (CFG)</b> for one method.
 *
 * @author Clemens Hammacher
 */
public class ControlFlowGraph2 implements Graph<ControlFlowGraph2.InstrNode> {

    public static class InstrList extends AbstractList<InstrNode> {

        private final InstrNode[] nodes;
        // ?nodes??
        private final int[] nonNullPositions;

        public InstrList(InstrNode[] nodes, int[] nonNullPositions) {
            this.nodes = nodes;
            this.nonNullPositions = nonNullPositions;
        }

        @Override
        public InstrNode get(int index) {
            if (index < 0 || index >= this.nonNullPositions.length)
                throw new IndexOutOfBoundsException();
            return this.nodes[this.nonNullPositions[index]];
        }

        @Override
        public int size() {
            return this.nonNullPositions.length;
        }

    }

    /**
     * Representation of one node in the CFG.
     *
     * @author Clemens Hammacher
     */
    public static interface InstrNode extends Graph.Node<InstrNode> {

        /**
         * Returns the number of outgoing edges from this node.
         * @return the out degree of the node
         */
        int getOutDegree();

        /**
         * Returns the number of incoming edges of this node.
         * @return the in degree of the node
         */
        int getInDegree();

        // concretisation:
        @Override
        Collection<InstrNode> getSuccessors();

        Collection<InstrNode> getPredecessors();

        Instruction getInstruction();

        /**
         * For internal use only
         */
        void addSuccessor(InstrNode successor);

        /**
         * For internal use only
         */
        void addPredecessor(InstrNode predecessor);

        ControlFlowGraph2 getGraph();

    }

    /**
     * Basic implementation of the interface {@link InstrNode}.
     *
     * @author Clemens Hammacher
     */
    //?CFG????
    // ???
    public static class AbstractInstrNode implements InstrNode {

        private final List<InstrNode> successors = new ArrayList<InstrNode>(0);
        private final List<InstrNode> predecessors = new ArrayList<InstrNode>(0);
        private final Instruction instruction;
        private final ControlFlowGraph2 cfg;

        public AbstractInstrNode(ControlFlowGraph2 cfg, Instruction instr) {
            if (cfg == null || instr == null)
                throw new NullPointerException();
            this.cfg = cfg;
            this.instruction = instr;
        }

        @Override
        public Instruction getInstruction() {
            return this.instruction;
        }

        @Override
        public Collection<InstrNode> getSuccessors() {
            return this.successors;
        }

        @Override
        public Collection<InstrNode> getPredecessors() {
            return this.predecessors;
        }

        @Override
        public int getOutDegree() {
            return this.successors.size();
        }

        @Override
        public int getInDegree() {
            return this.predecessors.size();
        }

        @Override
        public void addSuccessor(InstrNode successor) {
            this.successors.add(successor);
        }

        @Override
        public void addPredecessor(InstrNode predecessor) {
            this.predecessors.add(predecessor);
        }

        @Override
        public int hashCode() {
            return this.instruction.hashCode();
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            AbstractInstrNode other = (AbstractInstrNode) obj;
            if (!this.instruction.equals(other.instruction))
                return false;
            return true;
        }

        @Override
        public String toString() {
            return this.instruction.toString();
        }

        @Override
        public ControlFlowGraph2 getGraph() {
            return this.cfg;
        }

        @Override
        public String getLabel() {
            return toString();
        }

    }

    public interface NodeFactory {

        InstrNode createNode(ControlFlowGraph2 cfg, Instruction instruction);

    }

    public static class AbstractNodeFactory implements NodeFactory {

        @Override
        public InstrNode createNode(ControlFlowGraph2 cfg, Instruction instr) {
            return new AbstractInstrNode(cfg, instr);
        }

    }

    public ReadMethod method;
    public final InstrNode[] instructionNodes;
    private int[] nonNullPositions;

    /**
     * Computes the <b>control flow graph</b> for one method, using the usual
     * {@link AbstractNodeFactory}.
     *
     * @param method the method for which the CFG is computed
     */
    // CFG
    public ControlFlowGraph2(ReadMethod method) {
        this(method, new AbstractNodeFactory());
    }

    /**
     * Computes the <b>control flow graph</b> for one method.
     *
     * @param method the method for which the CFG is computed
     * @param nodeFactory the factory that creates the nodes of the CFG
     */
    // ??trycatch ?
    //? ?Labels  Gotos ?
    public ControlFlowGraph2(ReadMethod method, NodeFactory nodeFactory) {
        this(method, nodeFactory, false, false);
    }

    /**
     * Computes the <b>control flow graph</b> for one method.
     *
     * @param method the method for which the CFG is computed
     * @param nodeFactory the factory that creates the nodes of the CFG
     * @param addTryCatchEdges controls whether an edge should be inserted from each
     *                         instruction within a try block to the first instruction
     *                         in the catch block
     * @param excludeLabels if <code>true</code>, all Labels and goto instruction are excluded from the
     *                      CFG
     */
    public ControlFlowGraph2(ReadMethod method, NodeFactory nodeFactory, boolean addTryCatchEdges,
            boolean excludeLabels) {
        this.method = method;
        // CFG Method ?
        this.instructionNodes = new InstrNode[method.getInstructionNumberEnd()
                - method.getInstructionNumberStart()];
        for (Instruction instr : method.getInstructions()) {
            // ???????????
            getInstrNode(instr, nodeFactory, excludeLabels); // getInstrNode ??????
        }
        // now add the edges from try blocks to catch/finally blocks
        if (addTryCatchEdges) {
            for (TryCatchBlock tcb : method.getTryCatchBlocks()) {
                LabelMarker handler = tcb.getHandler();
                Instruction nonLabel = excludeLabels ? followLabelsAndGotos(handler) : handler;
                assert nonLabel != null;
                InstrNode tcbHandler = getNode(nonLabel);
                for (Instruction inst = tcb.getStart(); inst != null
                        && inst != tcb.getEnd(); inst = inst.getNext()) {
                    InstrNode instrNode = getNode(inst);
                    instrNode.addSuccessor(tcbHandler);
                    tcbHandler.addPredecessor(instrNode);
                }
            }
        }
    }

    /**
     * Returns the root of this CFG, which is just the Node corresponding to the
     * first instruction of this CFG's method, or null if the method contains no
     * instructions.
     *
     * If the CFG was created with <b>excludeLabels</b> and the first instruction is a label,
     * then the node for the first non-label instruction is returned.
     */
    public InstrNode getRootNode() {
        // search for the first non-label node
        int idx = 0;
        if (idx < this.instructionNodes.length) {
            InstrNode instrNode = this.instructionNodes[idx];
            while (instrNode == null && idx < this.instructionNodes.length) {
                instrNode = this.instructionNodes[idx++];
            }
            return instrNode;
        }
        return null;
    }

    /**
     * Returns the method on which this CFG was built.
     *
     * @return the method on which this CFG was built.
     */
    public ReadMethod getMethod() {
        return this.method;
    }

    /**
     * Return the node of the CFG associated to the given {@link Instruction}.
     * If the instruction is not contained in the method that this CFG corresponds
     * to, then <code>null</code> is returned.
     *
     * If the CFG was created with <b>excludeLabels</b> and the given instruction is a label,
     * then the node for the next non-label instruction is returned.
     *
     * @param instr the {@link Instruction} for which the node is requested
     * @return the node corresponding to the given {@link Instruction}, or
     *         <code>null</code> if the instruction is not contained in the method of this CFG
     */
    public InstrNode getNode(Instruction instr) {
        int idx = instr.getIndex() - this.method.getInstructionNumberStart();
        if (idx >= 0 && idx < this.instructionNodes.length) {
            InstrNode instrNode = this.instructionNodes[idx];
            while (instrNode == null && idx < this.instructionNodes.length) {
                assert instr.getType() == InstructionType.LABEL;
                instrNode = this.instructionNodes[idx++];
            }
            return instrNode;
        }
        return null;
    }

    private Instruction followLabelsAndGotos(Instruction instr) {
        Instruction nonLabel = instr;
        while (nonLabel != null) {
            if (nonLabel.getType() == InstructionType.LABEL) {
                nonLabel = nonLabel.getNext();
            } else if (nonLabel.getOpcode() == Opcodes.GOTO) {
                nonLabel = ((JumpInstruction2) nonLabel).getLabel().getNext();
            } else
                break;
        }
        return nonLabel;
    }

    protected InstrNode getInstrNode(Instruction instruction, NodeFactory nodeFactory, boolean excludeLabels) {
        int idx = instruction.getIndex() - this.method.getInstructionNumberStart();
        // instructionNodes CFGidx???
        InstrNode node = this.instructionNodes[idx];
        // LabelGoto CFG??
        if (node != null || (excludeLabels
                && (instruction.getType() == InstructionType.LABEL || instruction.getOpcode() == Opcodes.GOTO)))
            return node;

        InstrNode newNode = nodeFactory.createNode(this, instruction);
        this.instructionNodes[idx] = newNode;
        // getSuccessor(instruction) ??
        for (Instruction succ : getSuccessors(instruction)) {
            Instruction nonLabel = excludeLabels ? followLabelsAndGotos(succ) : succ;
            if (nonLabel == null)
                continue;
            InstrNode succNode = getInstrNode(nonLabel, nodeFactory, excludeLabels);
            // ???
            newNode.addSuccessor(succNode);
            succNode.addPredecessor(newNode);
        }
        return newNode;
    }

    // ????
    public Collection<Instruction> getSuccessors(Instruction instruction) {
        int opcode = instruction.getOpcode();
        // index??? getnextindex+1?
        Instruction nextInstruction = instruction.getNext();
        switch (instruction.getType()) {
        case JUMP:
            // GOTO and JSR are not conditional
            // ????
            if (opcode == Opcodes.GOTO || opcode == Opcodes.JSR) {
                List<AbstractInstruction> instrs = this.method.instructions;
                Iterator<AbstractInstruction> iter = instrs.iterator();
                Instruction temp = null;
                while (iter.hasNext()) {
                    temp = iter.next();
                    if (temp.getIndex() == ((JumpInstruction2) instruction).getTarget())
                        break;
                }
                return Collections.singleton(temp);
            }
            assert nextInstruction != null;
            // ?nextInstruction, ?label!
            List<AbstractInstruction> instrs = this.method.instructions;
            Iterator<AbstractInstruction> iter = instrs.iterator();
            Instruction temp = null;
            while (iter.hasNext()) {
                temp = iter.next();
                if (temp.getIndex() == ((JumpInstruction2) instruction).getTarget())
                    break;
            }
            return Arrays.asList(temp, nextInstruction);
        // switch 
        case LOOKUPSWITCH: {
            LookupSwitchInstruction2 lsi = (LookupSwitchInstruction2) instruction;
            List<AbstractInstruction> instrs4 = this.method.instructions;
            List<AbstractInstruction> instrs5 = this.method.instructions;
            Iterator<AbstractInstruction> iter4 = instrs4.iterator();
            Instruction temp2 = null;
            Instruction[] successors = new AbstractInstruction[lsi.getHandlers().size() + 1];
            // find the default
            while (iter4.hasNext()) {
                temp2 = iter4.next();
                if (temp2.getIndex() == lsi.getDefaultHandler()) {
                    successors[0] = temp2;
                    break;
                }
            }
            // find the handlers
            int index = 0;
            for (Integer lm : lsi.getHandlers().values()) {
                Iterator<AbstractInstruction> iter5 = instrs5.iterator();
                while (iter5.hasNext()) {
                    temp2 = iter5.next();
                    if (temp2.getIndex() == lm) {
                        successors[index + 1] = temp2;
                        index++;
                        break;
                    }
                }
            }
            return Arrays.asList(successors);
        }
        // switch ?
        case TABLESWITCH: {
            TableSwitchInstruction2 tsi = (TableSwitchInstruction2) instruction;
            List<AbstractInstruction> instrs2 = this.method.instructions;
            List<AbstractInstruction> instrs3 = this.method.instructions;
            Iterator<AbstractInstruction> iter2 = instrs2.iterator();
            //Iterator<AbstractInstruction> iter3=instrs3.iterator();
            Instruction temp2 = null;
            Instruction[] successors = new AbstractInstruction[tsi.getHandlers().length + 1];
            // get the default target
            while (iter2.hasNext()) {
                temp2 = iter2.next();
                if (tsi.getDefaultHandler() == temp2.getIndex()) {
                    successors[0] = temp2;
                    break;
                }
            }
            // get the target from min to max! 
            for (int i = 0; i < tsi.getHandlers().length; i++) {
                Iterator<AbstractInstruction> iter3 = instrs3.iterator();
                while (iter3.hasNext()) {
                    temp2 = iter3.next();
                    if (temp2.getIndex() == (tsi.getHandlers()[i])) {
                        successors[i + 1] = temp2;
                        break;
                    }
                }
            }
            return Arrays.asList(successors);
            /*
             TableSwitchInstruction tsi = (TableSwitchInstruction) instruction;
             Instruction[] successors = new AbstractInstruction[tsi.getHandlers().length+1];
             successors[0] = tsi.getDefaultHandler();
             System.arraycopy(tsi.getHandlers(), 0, successors, 1, tsi.getHandlers().length);
             return Arrays.asList(successors);*/
        }
        // ???return??nextInstruction!
        case SIMPLE:
            switch (opcode) {
            case Opcodes.IRETURN:
            case Opcodes.LRETURN:
            case Opcodes.FRETURN:
            case Opcodes.DRETURN:
            case Opcodes.ARETURN:
            case Opcodes.RETURN:
                return Collections.emptySet();

            default:
                break;
            }
            break;
        // ??ret 
        case VAR:
            if (opcode == Opcodes.RET) {
                List<JumpInstruction2> callingInstructions = getJsrInstructions((VarInstruction) instruction);
                Instruction[] successors = new AbstractInstruction[callingInstructions.size()];
                int i = 0;
                for (JumpInstruction2 instr : callingInstructions)
                    successors[i++] = instr.getNext();
                return Arrays.asList(successors);
            }
            break;
        // label abnormalTermination?nextInstruction!
        case LABEL:
            if (instruction == instruction.getMethod().getAbnormalTerminationLabel())
                return Collections.emptySet();
            break;
        default:
            break;
        }
        // ?index+1??
        assert nextInstruction != null;
        return Collections.singleton(nextInstruction);
    }

    /**
     * Returns all <code>jsr</code> instructions that may end up in the given <code>ret</code> instructions.
     */
    private List<JumpInstruction2> getJsrInstructions(VarInstruction retInstruction) {
        assert retInstruction.getOpcode() == Opcodes.RET;
        List<JumpInstruction2> list = new ArrayList<JumpInstruction2>();
        for (AbstractInstruction instr : retInstruction.getMethod().getInstructions()) {
            if (instr.getOpcode() == Opcodes.JSR) {
                Queue<Instruction> queue = new UniqueQueue<Instruction>();
                queue.add(((JumpInstruction2) instr).getLabel());
                while (!queue.isEmpty()) {
                    Instruction instr2 = queue.poll();
                    if (instr2.getOpcode() == Opcodes.RET) {
                        if (instr2 == retInstruction) {
                            list.add((JumpInstruction2) instr);
                        }
                        break;
                    }
                    queue.addAll(getSuccessors(instr));
                }
            }
        }

        return list;
    }

    @Override
    public List<InstrNode> getNodes() {
        if (this.nonNullPositions == null) {
            int numNonNull = 0;
            int[] newNonNullPositions = new int[this.instructionNodes.length];
            for (int i = 0; i < this.instructionNodes.length; ++i) {
                if (this.instructionNodes[i] != null) {
                    newNonNullPositions[numNonNull++] = i;
                }
            }
            this.nonNullPositions = new int[numNonNull];
            System.arraycopy(newNonNullPositions, 0, this.nonNullPositions, 0, numNonNull);

        }
        return new InstrList(this.instructionNodes, this.nonNullPositions);
    }

}