Coverage Report - org.apache.commons.javaflow.bytecode.transformation.bcel.analyser.Subroutines
 
Classes in this File Line Coverage Branch Coverage Complexity
Subroutines
0%
0/108
0%
0/60
4.857
Subroutines$SubroutineImpl
0%
0/113
0%
0/60
4.857
 
 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 org.apache.bcel.generic.ASTORE;
 20  
 import org.apache.bcel.generic.ATHROW;
 21  
 import org.apache.bcel.generic.BranchInstruction;
 22  
 import org.apache.bcel.generic.CodeExceptionGen;
 23  
 import org.apache.bcel.generic.GotoInstruction;
 24  
 import org.apache.bcel.generic.IndexedInstruction;
 25  
 import org.apache.bcel.generic.Instruction;
 26  
 import org.apache.bcel.generic.InstructionHandle;
 27  
 import org.apache.bcel.generic.JsrInstruction;
 28  
 import org.apache.bcel.generic.LocalVariableInstruction;
 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  
 import java.util.ArrayList;
 37  
 import java.util.HashSet;
 38  
 import java.util.Hashtable;
 39  
 import java.util.Iterator;
 40  
 import java.util.Set;
 41  
 
 42  
 /**
 43  
  * Instances of this class contain information about the subroutines
 44  
  * found in a code array of a method.
 45  
  * This implementation considers the top-level (the instructions
 46  
  * reachable without a JSR or JSR_W starting off from the first
 47  
  * instruction in a code array of a method) being a special subroutine;
 48  
  * see getTopLevel() for that.
 49  
  * Please note that the definition of subroutines in the Java Virtual
 50  
  * Machine Specification, Second Edition is somewhat incomplete.
 51  
  * Therefore, JustIce uses an own, more rigid notion.
 52  
  * Basically, a subroutine is a piece of code that starts at the target
 53  
  * of a JSR of JSR_W instruction and ends at a corresponding RET
 54  
  * instruction. Note also that the control flow of a subroutine
 55  
  * may be complex and non-linear; and that subroutines may be nested.
 56  
  *
 57  
  * WARNING! These classes are a fork of the bcel verifier.
 58  
  *
 59  
  * @version $Id: Subroutines.java 480487 2006-11-29 08:54:42Z bayard $
 60  
  * @author <A HREF="http://www.inf.fu-berlin.de/~ehaase"/>Enver Haase</A>
 61  
  * @see #getTopLevel()
 62  
  */
 63  
 public class Subroutines{
 64  
         /**
 65  
          * This inner class implements the Subroutine interface.
 66  
          */
 67  0
         private class SubroutineImpl implements Subroutine{
 68  
                 /**
 69  
                  * UNSET, a symbol for an uninitialized localVariable
 70  
                  * field. This is used for the "top-level" Subroutine;
 71  
                  * i.e. no subroutine.
 72  
                  */
 73  0
                 private final int UNSET = -1;
 74  
 
 75  
                 /**
 76  
                  * The Local Variable slot where the first
 77  
                  * instruction of this subroutine (an ASTORE) stores
 78  
                  * the JsrInstruction's ReturnAddress in and
 79  
                  * the RET of this subroutine operates on.
 80  
                  */
 81  0
                 private int localVariable = UNSET;
 82  
 
 83  
                 /** The instructions that belong to this subroutine. */
 84  
                 private Set instructions;
 85  
 
 86  
                 /*
 87  
                  * Refer to the Subroutine interface for documentation.
 88  
                  */
 89  
                 public boolean contains(InstructionHandle inst){
 90  0
                         return instructions.contains(inst);
 91  
                 }
 92  
 
 93  
                 /**
 94  
                  * The JSR or JSR_W instructions that define this
 95  
                  * subroutine by targeting it.
 96  
                  */
 97  0
                 private HashSet theJSRs = new HashSet();
 98  
 
 99  
                 /**
 100  
                  * The RET instruction that leaves this subroutine.
 101  
                  */
 102  
                 private InstructionHandle theRET;
 103  
 
 104  
                 /**
 105  
                  * Returns a String representation of this object, merely
 106  
                  * for debugging purposes.
 107  
                  * (Internal) Warning: Verbosity on a problematic subroutine may cause
 108  
                  * stack overflow errors due to recursive subSubs() calls.
 109  
                  * Don't use this, then.
 110  
                  */
 111  
                 public String toString(){
 112  0
                         String ret = "Subroutine: Local variable is '"+localVariable+"', JSRs are '"+theJSRs+"', RET is '"+theRET+"', Instructions: '"+instructions.toString()+"'.";
 113  
 
 114  0
                         ret += " Accessed local variable slots: '";
 115  0
                         int[] alv = getAccessedLocalsIndices();
 116  0
                         for (int i=0; i<alv.length; i++){
 117  0
                                 ret += alv[i]+" ";
 118  
                         }
 119  0
                         ret+="'.";
 120  
 
 121  0
                         ret += " Recursively (via subsub...routines) accessed local variable slots: '";
 122  0
                         alv = getRecursivelyAccessedLocalsIndices();
 123  0
                         for (int i=0; i<alv.length; i++){
 124  0
                                 ret += alv[i]+" ";
 125  
                         }
 126  0
                         ret+="'.";
 127  
 
 128  0
                         return ret;
 129  
                 }
 130  
 
 131  
                 /**
 132  
                  * Sets the leaving RET instruction. Must be invoked after all instructions are added.
 133  
                  * Must not be invoked for top-level 'subroutine'.
 134  
                  */
 135  
                 void setLeavingRET(){
 136  0
                         if (localVariable == UNSET){
 137  0
                                 throw new AssertionViolatedException("setLeavingRET() called for top-level 'subroutine' or forgot to set local variable first.");
 138  
                         }
 139  0
                         Iterator iter = instructions.iterator();
 140  0
                         InstructionHandle ret = null;
 141  0
                         while(iter.hasNext()){
 142  0
                                 InstructionHandle actual = (InstructionHandle) iter.next();
 143  0
                                 if (actual.getInstruction() instanceof RET){
 144  0
                                         if (ret != null){
 145  0
                                                 throw new StructuralCodeConstraintException("Subroutine with more then one RET detected: '"+ret+"' and '"+actual+"'.");
 146  
                                         }
 147  
                                         else{
 148  0
                                                 ret = actual;
 149  
                                         }
 150  
                                 }
 151  0
                         }
 152  0
                         if (ret == null){
 153  0
                                 throw new StructuralCodeConstraintException("Subroutine without a RET detected.");
 154  
                         }
 155  0
                         if (((RET) ret.getInstruction()).getIndex() != localVariable){
 156  0
                                 throw new StructuralCodeConstraintException("Subroutine uses '"+ret+"' which does not match the correct local variable '"+localVariable+"'.");
 157  
                         }
 158  0
                         theRET = ret;
 159  0
                 }
 160  
 
 161  
                 /*
 162  
                  * Refer to the Subroutine interface for documentation.
 163  
                  */
 164  
                 public InstructionHandle[] getEnteringJsrInstructions(){
 165  0
                         if (this == TOPLEVEL) {
 166  0
                                 throw new AssertionViolatedException("getLeavingRET() called on top level pseudo-subroutine.");
 167  
                         }
 168  0
                         InstructionHandle[] jsrs = new InstructionHandle[theJSRs.size()];
 169  0
                         return (InstructionHandle[]) (theJSRs.toArray(jsrs));
 170  
                 }
 171  
 
 172  
                 /**
 173  
                  * Adds a new JSR or JSR_W that has this subroutine as its target.
 174  
                  */
 175  
                 public void addEnteringJsrInstruction(InstructionHandle jsrInst){
 176  0
                         if ( (jsrInst == null) || (! (jsrInst.getInstruction() instanceof JsrInstruction))){
 177  0
                                 throw new AssertionViolatedException("Expecting JsrInstruction InstructionHandle.");
 178  
                         }
 179  0
                         if (localVariable == UNSET){
 180  0
                                 throw new AssertionViolatedException("Set the localVariable first!");
 181  
                         }
 182  
                         else{
 183  
                                 // Something is wrong when an ASTORE is targeted that does not operate on the same local variable than the rest of the
 184  
                                 // JsrInstruction-targets and the RET.
 185  
                                 // (We don't know out leader here so we cannot check if we're really targeted!)
 186  0
                                 if (localVariable != ((ASTORE) (((JsrInstruction) jsrInst.getInstruction()).getTarget().getInstruction())).getIndex()){
 187  0
                                         throw new AssertionViolatedException("Setting a wrong JsrInstruction.");
 188  
                                 }
 189  
                         }
 190  0
                         theJSRs.add(jsrInst);
 191  0
                 }
 192  
 
 193  
                 /*
 194  
                  * Refer to the Subroutine interface for documentation.
 195  
                  */
 196  
                 public InstructionHandle getLeavingRET(){
 197  0
                         if (this == TOPLEVEL) {
 198  0
                                 throw new AssertionViolatedException("getLeavingRET() called on top level pseudo-subroutine.");
 199  
                         }
 200  0
                         return theRET;
 201  
                 }
 202  
 
 203  
                 /*
 204  
                  * Refer to the Subroutine interface for documentation.
 205  
                  */
 206  
                 public InstructionHandle[] getInstructions(){
 207  0
                         InstructionHandle[] ret = new InstructionHandle[instructions.size()];
 208  0
                         return (InstructionHandle[]) instructions.toArray(ret);
 209  
                 }
 210  
 
 211  
                 /* Satisfies Subroutine.getRecursivelyAccessedLocalsIndices(). */
 212  
                 public int[] getRecursivelyAccessedLocalsIndices(){
 213  0
                         HashSet s = new HashSet();
 214  0
                         int[] lvs = getAccessedLocalsIndices();
 215  0
                         for (int j=0; j<lvs.length; j++){
 216  0
                                 s.add(new Integer(lvs[j]));
 217  
                         }
 218  0
                         _getRecursivelyAccessedLocalsIndicesHelper(s, this.subSubs());
 219  0
                         int[] ret = new int[s.size()];
 220  0
                         Iterator i = s.iterator();
 221  0
                         int j=-1;
 222  0
                         while (i.hasNext()){
 223  0
                                 j++;
 224  0
                                 ret[j] = ((Integer) i.next()).intValue();
 225  0
                         }
 226  0
                         return ret;
 227  
                 }
 228  
 
 229  
                 /**
 230  
                  * A recursive helper method for getRecursivelyAccessedLocalsIndices().
 231  
                  * @see #getRecursivelyAccessedLocalsIndices()
 232  
                  */
 233  
                 private void _getRecursivelyAccessedLocalsIndicesHelper(HashSet s, Subroutine[] subs){
 234  0
                         for (int i=0; i<subs.length; i++){
 235  0
                                 int[] lvs = subs[i].getAccessedLocalsIndices();
 236  0
                                 for (int j=0; j<lvs.length; j++){
 237  0
                                         s.add(new Integer(lvs[j]));
 238  
                                 }
 239  0
                                 if(subs[i].subSubs().length != 0){
 240  0
                                         _getRecursivelyAccessedLocalsIndicesHelper(s, subs[i].subSubs());
 241  
                                 }
 242  
                         }
 243  0
                 }
 244  
 
 245  
                 /*
 246  
                  * Satisfies Subroutine.getAccessedLocalIndices().
 247  
                  */
 248  
                 public int[] getAccessedLocalsIndices(){
 249  
                         //TODO: Implement caching.
 250  0
                         HashSet acc = new HashSet();
 251  0
                         if (theRET == null && this != TOPLEVEL){
 252  0
                                 throw new AssertionViolatedException("This subroutine object must be built up completely before calculating accessed locals.");
 253  
                         }
 254  0
                         Iterator i = instructions.iterator();
 255  0
                         while (i.hasNext()){
 256  0
                                 InstructionHandle ih = (InstructionHandle) i.next();
 257  
                                 // RET is not a LocalVariableInstruction in the current version of BCEL.
 258  0
                                 if (ih.getInstruction() instanceof LocalVariableInstruction || ih.getInstruction() instanceof RET){
 259  0
                                         int idx = ((IndexedInstruction) (ih.getInstruction())).getIndex();
 260  0
                                         acc.add(new Integer(idx));
 261  
                                         // LONG? DOUBLE?.
 262  
                                         try{
 263  
                                                 // LocalVariableInstruction instances are typed without the need to look into
 264  
                                                 // the constant pool.
 265  0
                                                 if (ih.getInstruction() instanceof LocalVariableInstruction){
 266  0
                                                         int s = ((LocalVariableInstruction) ih.getInstruction()).getType(null).getSize();
 267  0
                                                         if (s==2) acc.add(new Integer(idx+1));
 268  
                                                 }
 269  
                                         }
 270  0
                                         catch(RuntimeException re){
 271  0
                                                 throw new AssertionViolatedException("Oops. BCEL did not like NULL as a ConstantPoolGen object.");
 272  0
                                         }
 273  
                                 }
 274  0
                         }
 275  
 
 276  0
                         int[] ret = new int[acc.size()];
 277  0
                         i = acc.iterator();
 278  0
                         int j=-1;
 279  0
                         while (i.hasNext()){
 280  0
                                 j++;
 281  0
                                 ret[j] = ((Integer) i.next()).intValue();
 282  0
                         }
 283  0
                         return ret;
 284  
                 }
 285  
 
 286  
                 /*
 287  
                  * Satisfies Subroutine.subSubs().
 288  
                  */
 289  
                 public Subroutine[] subSubs(){
 290  0
                         HashSet h = new HashSet();
 291  
 
 292  0
                         Iterator i = instructions.iterator();
 293  0
                         while (i.hasNext()){
 294  0
                                 Instruction inst = ((InstructionHandle) i.next()).getInstruction();
 295  0
                                 if (inst instanceof JsrInstruction){
 296  0
                                         InstructionHandle targ = ((JsrInstruction) inst).getTarget();
 297  0
                                         h.add(getSubroutine(targ));
 298  
                                 }
 299  0
                         }
 300  0
                         Subroutine[] ret = new Subroutine[h.size()];
 301  0
                         return (Subroutine[]) h.toArray(ret);
 302  
                 }
 303  
 
 304  
                 /*
 305  
                  * Sets the local variable slot the ASTORE that is targeted
 306  
                  * by the JsrInstructions of this subroutine operates on.
 307  
                  * This subroutine's RET operates on that same local variable
 308  
                  * slot, of course.
 309  
                  */
 310  
                 void setLocalVariable(int i){
 311  0
                         if (localVariable != UNSET){
 312  0
                                 throw new AssertionViolatedException("localVariable set twice.");
 313  
                         }
 314  
                         else{
 315  0
                                 localVariable = i;
 316  
                         }
 317  0
                 }
 318  
 
 319  
                 /**
 320  
                  * The default constructor.
 321  
                  */
 322  0
                 public SubroutineImpl(){
 323  0
                 }
 324  
 
 325  
         void setInstructions(Set _instructions) {
 326  0
             this.instructions = _instructions;
 327  0
         }
 328  
     }// end Inner Class SubrouteImpl
 329  
 
 330  
         /**
 331  
          * The Hashtable containing the subroutines found.
 332  
          * Key: InstructionHandle of the leader of the subroutine.
 333  
          * Elements: SubroutineImpl objects.
 334  
          */
 335  0
         private Hashtable subroutines = new Hashtable();
 336  
 
 337  
         /**
 338  
          * This is referring to a special subroutine, namely the
 339  
          * top level. This is not really a subroutine but we use
 340  
          * it to distinguish between top level instructions and
 341  
          * unreachable instructions.
 342  
          */
 343  
         public final Subroutine TOPLEVEL;
 344  
 
 345  
         /**
 346  
          * Constructor.
 347  
          * @param mg A MethodGen object representing method to
 348  
          * create the Subroutine objects of.
 349  
          */
 350  0
         public Subroutines(MethodGen mg){
 351  
 
 352  0
                 InstructionHandle[] all = mg.getInstructionList().getInstructionHandles();
 353  0
                 CodeExceptionGen[] handlers = mg.getExceptionHandlers();
 354  
 
 355  
                 // Define our "Toplevel" fake subroutine.
 356  0
                 TOPLEVEL = new SubroutineImpl();
 357  
 
 358  
                 // Calculate "real" subroutines.
 359  0
                 HashSet sub_leaders = new HashSet(); // Elements: InstructionHandle
 360  0
                 for (int i=0; i<all.length; i++){
 361  0
                         Instruction inst = all[i].getInstruction();
 362  0
                         if (inst instanceof JsrInstruction){
 363  0
                                 sub_leaders.add(((JsrInstruction) inst).getTarget());
 364  
                         }
 365  
                 }
 366  
 
 367  
                 // Build up the database.
 368  0
                 Iterator iter = sub_leaders.iterator();
 369  0
                 while (iter.hasNext()){
 370  0
                         SubroutineImpl sr = new SubroutineImpl();
 371  0
                         InstructionHandle astore = (InstructionHandle) (iter.next());
 372  0
                         sr.setLocalVariable( ((ASTORE) (astore.getInstruction())).getIndex() );
 373  0
                         subroutines.put(astore, sr);
 374  0
                 }
 375  
 
 376  
                 // Fake it a bit. We want a virtual "TopLevel" subroutine.
 377  0
                 subroutines.put(all[0], TOPLEVEL);
 378  0
                 sub_leaders.add(all[0]);
 379  
 
 380  
                 // Tell the subroutines about their JsrInstructions.
 381  
                 // Note that there cannot be a JSR targeting the top-level
 382  
                 // since "Jsr 0" is disallowed in Pass 3a.
 383  
                 // Instructions shared by a subroutine and the toplevel are
 384  
                 // disallowed and checked below, after the BFS.
 385  0
                 for (int i=0; i<all.length; i++){
 386  0
                         Instruction inst = all[i].getInstruction();
 387  0
                         if (inst instanceof JsrInstruction){
 388  0
                                 InstructionHandle leader = ((JsrInstruction) inst).getTarget();
 389  0
                                 ((SubroutineImpl) getSubroutine(leader)).addEnteringJsrInstruction(all[i]);
 390  
                         }
 391  
                 }
 392  
 
 393  
                 // Now do a closure computation from every subroutine leader to find all the
 394  
                 // instructions that belong to a subroutine.
 395  0
                 HashSet instructions_assigned = new HashSet(); // we don't want to assign an instruction to two or more Subroutine objects.
 396  
 
 397  0
         iter = sub_leaders.iterator();
 398  0
                 while (iter.hasNext()){
 399  
             // set of InstructionHandles reachable from the top
 400  0
             Set closure = new HashSet();
 401  
 
 402  
             // Init Queue. Start with the entry point of closure.
 403  0
             InstructionHandle leader = (InstructionHandle) (iter.next());
 404  0
                         ArrayList Q = new ArrayList();
 405  0
                         Q.add(leader);
 406  
 
 407  0
             while (!Q.isEmpty()){
 408  0
                 while (!Q.isEmpty()){
 409  0
                     InstructionHandle u = (InstructionHandle) Q.remove(Q.size()-1);
 410  0
                     if(closure.add(u)) {
 411  0
                         InstructionHandle[] successors = getSuccessors(u);
 412  0
                         for (int i=0; i<successors.length; i++){
 413  0
                             Q.add(successors[i]);
 414  
                         }
 415  
                     }
 416  0
                 }
 417  
                 // check if exception handlers are reachable from the closure computed thus far.
 418  
                 // conceptually, an exception handler is a successor of each instruction inside
 419  
                 // the protected region, but the computation works faster if we don't implement it
 420  
                 // in a straight-forward way. Instead, we occasionally check if the first instruction
 421  
                 // of the protected region is in the closure, and if so, we know that the exception
 422  
                 // handler should also be in the closure.
 423  
                 //
 424  
                 // "one instruction must be always in at-most one subroutine" JVM requirement
 425  
                 // guarantees that the entire protected region always belongs to the same closure,
 426  
                 // so just checking the start instruction is suffice.
 427  0
                 for( int i=0; i<handlers.length; i++ ) {
 428  0
                     if(closure.contains(handlers[i].getStartPC())) {
 429  0
                         InstructionHandle handlerPC = handlers[i].getHandlerPC();
 430  0
                         if(!closure.contains(handlerPC)) {
 431  0
                             Q.add(handlerPC);
 432  
                         }
 433  
                     }
 434  
                 }
 435  0
             }
 436  
             // DFS ended above.
 437  0
             ((SubroutineImpl) (leader==all[0]?getTopLevel():getSubroutine(leader))).setInstructions(closure);
 438  
 
 439  0
             for (Iterator itr = closure.iterator(); itr.hasNext();) {
 440  0
                 InstructionHandle h = (InstructionHandle) itr.next();
 441  0
                 if(!instructions_assigned.add(h)) {
 442  0
                     throw new StructuralCodeConstraintException("Instruction '"+h+"' is part of more than one subroutine (or of the top level and a subroutine).");
 443  
                 }
 444  0
             }
 445  
 
 446  0
             if (leader != all[0]){// If we don't deal with the top-level 'subroutine'
 447  0
                                 ((SubroutineImpl) getSubroutine(leader)).setLeavingRET();
 448  
                         }
 449  0
                 }
 450  
 
 451  
                 // Now make sure no subroutine is calling a subroutine
 452  
                 // that uses the same local variable for the RET as themselves
 453  
                 // (recursively).
 454  
                 // This includes that subroutines may not call themselves
 455  
                 // recursively, even not through intermediate calls to other
 456  
                 // subroutines.
 457  0
                 noRecursiveCalls(getTopLevel(), new HashSet());
 458  
 
 459  0
         }
 460  
 
 461  
         /**
 462  
          * This (recursive) utility method makes sure that
 463  
          * no subroutine is calling a subroutine
 464  
          * that uses the same local variable for the RET as themselves
 465  
          * (recursively).
 466  
          * This includes that subroutines may not call themselves
 467  
          * recursively, even not through intermediate calls to other
 468  
          * subroutines.
 469  
          *
 470  
          * @throws StructuralCodeConstraintException if the above constraint is not satisfied.
 471  
          */
 472  
         private void noRecursiveCalls(Subroutine sub, HashSet set){
 473  0
                 Subroutine[] subs = sub.subSubs();
 474  
 
 475  0
                 for (int i=0; i<subs.length; i++){
 476  0
                         int index = ((RET) (subs[i].getLeavingRET().getInstruction())).getIndex();
 477  
 
 478  0
                         if (!set.add(new Integer(index))){
 479  
                                 // Don't use toString() here because of possibly infinite recursive subSubs() calls then.
 480  0
                                 SubroutineImpl si = (SubroutineImpl) subs[i];
 481  0
                                 throw new StructuralCodeConstraintException("Subroutine with local variable '"+si.localVariable+"', JSRs '"+si.theJSRs+"', RET '"+si.theRET+"' is called by a subroutine which uses the same local variable index as itself; maybe even a recursive call? JustIce's clean definition of a subroutine forbids both.");
 482  
                         }
 483  
 
 484  0
                         noRecursiveCalls(subs[i], set);
 485  
 
 486  0
                         set.remove(new Integer(index));
 487  
                 }
 488  0
         }
 489  
 
 490  
         /**
 491  
          * Returns the Subroutine object associated with the given
 492  
          * leader (that is, the first instruction of the subroutine).
 493  
          * You must not use this to get the top-level instructions
 494  
          * modeled as a Subroutine object.
 495  
          *
 496  
          * @see #getTopLevel()
 497  
          */
 498  
         public Subroutine getSubroutine(InstructionHandle leader){
 499  0
                 Subroutine ret = (Subroutine) subroutines.get(leader);
 500  
 
 501  0
                 if (ret == null){
 502  0
                         throw new AssertionViolatedException("Subroutine requested for an InstructionHandle that is not a leader of a subroutine.");
 503  
                 }
 504  
 
 505  0
                 if (ret == TOPLEVEL){
 506  0
                         throw new AssertionViolatedException("TOPLEVEL special subroutine requested; use getTopLevel().");
 507  
                 }
 508  
 
 509  0
                 return ret;
 510  
         }
 511  
 
 512  
         /**
 513  
          * Returns the subroutine object associated with the
 514  
          * given instruction. This is a costly operation, you
 515  
          * should consider using getSubroutine(InstructionHandle).
 516  
          * Returns 'null' if the given InstructionHandle lies
 517  
          * in so-called 'dead code', i.e. code that can never
 518  
          * be executed.
 519  
          *
 520  
          * @see #getSubroutine(InstructionHandle)
 521  
          * @see #getTopLevel()
 522  
          */
 523  
         public Subroutine subroutineOf(InstructionHandle any){
 524  0
                 Iterator i = subroutines.values().iterator();
 525  0
                 while (i.hasNext()){
 526  0
                         Subroutine s = (Subroutine) i.next();
 527  0
                         if (s.contains(any)) return s;
 528  0
                 }
 529  0
 System.err.println("DEBUG: Please verify '"+any+"' lies in dead code.");
 530  0
                 return null;
 531  
                 //throw new AssertionViolatedException("No subroutine for InstructionHandle found (DEAD CODE?).");
 532  
         }
 533  
 
 534  
         /**
 535  
          * For easy handling, the piece of code that is <B>not</B> a
 536  
          * subroutine, the top-level, is also modeled as a Subroutine
 537  
          * object.
 538  
          * It is a special Subroutine object where <B>you must not invoke
 539  
          * getEnteringJsrInstructions() or getLeavingRET()</B>.
 540  
          *
 541  
          * @see Subroutine#getEnteringJsrInstructions()
 542  
          * @see Subroutine#getLeavingRET()
 543  
          */
 544  
         public Subroutine getTopLevel(){
 545  0
                 return TOPLEVEL;
 546  
         }
 547  
 
 548  0
     static final InstructionHandle[] empty = new InstructionHandle[0];
 549  
 
 550  
     /**
 551  
          * A utility method that calculates the successors of a given InstructionHandle
 552  
          * <B>in the same subroutine</B>. That means, a RET does not have any successors
 553  
          * as defined here. A JsrInstruction has its physical successor as its successor
 554  
          * (opposed to its target) as defined here.
 555  
          */
 556  
         private static InstructionHandle[] getSuccessors(InstructionHandle instruction){
 557  0
                 Instruction inst = instruction.getInstruction();
 558  
 
 559  0
         if (inst instanceof RET){
 560  0
                         return empty;
 561  
                 }
 562  
 
 563  
                 // Terminates method normally.
 564  0
                 if (inst instanceof ReturnInstruction){
 565  0
                         return empty;
 566  
                 }
 567  
 
 568  
                 // Terminates method abnormally, because JustIce mandates
 569  
                 // subroutines not to be protected by exception handlers.
 570  0
                 if (inst instanceof ATHROW){
 571  0
                         return empty;
 572  
                 }
 573  
 
 574  0
         final InstructionHandle[] single = new InstructionHandle[1];
 575  
 
 576  
         // See method comment.
 577  0
                 if (inst instanceof JsrInstruction){
 578  0
                         single[0] = instruction.getNext();
 579  0
                         return single;
 580  
                 }
 581  
 
 582  0
                 if (inst instanceof GotoInstruction){
 583  0
                         single[0] = ((GotoInstruction) inst).getTarget();
 584  0
                         return single;
 585  
                 }
 586  
 
 587  0
                 if (inst instanceof BranchInstruction){
 588  0
                         if (inst instanceof Select){
 589  
                                 // BCEL's getTargets() returns only the non-default targets,
 590  
                                 // thanks to Eli Tilevich for reporting.
 591  0
                                 InstructionHandle[] matchTargets = ((Select) inst).getTargets();
 592  0
                                 InstructionHandle[] ret = new InstructionHandle[matchTargets.length+1];
 593  0
                                 ret[0] = ((Select) inst).getTarget();
 594  0
                                 System.arraycopy(matchTargets, 0, ret, 1, matchTargets.length);
 595  0
                                 return ret;
 596  
                         }
 597  
                         else{
 598  0
                 final InstructionHandle[] pair = new InstructionHandle[2];
 599  0
                                 pair[0] = instruction.getNext();
 600  0
                                 pair[1] = ((BranchInstruction) inst).getTarget();
 601  0
                                 return pair;
 602  
                         }
 603  
                 }
 604  
 
 605  
                 // default case: Fall through.
 606  0
                 single[0] = instruction.getNext();
 607  0
                 return single;
 608  
         }
 609  
 
 610  
         /**
 611  
          * Returns a String representation of this object; merely for debugging puposes.
 612  
          */
 613  
         public String toString(){
 614  0
                 return "---\n"+subroutines.toString()+"\n---\n";
 615  
         }
 616  
 }