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