001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   https://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.apache.bcel.verifier.structurals;
020
021import java.util.ArrayList;
022import java.util.BitSet;
023import java.util.HashMap;
024import java.util.HashSet;
025import java.util.List;
026import java.util.Map;
027import java.util.Set;
028
029import org.apache.bcel.generic.ASTORE;
030import org.apache.bcel.generic.ATHROW;
031import org.apache.bcel.generic.BranchInstruction;
032import org.apache.bcel.generic.CodeExceptionGen;
033import org.apache.bcel.generic.GotoInstruction;
034import org.apache.bcel.generic.IndexedInstruction;
035import org.apache.bcel.generic.Instruction;
036import org.apache.bcel.generic.InstructionHandle;
037import org.apache.bcel.generic.JsrInstruction;
038import org.apache.bcel.generic.LocalVariableInstruction;
039import org.apache.bcel.generic.MethodGen;
040import org.apache.bcel.generic.RET;
041import org.apache.bcel.generic.ReturnInstruction;
042import org.apache.bcel.generic.Select;
043import org.apache.bcel.verifier.exc.AssertionViolatedException;
044import org.apache.bcel.verifier.exc.StructuralCodeConstraintException;
045
046/**
047 * Instances of this class contain information about the subroutines found in a code array of a method. This
048 * implementation considers the top-level (the instructions 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; see getTopLevel() for that. Please note that the
050 * definition of subroutines in the Java Virtual Machine Specification, Second Edition is somewhat incomplete.
051 * Therefore, JustIce uses an own, more rigid notion. Basically, a subroutine is a piece of code that starts at the
052 * target of a JSR of JSR_W instruction and ends at a corresponding RET instruction. Note also that the control flow of
053 * a subroutine may be complex and non-linear; and that subroutines may be nested. JustIce also mandates subroutines not
054 * to be protected by exception handling code (for the sake of control flow predictability). To understand JustIce's
055 * notion of subroutines, please read
056 *
057 * TODO: refer to the paper.
058 *
059 * @see #getTopLevel()
060 */
061public class Subroutines {
062    // Node coloring constants
063    private enum ColourConstants {
064        WHITE, GRAY, BLACK
065    }
066
067    /**
068     * This inner class implements the Subroutine interface.
069     */
070    private final class SubroutineImpl implements Subroutine {
071
072        /**
073         * UNSET, a symbol for an uninitialized localVariable field. This is used for the "top-level" Subroutine; for example no
074         * subroutine.
075         */
076        private static final int UNSET = -1;
077
078        private final SubroutineImpl[] EMPTY_ARRAY = {};
079
080        /**
081         * The Local Variable slot where the first instruction of this subroutine (an ASTORE) stores the JsrInstruction's
082         * ReturnAddress in and 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         * The JSR or JSR_W instructions that define this subroutine by targeting it.
091         */
092        private final Set<InstructionHandle> theJSRs = new HashSet<>();
093
094        /**
095         * The RET instruction that leaves this subroutine.
096         */
097        private InstructionHandle theRET;
098
099        /**
100         * Constructs a new instance.
101         */
102        SubroutineImpl() {
103            // empty
104        }
105
106        /**
107         * Adds a new JSR or JSR_W that has this subroutine as its target.
108         */
109        public void addEnteringJsrInstruction(final InstructionHandle jsrInst) {
110            if (jsrInst == null || !(jsrInst.getInstruction() instanceof JsrInstruction)) {
111                throw new AssertionViolatedException("Expecting JsrInstruction InstructionHandle.");
112            }
113            if (localVariable == UNSET) {
114                throw new AssertionViolatedException("Set the localVariable first.");
115            }
116            // Something is wrong when an ASTORE is targeted that does not operate on the same local variable than the rest of the
117            // JsrInstruction-targets and the RET.
118            // (We don't know out leader here so we cannot check if we're really targeted!)
119            if (localVariable != ((ASTORE) ((JsrInstruction) jsrInst.getInstruction()).getTarget().getInstruction()).getIndex()) {
120                throw new AssertionViolatedException("Setting a wrong JsrInstruction.");
121            }
122            theJSRs.add(jsrInst);
123        }
124
125        /*
126         * Adds an instruction to this subroutine. All instructions must have been added before invoking setLeavingRET().
127         *
128         * @see #setLeavingRET
129         */
130        void addInstruction(final InstructionHandle ih) {
131            if (theRET != null) {
132                throw new AssertionViolatedException("All instructions must have been added before invoking setLeavingRET().");
133            }
134            instructions.add(ih);
135        }
136
137        /*
138         * Refer to the Subroutine interface for documentation.
139         */
140        @Override
141        public boolean contains(final InstructionHandle inst) {
142            return instructions.contains(inst);
143        }
144
145        /*
146         * Satisfies Subroutine.getAccessedLocalIndices().
147         */
148        @Override
149        public int[] getAccessedLocalsIndices() {
150            // TODO: Implement caching.
151            final Set<Integer> acc = new HashSet<>();
152            if (theRET == null && this != getTopLevel()) {
153                throw new AssertionViolatedException("This subroutine object must be built up completely before calculating accessed locals.");
154            }
155            {
156                for (final InstructionHandle ih : instructions) {
157                    // RET is not a LocalVariableInstruction in the current version of BCEL.
158                    if (ih.getInstruction() instanceof LocalVariableInstruction || ih.getInstruction() instanceof RET) {
159                        final int idx = ((IndexedInstruction) ih.getInstruction()).getIndex();
160                        acc.add(Integer.valueOf(idx));
161                        // LONG? DOUBLE?.
162                        try {
163                            // LocalVariableInstruction instances are typed without the need to look into
164                            // the constant pool.
165                            if (ih.getInstruction() instanceof LocalVariableInstruction) {
166                                final int s = ((LocalVariableInstruction) ih.getInstruction()).getType(null).getSize();
167                                if (s == 2) {
168                                    acc.add(Integer.valueOf(idx + 1));
169                                }
170                            }
171                        } catch (final RuntimeException re) {
172                            throw new AssertionViolatedException("BCEL did not like NULL as a ConstantPoolGen object.", re);
173                        }
174                    }
175                }
176            }
177
178            {
179                final int[] ret = new int[acc.size()];
180                int j = -1;
181                for (final Integer accessedLocal : acc) {
182                    j++;
183                    ret[j] = accessedLocal.intValue();
184                }
185                return ret;
186            }
187        }
188
189        /*
190         * Refer to the Subroutine interface for documentation.
191         */
192        @Override
193        public InstructionHandle[] getEnteringJsrInstructions() {
194            if (this == getTopLevel()) {
195                throw new AssertionViolatedException("getLeavingRET() called on top level pseudo-subroutine.");
196            }
197            return theJSRs.toArray(InstructionHandle.EMPTY_ARRAY);
198        }
199
200        /*
201         * Refer to the Subroutine interface for documentation.
202         */
203        @Override
204        public InstructionHandle[] getInstructions() {
205            return instructions.toArray(InstructionHandle.EMPTY_ARRAY);
206        }
207
208        /*
209         * Refer to the Subroutine interface for documentation.
210         */
211        @Override
212        public InstructionHandle getLeavingRET() {
213            if (this == getTopLevel()) {
214                throw new AssertionViolatedException("getLeavingRET() called on top level pseudo-subroutine.");
215            }
216            return theRET;
217        }
218
219        /* Satisfies Subroutine.getRecursivelyAccessedLocalsIndices(). */
220        @Override
221        public int[] getRecursivelyAccessedLocalsIndices() {
222            final Set<Integer> s = new HashSet<>();
223            final int[] lvs = getAccessedLocalsIndices();
224            for (final int lv : lvs) {
225                s.add(Integer.valueOf(lv));
226            }
227            getRecursivelyAccessedLocalsIndicesHelper(s, subSubs(), new HashSet<>());
228            final int[] ret = new int[s.size()];
229            int j = -1;
230            for (final Integer index : s) {
231                j++;
232                ret[j] = index.intValue();
233            }
234            return ret;
235        }
236
237        /**
238         * Gets the recursively accessed local variable indexes and adds them to the supplied set.
239         * <p>
240         * A recursive helper method for getRecursivelyAccessedLocalsIndices(). Every subroutine is visited at most
241         * once: since the computed set is a plain union, re-exploring an already visited subroutine cannot add
242         * anything, but doing so once per call path made this helper exponential in the depth of the JSR call graph
243         * (and made it recurse forever on a cyclic one).
244         * </p>
245         *
246         * @see #getRecursivelyAccessedLocalsIndices()
247         */
248        private void getRecursivelyAccessedLocalsIndicesHelper(final Set<Integer> set, final Subroutine[] subs, final Set<Subroutine> visited) {
249            for (final Subroutine sub : subs) {
250                if (!visited.add(sub)) {
251                    continue;
252                }
253                final int[] lvs = sub.getAccessedLocalsIndices();
254                for (final int lv : lvs) {
255                    set.add(Integer.valueOf(lv));
256                }
257                if (sub.subSubs().length != 0) {
258                    getRecursivelyAccessedLocalsIndicesHelper(set, sub.subSubs(), visited);
259                }
260            }
261        }
262
263        /**
264         * Sets the leaving RET instruction. Must be invoked after all instructions are added. Must not be invoked for top-level
265         * 'subroutine'.
266         */
267        void setLeavingRET() {
268            if (localVariable == UNSET) {
269                throw new AssertionViolatedException("setLeavingRET() called for top-level 'subroutine' or forgot to set local variable first.");
270            }
271            InstructionHandle ret = null;
272            for (final InstructionHandle actual : instructions) {
273                if (actual.getInstruction() instanceof RET) {
274                    if (ret != null) {
275                        throw new StructuralCodeConstraintException("Subroutine with more than one RET detected: '" + ret + "' and '" + actual + "'.");
276                    }
277                    ret = actual;
278                }
279            }
280            if (ret == null) {
281                throw new StructuralCodeConstraintException("Subroutine without a RET detected.");
282            }
283            if (((RET) ret.getInstruction()).getIndex() != localVariable) {
284                throw new StructuralCodeConstraintException(
285                    "Subroutine uses '" + ret + "' which does not match the correct local variable '" + localVariable + "'.");
286            }
287            theRET = ret;
288        }
289
290        /*
291         * Sets the local variable slot the ASTORE that is targeted by the JsrInstructions of this subroutine operates on. This
292         * subroutine's RET operates on that same local variable slot, of course.
293         */
294        void setLocalVariable(final int i) {
295            if (localVariable != UNSET) {
296                throw new AssertionViolatedException("localVariable set twice.");
297            }
298            localVariable = i;
299        }
300
301        /*
302         * Satisfies Subroutine.subSubs().
303         */
304        @Override
305        public Subroutine[] subSubs() {
306            final Set<Subroutine> h = new HashSet<>();
307
308            for (final InstructionHandle ih : instructions) {
309                final Instruction inst = ih.getInstruction();
310                if (inst instanceof JsrInstruction) {
311                    final InstructionHandle targ = ((JsrInstruction) inst).getTarget();
312                    h.add(getSubroutine(targ));
313                }
314            }
315            return h.toArray(EMPTY_ARRAY);
316        }
317
318        /**
319         * Returns a String representation of this object, merely for debugging purposes. (Internal) Warning: Verbosity on a
320         * problematic subroutine may cause stack overflow errors due to recursive subSubs() calls. Don't use this, then.
321         */
322        @Override
323        public String toString() {
324            final StringBuilder ret = new StringBuilder();
325            ret.append("Subroutine: Local variable is '").append(localVariable);
326            ret.append("', JSRs are '").append(theJSRs);
327            ret.append("', RET is '").append(theRET);
328            ret.append("', Instructions: '").append(instructions).append("'.");
329
330            ret.append(" Accessed local variable slots: '");
331            int[] alv = getAccessedLocalsIndices();
332            for (final int element : alv) {
333                ret.append(element);
334                ret.append(" ");
335            }
336            ret.append("'.");
337
338            ret.append(" Recursively (via subsub...routines) accessed local variable slots: '");
339            alv = getRecursivelyAccessedLocalsIndices();
340            for (final int element : alv) {
341                ret.append(element);
342                ret.append(" ");
343            }
344            ret.append("'.");
345
346            return ret.toString();
347        }
348
349    } // end Inner Class SubrouteImpl
350
351    /**
352     * Gets the successors of a given InstructionHandle <strong>in the same subroutine</strong>. That
353     * means, a RET does not have any successors as defined here. A JsrInstruction has its physical successor as its
354     * successor (opposed to its target) as defined here.
355     */
356    private static InstructionHandle[] getSuccessors(final InstructionHandle instruction) {
357        final InstructionHandle[] single = new InstructionHandle[1];
358
359        final Instruction inst = instruction.getInstruction();
360
361        // Terminates method normally.
362        // Terminates method abnormally, because JustIce mandates
363        // subroutines not to be protected by exception handlers.
364        if (inst instanceof RET || inst instanceof ReturnInstruction || inst instanceof ATHROW) {
365            return InstructionHandle.EMPTY_ARRAY;
366        }
367
368        // See method comment.
369        if (inst instanceof JsrInstruction) {
370            single[0] = instruction.getNext();
371            return single;
372        }
373
374        if (inst instanceof GotoInstruction) {
375            single[0] = ((GotoInstruction) inst).getTarget();
376            return single;
377        }
378
379        if (inst instanceof BranchInstruction) {
380            if (inst instanceof Select) {
381                // BCEL's getTargets() returns only the non-default targets,
382                // thanks to Eli Tilevich for reporting.
383                final InstructionHandle[] matchTargets = ((Select) inst).getTargets();
384                final InstructionHandle[] ret = new InstructionHandle[matchTargets.length + 1];
385                ret[0] = ((Select) inst).getTarget();
386                System.arraycopy(matchTargets, 0, ret, 1, matchTargets.length);
387                return ret;
388            }
389            final InstructionHandle[] pair = new InstructionHandle[2];
390            pair[0] = instruction.getNext();
391            pair[1] = ((BranchInstruction) inst).getTarget();
392            return pair;
393        }
394
395        // default case: Fall through.
396        single[0] = instruction.getNext();
397        return single;
398    }
399
400    private static StructuralCodeConstraintException recursiveCallException(final Subroutine sub2) {
401        // Don't use toString() here because of possibly infinite recursive subSubs() calls then.
402        final SubroutineImpl si = (SubroutineImpl) sub2;
403        return new StructuralCodeConstraintException("Subroutine with local variable '" + si.localVariable + "', JSRs '" + si.theJSRs + "', RET '"
404            + si.theRET + "' is called by a subroutine which uses the same local variable index as itself; maybe even a recursive call?"
405            + " JustIce's clean definition of a subroutine forbids both.");
406    }
407
408    /**
409     * The map containing the subroutines found. Key: InstructionHandle of the leader of the subroutine. Elements:
410     * SubroutineImpl objects.
411     */
412    private final Map<InstructionHandle, Subroutine> subroutines = new HashMap<>();
413
414    /**
415     * This is referring to a special subroutine, namely the top level. This is not really a subroutine but we use it to
416     * distinguish between top level instructions and unreachable instructions.
417     */
418    // CHECKSTYLE:OFF
419    public final Subroutine TOPLEVEL; // TODO can this be made private?
420    // CHECKSTYLE:ON
421
422    /**
423     * Constructs a new instance.
424     *
425     * @param mg A MethodGen object representing method to create the Subroutine objects of. Assumes that JustIce strict
426     *        checks are needed.
427     */
428    public Subroutines(final MethodGen mg) {
429        this(mg, true);
430    }
431
432    /**
433     * Constructs a new instance.
434     *
435     * @param mg A MethodGen object representing method to create the Subroutine objects of.
436     * @param enableJustIceCheck whether to enable additional JustIce checks.
437     * @since 6.0
438     */
439    public Subroutines(final MethodGen mg, final boolean enableJustIceCheck) {
440        final InstructionHandle[] all = mg.getInstructionList().getInstructionHandles();
441        final CodeExceptionGen[] handlers = mg.getExceptionHandlers();
442
443        // Define our "Toplevel" fake subroutine.
444        TOPLEVEL = new SubroutineImpl();
445
446        // Calculate "real" subroutines.
447        final Set<InstructionHandle> subLeaders = new HashSet<>(); // Elements: InstructionHandle
448        for (final InstructionHandle element : all) {
449            final Instruction inst = element.getInstruction();
450            if (inst instanceof JsrInstruction) {
451                subLeaders.add(((JsrInstruction) inst).getTarget());
452            }
453        }
454
455        // Build up the database.
456        for (final InstructionHandle astore : subLeaders) {
457            final SubroutineImpl sr = new SubroutineImpl();
458            sr.setLocalVariable(((ASTORE) astore.getInstruction()).getIndex());
459            subroutines.put(astore, sr);
460        }
461
462        // Fake it a bit. We want a virtual "TopLevel" subroutine.
463        subroutines.put(all[0], TOPLEVEL);
464        subLeaders.add(all[0]);
465
466        // Tell the subroutines about their JsrInstructions.
467        // Note that there cannot be a JSR targeting the top-level
468        // since "Jsr 0" is disallowed in Pass 3a.
469        // Instructions shared by a subroutine and the toplevel are
470        // disallowed and checked below, after the BFS.
471        for (final InstructionHandle element : all) {
472            final Instruction inst = element.getInstruction();
473            if (inst instanceof JsrInstruction) {
474                final InstructionHandle leader = ((JsrInstruction) inst).getTarget();
475                ((SubroutineImpl) getSubroutine(leader)).addEnteringJsrInstruction(element);
476            }
477        }
478
479        // Now do a BFS from every subroutine leader to find all the
480        // instructions that belong to a subroutine.
481        // we don't want to assign an instruction to two or more Subroutine objects.
482        final Set<InstructionHandle> instructionsAssigned = new HashSet<>();
483
484        // Graph coloring. Key: InstructionHandle, Value: ColourConstants enum.
485        final Map<InstructionHandle, ColourConstants> colors = new HashMap<>();
486
487        final List<InstructionHandle> qList = new ArrayList<>();
488        for (final InstructionHandle actual : subLeaders) {
489            // Do some BFS with "actual" as the root of the graph.
490            // Init colors: an instruction absent from the map is WHITE. Explicitly coloring every
491            // instruction WHITE on every round would make this initialization quadratic in the
492            // method size.
493            colors.clear();
494            colors.put(actual, ColourConstants.GRAY);
495            // Init Queue
496
497            qList.clear();
498            qList.add(actual); // add(Obj) adds to the end, remove(0) removes from the start.
499
500            /*
501             * BFS ALGORITHM MODIFICATION: Start out with multiple "root" nodes, as exception handlers are starting points of
502             * top-level code, too. [why top-level? TODO: Refer to the special JustIce notion of subroutines.]
503             */
504            if (actual == all[0]) {
505                for (final CodeExceptionGen handler : handlers) {
506                    colors.put(handler.getHandlerPC(), ColourConstants.GRAY);
507                    qList.add(handler.getHandlerPC());
508                }
509            }
510            /* CONTINUE NORMAL BFS ALGORITHM */
511
512            // Loop until Queue is empty
513            while (!qList.isEmpty()) {
514                final InstructionHandle u = qList.remove(0);
515                final InstructionHandle[] successors = getSuccessors(u);
516                for (final InstructionHandle successor : successors) {
517                    if (successor != null && colors.get(successor) == null) { // absent from the map means WHITE
518                        colors.put(successor, ColourConstants.GRAY);
519                        qList.add(successor);
520                    }
521                }
522                colors.put(u, ColourConstants.BLACK);
523            }
524            // BFS ended above. Only instructions visited by this BFS round are in the color map,
525            // so this scan is proportional to the round, not to the whole method.
526            for (final Map.Entry<InstructionHandle, ColourConstants> entry : colors.entrySet()) {
527                if (entry.getValue() == ColourConstants.BLACK) {
528                    final InstructionHandle element = entry.getKey();
529                    ((SubroutineImpl) (actual == all[0] ? getTopLevel() : getSubroutine(actual))).addInstruction(element);
530                    if (instructionsAssigned.contains(element)) {
531                        throw new StructuralCodeConstraintException(
532                            "Instruction '" + element + "' is part of more than one subroutine (or of the top level and a subroutine).");
533                    }
534                    instructionsAssigned.add(element);
535                }
536            }
537            if (actual != all[0]) { // If we don't deal with the top-level 'subroutine'
538                ((SubroutineImpl) getSubroutine(actual)).setLeavingRET();
539            }
540        }
541
542        if (enableJustIceCheck && handlers.length > 0) {
543            // Now make sure no instruction of a Subroutine is protected by exception handling code
544            // as is mandated by JustIces notion of subroutines.
545            // The handler coverage of every instruction is computed once, with a difference array
546            // over instruction list indices. Walking every handler's protected range and, per
547            // protected instruction, every subroutine would let a crafted method (thousands of
548            // handlers over large ranges) keep this constructor busy nearly forever.
549            final Map<InstructionHandle, Integer> instructionIndexes = new HashMap<>();
550            for (int i = 0; i < all.length; i++) {
551                instructionIndexes.put(all[i], Integer.valueOf(i));
552            }
553            final int[] coverageDelta = new int[all.length + 1];
554            for (final CodeExceptionGen handler : handlers) {
555                // Note the inclusive/inclusive notation of "generic API" exception handlers!
556                final Integer startIndex = instructionIndexes.get(handler.getStartPC());
557                final Integer endIndex = instructionIndexes.get(handler.getEndPC());
558                if (startIndex == null || endIndex == null || startIndex.intValue() > endIndex.intValue()) {
559                    throw new StructuralCodeConstraintException("Exception handler '" + handler + "' does not protect a valid instruction range.");
560                }
561                coverageDelta[startIndex.intValue()]++;
562                coverageDelta[endIndex.intValue() + 1]--;
563            }
564            final boolean[] isProtected = new boolean[all.length];
565            int covered = 0;
566            for (int i = 0; i < all.length; i++) {
567                covered += coverageDelta[i];
568                isProtected[i] = covered > 0;
569            }
570            for (final Subroutine sub : subroutines.values()) {
571                if (sub == subroutines.get(all[0])) {
572                    continue;
573                }
574                for (final InstructionHandle protectedIh : sub.getInstructions()) {
575                    final Integer index = instructionIndexes.get(protectedIh);
576                    if (index != null && isProtected[index.intValue()]) {
577                        // Only the error message needs the offending handler; this scan runs at most once.
578                        for (final CodeExceptionGen handler : handlers) {
579                            final int startIndex = instructionIndexes.get(handler.getStartPC()).intValue();
580                            final int endIndex = instructionIndexes.get(handler.getEndPC()).intValue();
581                            if (startIndex <= index.intValue() && index.intValue() <= endIndex) {
582                                throw new StructuralCodeConstraintException("Subroutine instruction '" + protectedIh
583                                    + "' is protected by an exception handler, '" + handler
584                                    + "'. This is forbidden by the JustIce verifier due to its clear definition of subroutines.");
585                            }
586                        }
587                    }
588                }
589            }
590        }
591
592        // Now make sure no subroutine is calling a subroutine
593        // that uses the same local variable for the RET as themselves
594        // (recursively).
595        // This includes that subroutines may not call themselves
596        // recursively, even not through intermediate calls to other
597        // subroutines.
598        noRecursiveCalls(getTopLevel());
599
600    }
601
602    /**
603     * Gets the Subroutine object associated with the given leader (that is, the first instruction of the subroutine).
604     * You must not use this to get the top-level instructions modeled as a Subroutine object.
605     *
606     * @param leader The leader instruction handle.
607     * @return The Subroutine object.
608     * @see #getTopLevel()
609     */
610    public Subroutine getSubroutine(final InstructionHandle leader) {
611        final Subroutine ret = subroutines.get(leader);
612
613        if (ret == null) {
614            throw new AssertionViolatedException("Subroutine requested for an InstructionHandle that is not a leader of a subroutine.");
615        }
616
617        if (ret == TOPLEVEL) {
618            throw new AssertionViolatedException("TOPLEVEL special subroutine requested; use getTopLevel().");
619        }
620
621        return ret;
622    }
623
624    /**
625     * Gets the Subroutine object representing the top-level code.
626     *
627     * For easy handling, the piece of code that is <strong>not</strong> a subroutine, the top-level, is also modeled as a Subroutine
628     * object. It is a special Subroutine object where <B>you must not invoke getEnteringJsrInstructions() or
629     * getLeavingRET()</B>.
630     *
631     * @return The top-level Subroutine.
632     * @see Subroutine#getEnteringJsrInstructions()
633     * @see Subroutine#getLeavingRET()
634     */
635    public Subroutine getTopLevel() {
636        return TOPLEVEL;
637    }
638
639    /**
640     * This utility method makes sure that no subroutine is calling a subroutine that uses the same local
641     * variable for the RET as themselves (recursively). This includes that subroutines may not call themselves recursively,
642     * even not through intermediate calls to other subroutines.
643     *
644     * Every subroutine is fully validated exactly once, memoizing the RET local variable indices used anywhere in its
645     * call subtree. The former implementation re-explored a subroutine once per call path, which is exponential in the
646     * number of subroutines for a layered JSR call graph.
647     *
648     * @throws StructuralCodeConstraintException Thrown if the above constraint is not satisfied.
649     */
650    private void noRecursiveCalls(final Subroutine sub) {
651        noRecursiveCalls(sub, new BitSet(), new HashMap<>(), new HashMap<>());
652    }
653
654    /**
655     * The recursive helper for {@link #noRecursiveCalls(Subroutine)}.
656     *
657     * @param sub the subroutine whose callees are validated.
658     * @param pathLocals compact ids (see {@code localIds}) of the RET local variables used by the subroutines on the current call path.
659     * @param validated maps every fully validated subroutine to the compact ids of the RET local variables used by it and its entire call subtree.
660     * @param localIds maps a RET local variable index to a compact id so the bit sets stay small.
661     * @return the compact ids of the RET local variables used by {@code sub}'s callees and their call subtrees.
662     * @throws StructuralCodeConstraintException Thrown if a subroutine calls a subroutine using the same RET local variable.
663     */
664    private BitSet noRecursiveCalls(final Subroutine sub, final BitSet pathLocals, final Map<Subroutine, BitSet> validated,
665        final Map<Integer, Integer> localIds) {
666        final BitSet subtreeLocals = new BitSet();
667
668        for (final Subroutine sub2 : sub.subSubs()) {
669            final Integer index = Integer.valueOf(((RET) sub2.getLeavingRET().getInstruction()).getIndex());
670            final int localId = localIds.computeIfAbsent(index, k -> Integer.valueOf(localIds.size())).intValue();
671
672            BitSet childLocals = validated.get(sub2);
673            if (childLocals == null) {
674                if (pathLocals.get(localId)) {
675                    // sub2 uses a RET local variable also used by a subroutine on the current call path;
676                    // this also covers (possibly indirect) recursive calls.
677                    throw recursiveCallException(sub2);
678                }
679                pathLocals.set(localId);
680                childLocals = noRecursiveCalls(sub2, pathLocals, validated, localIds);
681                pathLocals.clear(localId);
682                childLocals.set(localId);
683                validated.put(sub2, childLocals);
684            } else if (childLocals.intersects(pathLocals)) {
685                // A subroutine in sub2's (already validated) call subtree uses a RET local variable also
686                // used by a subroutine on the current call path.
687                throw recursiveCallException(sub2);
688            }
689            subtreeLocals.or(childLocals);
690        }
691        return subtreeLocals;
692    }
693
694    /**
695     * Returns the subroutine object associated with the given instruction. This is a costly operation, you should consider
696     * using getSubroutine(InstructionHandle). Returns 'null' if the given InstructionHandle lies in so-called 'dead code',
697     * for example code that can never be executed.
698     *
699     * @param any The instruction handle.
700     * @return The Subroutine object or null.
701     * @see #getSubroutine(InstructionHandle)
702     * @see #getTopLevel()
703     */
704    public Subroutine subroutineOf(final InstructionHandle any) {
705        for (final Subroutine s : subroutines.values()) {
706            if (s.contains(any)) {
707                return s;
708            }
709        }
710        System.err.println("DEBUG: Please verify '" + any.toString(true) + "' lies in dead code.");
711        return null;
712        // throw new AssertionViolatedException("No subroutine for InstructionHandle found (DEAD CODE?).");
713    }
714
715    /**
716     * Returns a String representation of this object; merely for debugging purposes.
717     */
718    @Override
719    public String toString() {
720        return "---\n" + subroutines + "\n---\n";
721    }
722}