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