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