Coverage Report - org.apache.commons.javaflow.bytecode.transformation.bcel.analyser.ControlFlowGraph
 
Classes in this File Line Coverage Branch Coverage Complexity
ControlFlowGraph
0%
0/20
0%
0/8
3.167
ControlFlowGraph$InstructionContextImpl
0%
0/91
0%
0/40
3.167
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one or more
 3  
  * contributor license agreements.  See the NOTICE file distributed with
 4  
  * this work for additional information regarding copyright ownership.
 5  
  * The ASF licenses this file to You under the Apache License, Version 2.0
 6  
  * (the "License"); you may not use this file except in compliance with
 7  
  * the License.  You may obtain a copy of the License at
 8  
  *
 9  
  *      http://www.apache.org/licenses/LICENSE-2.0
 10  
  *
 11  
  * Unless required by applicable law or agreed to in writing, software
 12  
  * distributed under the License is distributed on an "AS IS" BASIS,
 13  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  * See the License for the specific language governing permissions and
 15  
  * limitations under the License.
 16  
  */
 17  
 package org.apache.commons.javaflow.bytecode.transformation.bcel.analyser;
 18  
 
 19  
 import java.util.ArrayList;
 20  
 import java.util.HashMap;
 21  
 import java.util.Hashtable;
 22  
 
 23  
 import org.apache.bcel.generic.ATHROW;
 24  
 import org.apache.bcel.generic.BranchInstruction;
 25  
 import org.apache.bcel.generic.GotoInstruction;
 26  
 import org.apache.bcel.generic.Instruction;
 27  
 import org.apache.bcel.generic.InstructionHandle;
 28  
 import org.apache.bcel.generic.JsrInstruction;
 29  
 import org.apache.bcel.generic.MethodGen;
 30  
 import org.apache.bcel.generic.RET;
 31  
 import org.apache.bcel.generic.ReturnInstruction;
 32  
 import org.apache.bcel.generic.Select;
 33  
 import org.apache.bcel.verifier.exc.AssertionViolatedException;
 34  
 import org.apache.bcel.verifier.exc.StructuralCodeConstraintException;
 35  
 
 36  
 
 37  
 /**
 38  
  * This class represents a control flow graph of a method.
 39  
  * 
 40  
  * WARNING! These classes are a fork of the bcel verifier. 
 41  
  *
 42  
  * @version $Id: ControlFlowGraph.java 480487 2006-11-29 08:54:42Z bayard $
 43  
  * @author <A HREF="http://www.inf.fu-berlin.de/~ehaase"/>Enver Haase</A>
 44  
  */
 45  0
 public class ControlFlowGraph{
 46  
 
 47  
         /**
 48  
          * Objects of this class represent a node in a ControlFlowGraph.
 49  
          * These nodes are instructions, not basic blocks.
 50  
          */
 51  
         private class InstructionContextImpl implements InstructionContext{
 52  
 
 53  
                 /**
 54  
                  * The InstructionHandle this InstructionContext is wrapped around.
 55  
                  */
 56  
                 private InstructionHandle instruction;
 57  
 
 58  
                 /**
 59  
                  * The 'incoming' execution Frames.
 60  
                  */
 61  
                 private HashMap inFrames;        // key: the last-executed JSR
 62  
 
 63  
                 /**
 64  
                  * The 'outgoing' execution Frames.
 65  
                  */
 66  
                 private HashMap outFrames; // key: the last-executed JSR 
 67  
 
 68  
                 /**
 69  
                  * The 'execution predecessors' - a list of {@link InstructionContext}
 70  
                  * of those instances that have been execute()d before in that order.
 71  
                  */
 72  0
                 private ExecutionPath executionPredecessors = null; // Type: InstructionContext
 73  
         
 74  
                 /**
 75  
                  * Creates an InstructionHandleImpl object from an InstructionHandle.
 76  
                  * Creation of one per InstructionHandle suffices. Don't create more.
 77  
                  */
 78  0
                 public InstructionContextImpl(InstructionHandle inst){
 79  0
                         if (inst == null) throw new AssertionViolatedException("Cannot instantiate InstructionContextImpl from NULL.");
 80  
                 
 81  0
                         instruction = inst;
 82  0
                         inFrames = new java.util.HashMap();
 83  0
                         outFrames = new java.util.HashMap();
 84  0
                 }
 85  
 
 86  
                 /**
 87  
                  * Returns the exception handlers of this instruction.
 88  
                  */
 89  
                 public ExceptionHandler[] getExceptionHandlers(){
 90  0
                         return exceptionhandlers.getExceptionHandlers(getInstruction());
 91  
                 }
 92  
 
 93  
                 /**
 94  
                  * Returns a clone of the "outgoing" frame situation with respect to the given ExecutionChain.
 95  
                  */        
 96  
                 public Frame getOutFrame(ExecutionPath execChain){
 97  0
                         executionPredecessors = execChain;
 98  
 
 99  
                         Frame org;
 100  
 
 101  0
                         InstructionContext jsr = lastExecutionJSR();
 102  
 
 103  0
                         org = (Frame) outFrames.get(jsr);
 104  
 
 105  0
                         if (org == null){
 106  0
                                 throw new AssertionViolatedException("outFrame not set! This:\n"+this+"\nExecutionChain: "+getExecutionChain()+"\nOutFrames: '"+outFrames+"'.");
 107  
                         }
 108  0
                         return org.getClone();
 109  
                 }
 110  
 
 111  
     public Frame getInFrame() {
 112  
                   Frame org;
 113  
                         
 114  0
                         InstructionContext jsr = lastExecutionJSR();
 115  
                         
 116  0
                         org = (Frame) inFrames.get(jsr);
 117  
 
 118  0
                         if (org == null){
 119  0
                             throw new AssertionViolatedException("inFrame not set! This:\n"+this+"\nInFrames: '"+inFrames+"'.");
 120  
       }
 121  0
       return org.getClone();
 122  
     }
 123  
 
 124  
                 /**
 125  
                  * "Merges in" (vmspec2, page 146) the "incoming" frame situation;
 126  
                  * executes the instructions symbolically
 127  
                  * and therefore calculates the "outgoing" frame situation.
 128  
                  * Returns: True iff the "incoming" frame situation changed after
 129  
                  * merging with "inFrame".
 130  
                  * The execPreds contain the InstructionContext
 131  
                  * objects executed so far in the correct order. This is just
 132  
                  * one execution path [out of many]. This is needed to correctly
 133  
                  * "merge" in the special case of a RET's successor.
 134  
                  * <B>The InstConstraintVisitor and ExecutionVisitor instances
 135  
                  * must be set up correctly.</B>
 136  
                  * @return true - if and only if the "outgoing" frame situation
 137  
                  * changed from the one before execute()ing.
 138  
                  */
 139  
                 public boolean execute(Frame inFrame, ExecutionPath execPreds, ExecutionVisitor ev){
 140  
 
 141  
             // When merge failed, this is useful to see what are two passes
 142  0
             ExecutionPath oldExecPreds = executionPredecessors;
 143  
 
 144  0
             executionPredecessors = execPreds;
 145  
 
 146  
                         //sanity check
 147  0
                         if ( (lastExecutionJSR() == null) && (subroutines.subroutineOf(getInstruction()) != subroutines.getTopLevel() ) ){
 148  
                                 //throw new AssertionViolatedException("Huh?! Am I '"+this+"' part of a subroutine or not?");
 149  0
                                 return false;
 150  
                         }
 151  0
                         if ( (lastExecutionJSR() != null) && (subroutines.subroutineOf(getInstruction()) == subroutines.getTopLevel() ) ){
 152  
                                 //throw new AssertionViolatedException("Huh?! Am I '"+this+"' part of a subroutine or not?");
 153  0
                                 return false;
 154  
                         }
 155  
 
 156  0
                         Frame inF = (Frame) inFrames.get(lastExecutionJSR());
 157  0
                         if (inF == null){// no incoming frame was set, so set it.
 158  0
                                 inFrames.put(lastExecutionJSR(), inFrame);
 159  0
                                 inF = inFrame;
 160  0
                         }
 161  
                         else{// if there was an "old" inFrame
 162  0
                                 if (inF.equals(inFrame)){ //shortcut: no need to merge equal frames.
 163  0
                                         return false;
 164  
                                 }
 165  0
                                 if (! mergeInFrames(inFrame)){
 166  0
                                         return false;
 167  
                                 }
 168  
                         }
 169  
                         
 170  
                         // Now we're sure the inFrame has changed!
 171  
                         
 172  
                         // new inFrame is already merged in, see above.                
 173  0
                         Frame workingFrame = inF.getClone();
 174  
 
 175  
                         // This executes the Instruction.
 176  
                         // Therefore the workingFrame object is modified.
 177  
 //ExecutionVisitor ev = ExecutionVisitor.getInstance(VerifierFactory.getVerifier(method_gen.getClassName()));
 178  0
                         ev.setFrame(workingFrame);
 179  0
                         getInstruction().accept(ev);
 180  
                         //getInstruction().accept(ExecutionVisitor.withFrame(workingFrame));
 181  0
                         outFrames.put(lastExecutionJSR(), workingFrame);
 182  
 
 183  0
                         return true;        // new inFrame was different from old inFrame so merging them
 184  
                                                                                 // yielded a different this.inFrame.
 185  
                 }
 186  
 
 187  
                 /**
 188  
                  * Returns a simple String representation of this InstructionContext.
 189  
                  */
 190  
                 public String toString(){
 191  
                 //TODO: Put information in the brackets, e.g.
 192  
                 //      Is this an ExceptionHandler? Is this a RET? Is this the start of
 193  
                 //      a subroutine?
 194  0
                         String ret = getInstruction().toString(false)+"\t[InstructionContext]";
 195  0
                         return ret;
 196  
                 }
 197  
 
 198  
                 /**
 199  
                  * Does the actual merging (vmspec2, page 146).
 200  
                  * Returns true IFF this.inFrame was changed in course of merging with inFrame.
 201  
                  */
 202  
                 private boolean mergeInFrames(Frame inFrame){
 203  
                         // TODO: Can be performance-improved.
 204  0
                         Frame inF = (Frame) inFrames.get(lastExecutionJSR());
 205  0
                         OperandStack oldstack = inF.getStack().getClone();
 206  0
                         LocalVariables oldlocals = inF.getLocals().getClone();
 207  
                         try{
 208  0
                                 inF.getStack().merge(inFrame.getStack());
 209  0
                                 inF.getLocals().merge(inFrame.getLocals());
 210  
                         }
 211  0
                         catch (StructuralCodeConstraintException sce){
 212  0
                                 extendMessageWithFlow(sce);
 213  0
                                 throw sce;
 214  0
                         }
 215  0
                         if (        oldstack.equals(inF.getStack()) &&
 216  
                                                 oldlocals.equals(inF.getLocals()) ){
 217  0
                                 return false;
 218  
                         }
 219  
                         else{
 220  0
                                 return true;
 221  
                         }
 222  
                 }
 223  
 
 224  
                 /**
 225  
                  * Returns the control flow execution chain. This is built
 226  
                  * while {@link #execute(Frame, ExecutionPath, ExecutionVisitor) executing} the code represented
 227  
                  * by the surrounding ControlFlowGraph.
 228  
                  */
 229  
                 private String getExecutionChain(){
 230  0
             return executionPredecessors.toString()+"\n"+this.toString();
 231  
                 }
 232  
 
 233  
 
 234  
                 /**
 235  
                  * Extends the StructuralCodeConstraintException ("e") object with an at-the-end-extended message.
 236  
                  * This extended message will then reflect the execution flow needed to get to the constraint
 237  
                  * violation that triggered the throwing of the "e" object.
 238  
                  */
 239  
                 private void extendMessageWithFlow(StructuralCodeConstraintException e){
 240  0
                         String s = "Execution flow:\n";
 241  0
                         e.extendMessage("", s+getExecutionChain());
 242  0
                 }
 243  
 
 244  
                 /*
 245  
                  * Fulfils the contract of InstructionContext.getInstruction().
 246  
                  */
 247  
                 public InstructionHandle getInstruction(){
 248  0
                         return instruction;
 249  
                 }
 250  
 
 251  
                 private InstructionContext lastExecutionJSR(){
 252  0
             return executionPredecessors.lastExecutionJSR();
 253  
                 }
 254  
 
 255  
                 /* Satisfies InstructionContext.getSuccessors(). */
 256  
                 public InstructionContext[] getSuccessors(){
 257  0
                         return contextsOf(_getSuccessors());
 258  
                 }
 259  
 
 260  
                 /**
 261  
                  * A utility method that calculates the successors of a given InstructionHandle
 262  
                  * That means, a RET does have successors as defined here.
 263  
                  * A JsrInstruction has its target as its successor
 264  
                  * (opposed to its physical successor) as defined here.
 265  
                  */
 266  
 // TODO: implement caching!
 267  
                 private InstructionHandle[] _getSuccessors(){
 268  0
                         final InstructionHandle[] empty = new InstructionHandle[0];
 269  0
                         final InstructionHandle[] single = new InstructionHandle[1];
 270  0
                         final InstructionHandle[] pair = new InstructionHandle[2];
 271  
                 
 272  0
                         Instruction inst = getInstruction().getInstruction();
 273  
                 
 274  0
                         if (inst instanceof RET){
 275  0
                                 Subroutine s = subroutines.subroutineOf(getInstruction());
 276  0
                                 if (s==null){ //return empty; // RET in dead code. "empty" would be the correct answer, but we know something about the surrounding project...
 277  0
                                         throw new AssertionViolatedException("Asking for successors of a RET in dead code?!");
 278  
                                 }
 279  
 //TODO: remove
 280  0
 throw new AssertionViolatedException("DID YOU REALLY WANT TO ASK FOR RET'S SUCCS?");
 281  
 /*
 282  
                                 InstructionHandle[] jsrs = s.getEnteringJsrInstructions();
 283  
                                 InstructionHandle[] ret = new InstructionHandle[jsrs.length];
 284  
                                 for (int i=0; i<jsrs.length; i++){
 285  
                                         ret[i] = jsrs[i].getNext();
 286  
                                 }
 287  
                                 return ret;
 288  
 */
 289  
                         }
 290  
                 
 291  
                         // Terminates method normally.
 292  0
                         if (inst instanceof ReturnInstruction){
 293  0
                                 return empty;
 294  
                         }
 295  
                 
 296  
                         // Terminates method abnormally, because JustIce mandates
 297  
                         // subroutines not to be protected by exception handlers.
 298  0
                         if (inst instanceof ATHROW){
 299  0
                                 return empty;
 300  
                         }
 301  
                 
 302  
                         // See method comment.
 303  0
                         if (inst instanceof JsrInstruction){
 304  0
                                 single[0] = ((JsrInstruction) inst).getTarget();
 305  0
                                 return single;
 306  
                         }
 307  
 
 308  0
                         if (inst instanceof GotoInstruction){
 309  0
                                 single[0] = ((GotoInstruction) inst).getTarget();
 310  0
                                 return single;
 311  
                         }
 312  
 
 313  0
                         if (inst instanceof BranchInstruction){
 314  0
                                 if (inst instanceof Select){
 315  
                                         // BCEL's getTargets() returns only the non-default targets,
 316  
                                         // thanks to Eli Tilevich for reporting.
 317  0
                                         InstructionHandle[] matchTargets = ((Select) inst).getTargets();
 318  0
                                         InstructionHandle[] ret = new InstructionHandle[matchTargets.length+1];
 319  0
                                         ret[0] = ((Select) inst).getTarget();
 320  0
                                         System.arraycopy(matchTargets, 0, ret, 1, matchTargets.length);
 321  0
                                         return ret;
 322  
                                 }
 323  
                                 else{
 324  0
                                         pair[0] = getInstruction().getNext();
 325  0
                                         pair[1] = ((BranchInstruction) inst).getTarget();
 326  0
                                         return pair;
 327  
                                 }
 328  
                         }
 329  
 
 330  
                         // default case: Fall through.                
 331  0
                         single[0] = getInstruction().getNext();
 332  0
                         return single;
 333  
                 }
 334  
 
 335  
         } // End Inner InstructionContextImpl Class.
 336  
 
 337  
         /** The Subroutines object for the method whose control flow is represented by this ControlFlowGraph. */
 338  
         private final Subroutines subroutines;
 339  
 
 340  
         /** The ExceptionHandlers object for the method whose control flow is represented by this ControlFlowGraph. */
 341  
         private final ExceptionHandlers exceptionhandlers;
 342  
 
 343  
         /** All InstructionContext instances of this ControlFlowGraph. */
 344  0
         private Hashtable instructionContexts = new Hashtable(); //keys: InstructionHandle, values: InstructionContextImpl
 345  
 
 346  
         /** 
 347  
          * A Control Flow Graph.
 348  
          */
 349  0
         public ControlFlowGraph(MethodGen method_gen){
 350  0
                 subroutines = new Subroutines(method_gen);
 351  0
                 exceptionhandlers = new ExceptionHandlers(method_gen);
 352  
 
 353  0
                 InstructionHandle[] instructionhandles = method_gen.getInstructionList().getInstructionHandles();
 354  0
                 for (int i=0; i<instructionhandles.length; i++){
 355  0
                         instructionContexts.put(instructionhandles[i], new InstructionContextImpl(instructionhandles[i]));
 356  
                 }
 357  0
         }
 358  
 
 359  
         /**
 360  
          * Returns the InstructionContext of a given instruction.
 361  
          */
 362  
         public InstructionContext contextOf(InstructionHandle inst){
 363  0
                 InstructionContext ic = (InstructionContext) instructionContexts.get(inst);
 364  0
                 if (ic == null){
 365  0
                         throw new AssertionViolatedException("InstructionContext requested for an InstructionHandle that's not known!");
 366  
                 }
 367  0
                 return ic;
 368  
         }
 369  
 
 370  
         /**
 371  
          * Returns the InstructionContext[] of a given InstructionHandle[],
 372  
          * in a naturally ordered manner.
 373  
          */
 374  
         public InstructionContext[] contextsOf(InstructionHandle[] insts){
 375  0
                 InstructionContext[] ret = new InstructionContext[insts.length];
 376  0
                 for (int i=0; i<insts.length; i++){
 377  0
                         ret[i] = contextOf(insts[i]);
 378  
                 }
 379  0
                 return ret;
 380  
         }
 381  
 
 382  
         /**
 383  
          * Returns an InstructionContext[] with all the InstructionContext instances
 384  
          * for the method whose control flow is represented by this ControlFlowGraph
 385  
          * <B>(NOT ORDERED!)</B>.
 386  
          */
 387  
         public InstructionContext[] getInstructionContexts(){
 388  0
                 InstructionContext[] ret = new InstructionContext[instructionContexts.values().size()];
 389  0
                 return (InstructionContext[]) instructionContexts.values().toArray(ret);
 390  
         }
 391  
 
 392  
         /**
 393  
          * Returns true, if and only if the said instruction is not reachable; that means,
 394  
          * if it is not part of this ControlFlowGraph.
 395  
          */
 396  
         public boolean isDead(InstructionHandle i){
 397  0
         return subroutines.subroutineOf(i) == null;
 398  
         }         
 399  
 }