    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("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}