001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 *  Unless required by applicable law or agreed to in writing, software
012 *  distributed under the License is distributed on an "AS IS" BASIS,
013 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 *  See the License for the specific language governing permissions and
015 *  limitations under the License.
016 *
017 */
018package org.apache.bcel.verifier.structurals;
019
020import java.util.ArrayList;
021import java.util.HashMap;
022import java.util.HashSet;
023import java.util.List;
024import java.util.Map;
025import java.util.Set;
026
027import org.apache.bcel.generic.ASTORE;
028import org.apache.bcel.generic.ATHROW;
029import org.apache.bcel.generic.BranchInstruction;
030import org.apache.bcel.generic.CodeExceptionGen;
031import org.apache.bcel.generic.GotoInstruction;
032import org.apache.bcel.generic.IndexedInstruction;
033import org.apache.bcel.generic.Instruction;
034import org.apache.bcel.generic.InstructionHandle;
035import org.apache.bcel.generic.JsrInstruction;
036import org.apache.bcel.generic.LocalVariableInstruction;
037import org.apache.bcel.generic.MethodGen;
038import org.apache.bcel.generic.RET;
039import org.apache.bcel.generic.ReturnInstruction;
040import org.apache.bcel.generic.Select;
041import org.apache.bcel.verifier.exc.AssertionViolatedException;
042import org.apache.bcel.verifier.exc.StructuralCodeConstraintException;
043
044/**
045 * Instances of this class contain information about the subroutines
046 * found in a code array of a method.
047 * This implementation considers the top-level (the instructions
048 * reachable without a JSR or JSR_W starting off from the first
049 * instruction in a code array of a method) being a special subroutine;
050 * see getTopLevel() for that.
051 * Please note that the definition of subroutines in the Java Virtual
052 * Machine Specification, Second Edition is somewhat incomplete.
053 * Therefore, JustIce uses an own, more rigid notion.
054 * Basically, a subroutine is a piece of code that starts at the target
055 * of a JSR of JSR_W instruction and ends at a corresponding RET
056 * instruction. Note also that the control flow of a subroutine
057 * may be complex and non-linear; and that subroutines may be nested.
058 * JustIce also mandates subroutines not to be protected by exception
059 * handling code (for the sake of control flow predictability).
060 * To understand JustIce's notion of subroutines, please read
061 *
062 * TODO: refer to the paper.
063 *
064 * @see #getTopLevel()
065 */
066public class Subroutines{
067    /**
068     * This inner class implements the Subroutine interface.
069     */
070    private class SubroutineImpl implements Subroutine{
071        /**
072         * UNSET, a symbol for an uninitialized localVariable
073         * field. This is used for the "top-level" Subroutine;
074         * i.e. no subroutine.
075         */
076        private static final int UNSET = -1;
077
078        /**
079         * The Local Variable slot where the first
080         * instruction of this subroutine (an ASTORE) stores
081         * the JsrInstruction's ReturnAddress in and
082         * the RET of this subroutine operates on.
083         */
084        private int localVariable = UNSET;
085
086        /** The instructions that belong to this subroutine. */
087        private final Set<InstructionHandle> instructions = new HashSet<>(); // Elements: InstructionHandle
088
089        /*
090         * Refer to the Subroutine interface for documentation.
091         */
092        @Override
093        public boolean contains(final InstructionHandle inst) {
094            return instructions.contains(inst);
095        }
096
097        /**
098         * The JSR or JSR_W instructions that define this
099         * 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 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 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("Oops. 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 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 InstructionHandle[] getSuccessors(final InstructionHandle instruction) {
623        final InstructionHandle[] empty = new InstructionHandle[0];
624        final 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 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 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}