Subroutines.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  *  Unless required by applicable law or agreed to in writing, software
  12.  *  distributed under the License is distributed on an "AS IS" BASIS,
  13.  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  *  See the License for the specific language governing permissions and
  15.  *  limitations under the License.
  16.  */
  17. package org.apache.bcel.verifier.structurals;

  18. import java.util.ArrayList;
  19. import java.util.HashMap;
  20. import java.util.HashSet;
  21. import java.util.List;
  22. import java.util.Map;
  23. import java.util.Set;

  24. import org.apache.bcel.generic.ASTORE;
  25. import org.apache.bcel.generic.ATHROW;
  26. import org.apache.bcel.generic.BranchInstruction;
  27. import org.apache.bcel.generic.CodeExceptionGen;
  28. import org.apache.bcel.generic.GotoInstruction;
  29. import org.apache.bcel.generic.IndexedInstruction;
  30. import org.apache.bcel.generic.Instruction;
  31. import org.apache.bcel.generic.InstructionHandle;
  32. import org.apache.bcel.generic.JsrInstruction;
  33. import org.apache.bcel.generic.LocalVariableInstruction;
  34. import org.apache.bcel.generic.MethodGen;
  35. import org.apache.bcel.generic.RET;
  36. import org.apache.bcel.generic.ReturnInstruction;
  37. import org.apache.bcel.generic.Select;
  38. import org.apache.bcel.verifier.exc.AssertionViolatedException;
  39. import org.apache.bcel.verifier.exc.StructuralCodeConstraintException;

  40. /**
  41.  * Instances of this class contain information about the subroutines found in a code array of a method. This
  42.  * implementation considers the top-level (the instructions reachable without a JSR or JSR_W starting off from the first
  43.  * instruction in a code array of a method) being a special subroutine; see getTopLevel() for that. Please note that the
  44.  * definition of subroutines in the Java Virtual Machine Specification, Second Edition is somewhat incomplete.
  45.  * Therefore, JustIce uses an own, more rigid notion. Basically, a subroutine is a piece of code that starts at the
  46.  * target of a JSR of JSR_W instruction and ends at a corresponding RET instruction. Note also that the control flow of
  47.  * a subroutine may be complex and non-linear; and that subroutines may be nested. JustIce also mandates subroutines not
  48.  * to be protected by exception handling code (for the sake of control flow predictability). To understand JustIce's
  49.  * notion of subroutines, please read
  50.  *
  51.  * TODO: refer to the paper.
  52.  *
  53.  * @see #getTopLevel()
  54.  */
  55. public class Subroutines {
  56.     // Node coloring constants
  57.     private enum ColourConstants {
  58.         WHITE, GRAY, BLACK
  59.     }

  60.     /**
  61.      * This inner class implements the Subroutine interface.
  62.      */
  63.     private final class SubroutineImpl implements Subroutine {
  64.         /**
  65.          * UNSET, a symbol for an uninitialized localVariable field. This is used for the "top-level" Subroutine; i.e. no
  66.          * subroutine.
  67.          */
  68.         private static final int UNSET = -1;

  69.         private final SubroutineImpl[] EMPTY_ARRAY = {};

  70.         /**
  71.          * The Local Variable slot where the first instruction of this subroutine (an ASTORE) stores the JsrInstruction's
  72.          * ReturnAddress in and the RET of this subroutine operates on.
  73.          */
  74.         private int localVariable = UNSET;

  75.         /** The instructions that belong to this subroutine. */
  76.         private final Set<InstructionHandle> instructions = new HashSet<>(); // Elements: InstructionHandle

  77.         /**
  78.          * The JSR or JSR_W instructions that define this subroutine by targeting it.
  79.          */
  80.         private final Set<InstructionHandle> theJSRs = new HashSet<>();

  81.         /**
  82.          * The RET instruction that leaves this subroutine.
  83.          */
  84.         private InstructionHandle theRET;

  85.         /**
  86.          * The default constructor.
  87.          */
  88.         public SubroutineImpl() {
  89.         }

  90.         /**
  91.          * Adds a new JSR or JSR_W that has this subroutine as its target.
  92.          */
  93.         public void addEnteringJsrInstruction(final InstructionHandle jsrInst) {
  94.             if (jsrInst == null || !(jsrInst.getInstruction() instanceof JsrInstruction)) {
  95.                 throw new AssertionViolatedException("Expecting JsrInstruction InstructionHandle.");
  96.             }
  97.             if (localVariable == UNSET) {
  98.                 throw new AssertionViolatedException("Set the localVariable first!");
  99.             }
  100.             // Something is wrong when an ASTORE is targeted that does not operate on the same local variable than the rest of the
  101.             // JsrInstruction-targets and the RET.
  102.             // (We don't know out leader here so we cannot check if we're really targeted!)
  103.             if (localVariable != ((ASTORE) ((JsrInstruction) jsrInst.getInstruction()).getTarget().getInstruction()).getIndex()) {
  104.                 throw new AssertionViolatedException("Setting a wrong JsrInstruction.");
  105.             }
  106.             theJSRs.add(jsrInst);
  107.         }

  108.         /*
  109.          * Adds an instruction to this subroutine. All instructions must have been added before invoking setLeavingRET().
  110.          *
  111.          * @see #setLeavingRET
  112.          */
  113.         void addInstruction(final InstructionHandle ih) {
  114.             if (theRET != null) {
  115.                 throw new AssertionViolatedException("All instructions must have been added before invoking setLeavingRET().");
  116.             }
  117.             instructions.add(ih);
  118.         }

  119.         /*
  120.          * Refer to the Subroutine interface for documentation.
  121.          */
  122.         @Override
  123.         public boolean contains(final InstructionHandle inst) {
  124.             return instructions.contains(inst);
  125.         }

  126.         /*
  127.          * Satisfies Subroutine.getAccessedLocalIndices().
  128.          */
  129.         @Override
  130.         public int[] getAccessedLocalsIndices() {
  131.             // TODO: Implement caching.
  132.             final Set<Integer> acc = new HashSet<>();
  133.             if (theRET == null && this != getTopLevel()) {
  134.                 throw new AssertionViolatedException("This subroutine object must be built up completely before calculating accessed locals.");
  135.             }
  136.             {
  137.                 for (final InstructionHandle ih : instructions) {
  138.                     // RET is not a LocalVariableInstruction in the current version of BCEL.
  139.                     if (ih.getInstruction() instanceof LocalVariableInstruction || ih.getInstruction() instanceof RET) {
  140.                         final int idx = ((IndexedInstruction) ih.getInstruction()).getIndex();
  141.                         acc.add(Integer.valueOf(idx));
  142.                         // LONG? DOUBLE?.
  143.                         try {
  144.                             // LocalVariableInstruction instances are typed without the need to look into
  145.                             // the constant pool.
  146.                             if (ih.getInstruction() instanceof LocalVariableInstruction) {
  147.                                 final int s = ((LocalVariableInstruction) ih.getInstruction()).getType(null).getSize();
  148.                                 if (s == 2) {
  149.                                     acc.add(Integer.valueOf(idx + 1));
  150.                                 }
  151.                             }
  152.                         } catch (final RuntimeException re) {
  153.                             throw new AssertionViolatedException("BCEL did not like NULL as a ConstantPoolGen object.", re);
  154.                         }
  155.                     }
  156.                 }
  157.             }

  158.             {
  159.                 final int[] ret = new int[acc.size()];
  160.                 int j = -1;
  161.                 for (final Integer accessedLocal : acc) {
  162.                     j++;
  163.                     ret[j] = accessedLocal.intValue();
  164.                 }
  165.                 return ret;
  166.             }
  167.         }

  168.         /*
  169.          * Refer to the Subroutine interface for documentation.
  170.          */
  171.         @Override
  172.         public InstructionHandle[] getEnteringJsrInstructions() {
  173.             if (this == getTopLevel()) {
  174.                 throw new AssertionViolatedException("getLeavingRET() called on top level pseudo-subroutine.");
  175.             }
  176.             return theJSRs.toArray(InstructionHandle.EMPTY_ARRAY);
  177.         }

  178.         /*
  179.          * Refer to the Subroutine interface for documentation.
  180.          */
  181.         @Override
  182.         public InstructionHandle[] getInstructions() {
  183.             return instructions.toArray(InstructionHandle.EMPTY_ARRAY);
  184.         }

  185.         /*
  186.          * Refer to the Subroutine interface for documentation.
  187.          */
  188.         @Override
  189.         public InstructionHandle getLeavingRET() {
  190.             if (this == getTopLevel()) {
  191.                 throw new AssertionViolatedException("getLeavingRET() called on top level pseudo-subroutine.");
  192.             }
  193.             return theRET;
  194.         }

  195.         /* Satisfies Subroutine.getRecursivelyAccessedLocalsIndices(). */
  196.         @Override
  197.         public int[] getRecursivelyAccessedLocalsIndices() {
  198.             final Set<Integer> s = new HashSet<>();
  199.             final int[] lvs = getAccessedLocalsIndices();
  200.             for (final int lv : lvs) {
  201.                 s.add(Integer.valueOf(lv));
  202.             }
  203.             getRecursivelyAccessedLocalsIndicesHelper(s, subSubs());
  204.             final int[] ret = new int[s.size()];
  205.             int j = -1;
  206.             for (final Integer index : s) {
  207.                 j++;
  208.                 ret[j] = index.intValue();
  209.             }
  210.             return ret;
  211.         }

  212.         /**
  213.          * A recursive helper method for getRecursivelyAccessedLocalsIndices().
  214.          *
  215.          * @see #getRecursivelyAccessedLocalsIndices()
  216.          */
  217.         private void getRecursivelyAccessedLocalsIndicesHelper(final Set<Integer> set, final Subroutine[] subs) {
  218.             for (final Subroutine sub : subs) {
  219.                 final int[] lvs = sub.getAccessedLocalsIndices();
  220.                 for (final int lv : lvs) {
  221.                     set.add(Integer.valueOf(lv));
  222.                 }
  223.                 if (sub.subSubs().length != 0) {
  224.                     getRecursivelyAccessedLocalsIndicesHelper(set, sub.subSubs());
  225.                 }
  226.             }
  227.         }

  228.         /**
  229.          * Sets the leaving RET instruction. Must be invoked after all instructions are added. Must not be invoked for top-level
  230.          * 'subroutine'.
  231.          */
  232.         void setLeavingRET() {
  233.             if (localVariable == UNSET) {
  234.                 throw new AssertionViolatedException("setLeavingRET() called for top-level 'subroutine' or forgot to set local variable first.");
  235.             }
  236.             InstructionHandle ret = null;
  237.             for (final InstructionHandle actual : instructions) {
  238.                 if (actual.getInstruction() instanceof RET) {
  239.                     if (ret != null) {
  240.                         throw new StructuralCodeConstraintException("Subroutine with more then one RET detected: '" + ret + "' and '" + actual + "'.");
  241.                     }
  242.                     ret = actual;
  243.                 }
  244.             }
  245.             if (ret == null) {
  246.                 throw new StructuralCodeConstraintException("Subroutine without a RET detected.");
  247.             }
  248.             if (((RET) ret.getInstruction()).getIndex() != localVariable) {
  249.                 throw new StructuralCodeConstraintException(
  250.                     "Subroutine uses '" + ret + "' which does not match the correct local variable '" + localVariable + "'.");
  251.             }
  252.             theRET = ret;
  253.         }

  254.         /*
  255.          * Sets the local variable slot the ASTORE that is targeted by the JsrInstructions of this subroutine operates on. This
  256.          * subroutine's RET operates on that same local variable slot, of course.
  257.          */
  258.         void setLocalVariable(final int i) {
  259.             if (localVariable != UNSET) {
  260.                 throw new AssertionViolatedException("localVariable set twice.");
  261.             }
  262.             localVariable = i;
  263.         }

  264.         /*
  265.          * Satisfies Subroutine.subSubs().
  266.          */
  267.         @Override
  268.         public Subroutine[] subSubs() {
  269.             final Set<Subroutine> h = new HashSet<>();

  270.             for (final InstructionHandle ih : instructions) {
  271.                 final Instruction inst = ih.getInstruction();
  272.                 if (inst instanceof JsrInstruction) {
  273.                     final InstructionHandle targ = ((JsrInstruction) inst).getTarget();
  274.                     h.add(getSubroutine(targ));
  275.                 }
  276.             }
  277.             return h.toArray(EMPTY_ARRAY);
  278.         }

  279.         /**
  280.          * Returns a String representation of this object, merely for debugging purposes. (Internal) Warning: Verbosity on a
  281.          * problematic subroutine may cause stack overflow errors due to recursive subSubs() calls. Don't use this, then.
  282.          */
  283.         @Override
  284.         public String toString() {
  285.             final StringBuilder ret = new StringBuilder();
  286.             ret.append("Subroutine: Local variable is '").append(localVariable);
  287.             ret.append("', JSRs are '").append(theJSRs);
  288.             ret.append("', RET is '").append(theRET);
  289.             ret.append("', Instructions: '").append(instructions).append("'.");

  290.             ret.append(" Accessed local variable slots: '");
  291.             int[] alv = getAccessedLocalsIndices();
  292.             for (final int element : alv) {
  293.                 ret.append(element);
  294.                 ret.append(" ");
  295.             }
  296.             ret.append("'.");

  297.             ret.append(" Recursively (via subsub...routines) accessed local variable slots: '");
  298.             alv = getRecursivelyAccessedLocalsIndices();
  299.             for (final int element : alv) {
  300.                 ret.append(element);
  301.                 ret.append(" ");
  302.             }
  303.             ret.append("'.");

  304.             return ret.toString();
  305.         }

  306.     } // end Inner Class SubrouteImpl

  307.     /**
  308.      * A utility method that calculates the successors of a given InstructionHandle <B>in the same subroutine</B>. That
  309.      * means, a RET does not have any successors as defined here. A JsrInstruction has its physical successor as its
  310.      * successor (opposed to its target) as defined here.
  311.      */
  312.     private static InstructionHandle[] getSuccessors(final InstructionHandle instruction) {
  313.         final InstructionHandle[] single = new InstructionHandle[1];

  314.         final Instruction inst = instruction.getInstruction();

  315.         // Terminates method normally.
  316.         // Terminates method abnormally, because JustIce mandates
  317.         // subroutines not to be protected by exception handlers.
  318.         if (inst instanceof RET || inst instanceof ReturnInstruction || inst instanceof ATHROW) {
  319.             return InstructionHandle.EMPTY_ARRAY;
  320.         }

  321.         // See method comment.
  322.         if (inst instanceof JsrInstruction) {
  323.             single[0] = instruction.getNext();
  324.             return single;
  325.         }

  326.         if (inst instanceof GotoInstruction) {
  327.             single[0] = ((GotoInstruction) inst).getTarget();
  328.             return single;
  329.         }

  330.         if (inst instanceof BranchInstruction) {
  331.             if (inst instanceof Select) {
  332.                 // BCEL's getTargets() returns only the non-default targets,
  333.                 // thanks to Eli Tilevich for reporting.
  334.                 final InstructionHandle[] matchTargets = ((Select) inst).getTargets();
  335.                 final InstructionHandle[] ret = new InstructionHandle[matchTargets.length + 1];
  336.                 ret[0] = ((Select) inst).getTarget();
  337.                 System.arraycopy(matchTargets, 0, ret, 1, matchTargets.length);
  338.                 return ret;
  339.             }
  340.             final InstructionHandle[] pair = new InstructionHandle[2];
  341.             pair[0] = instruction.getNext();
  342.             pair[1] = ((BranchInstruction) inst).getTarget();
  343.             return pair;
  344.         }

  345.         // default case: Fall through.
  346.         single[0] = instruction.getNext();
  347.         return single;
  348.     }

  349.     /**
  350.      * The map containing the subroutines found. Key: InstructionHandle of the leader of the subroutine. Elements:
  351.      * SubroutineImpl objects.
  352.      */
  353.     private final Map<InstructionHandle, Subroutine> subroutines = new HashMap<>();

  354.     /**
  355.      * This is referring to a special subroutine, namely the top level. This is not really a subroutine but we use it to
  356.      * distinguish between top level instructions and unreachable instructions.
  357.      */
  358.     // CHECKSTYLE:OFF
  359.     public final Subroutine TOPLEVEL; // TODO can this be made private?
  360.     // CHECKSTYLE:ON

  361.     /**
  362.      * Constructs a new instance.
  363.      *
  364.      * @param mg A MethodGen object representing method to create the Subroutine objects of. Assumes that JustIce strict
  365.      *        checks are needed.
  366.      */
  367.     public Subroutines(final MethodGen mg) {
  368.         this(mg, true);
  369.     }

  370.     /**
  371.      * Constructs a new instance.
  372.      *
  373.      * @param mg A MethodGen object representing method to create the Subroutine objects of.
  374.      * @param enableJustIceCheck whether to enable additional JustIce checks
  375.      * @since 6.0
  376.      */
  377.     public Subroutines(final MethodGen mg, final boolean enableJustIceCheck) {
  378.         final InstructionHandle[] all = mg.getInstructionList().getInstructionHandles();
  379.         final CodeExceptionGen[] handlers = mg.getExceptionHandlers();

  380.         // Define our "Toplevel" fake subroutine.
  381.         TOPLEVEL = new SubroutineImpl();

  382.         // Calculate "real" subroutines.
  383.         final Set<InstructionHandle> subLeaders = new HashSet<>(); // Elements: InstructionHandle
  384.         for (final InstructionHandle element : all) {
  385.             final Instruction inst = element.getInstruction();
  386.             if (inst instanceof JsrInstruction) {
  387.                 subLeaders.add(((JsrInstruction) inst).getTarget());
  388.             }
  389.         }

  390.         // Build up the database.
  391.         for (final InstructionHandle astore : subLeaders) {
  392.             final SubroutineImpl sr = new SubroutineImpl();
  393.             sr.setLocalVariable(((ASTORE) astore.getInstruction()).getIndex());
  394.             subroutines.put(astore, sr);
  395.         }

  396.         // Fake it a bit. We want a virtual "TopLevel" subroutine.
  397.         subroutines.put(all[0], TOPLEVEL);
  398.         subLeaders.add(all[0]);

  399.         // Tell the subroutines about their JsrInstructions.
  400.         // Note that there cannot be a JSR targeting the top-level
  401.         // since "Jsr 0" is disallowed in Pass 3a.
  402.         // Instructions shared by a subroutine and the toplevel are
  403.         // disallowed and checked below, after the BFS.
  404.         for (final InstructionHandle element : all) {
  405.             final Instruction inst = element.getInstruction();
  406.             if (inst instanceof JsrInstruction) {
  407.                 final InstructionHandle leader = ((JsrInstruction) inst).getTarget();
  408.                 ((SubroutineImpl) getSubroutine(leader)).addEnteringJsrInstruction(element);
  409.             }
  410.         }

  411.         // Now do a BFS from every subroutine leader to find all the
  412.         // instructions that belong to a subroutine.
  413.         // we don't want to assign an instruction to two or more Subroutine objects.
  414.         final Set<InstructionHandle> instructionsAssigned = new HashSet<>();

  415.         // Graph coloring. Key: InstructionHandle, Value: ColourConstants enum .
  416.         final Map<InstructionHandle, ColourConstants> colors = new HashMap<>();

  417.         final List<InstructionHandle> qList = new ArrayList<>();
  418.         for (final InstructionHandle actual : subLeaders) {
  419.             // Do some BFS with "actual" as the root of the graph.
  420.             // Init colors
  421.             for (final InstructionHandle element : all) {
  422.                 colors.put(element, ColourConstants.WHITE);
  423.             }
  424.             colors.put(actual, ColourConstants.GRAY);
  425.             // Init Queue

  426.             qList.clear();
  427.             qList.add(actual); // add(Obj) adds to the end, remove(0) removes from the start.

  428.             /*
  429.              * BFS ALGORITHM MODIFICATION: Start out with multiple "root" nodes, as exception handlers are starting points of
  430.              * top-level code, too. [why top-level? TODO: Refer to the special JustIce notion of subroutines.]
  431.              */
  432.             if (actual == all[0]) {
  433.                 for (final CodeExceptionGen handler : handlers) {
  434.                     colors.put(handler.getHandlerPC(), ColourConstants.GRAY);
  435.                     qList.add(handler.getHandlerPC());
  436.                 }
  437.             }
  438.             /* CONTINUE NORMAL BFS ALGORITHM */

  439.             // Loop until Queue is empty
  440.             while (!qList.isEmpty()) {
  441.                 final InstructionHandle u = qList.remove(0);
  442.                 final InstructionHandle[] successors = getSuccessors(u);
  443.                 for (final InstructionHandle successor : successors) {
  444.                     if (colors.get(successor) == ColourConstants.WHITE) {
  445.                         colors.put(successor, ColourConstants.GRAY);
  446.                         qList.add(successor);
  447.                     }
  448.                 }
  449.                 colors.put(u, ColourConstants.BLACK);
  450.             }
  451.             // BFS ended above.
  452.             for (final InstructionHandle element : all) {
  453.                 if (colors.get(element) == ColourConstants.BLACK) {
  454.                     ((SubroutineImpl) (actual == all[0] ? getTopLevel() : getSubroutine(actual))).addInstruction(element);
  455.                     if (instructionsAssigned.contains(element)) {
  456.                         throw new StructuralCodeConstraintException(
  457.                             "Instruction '" + element + "' is part of more than one subroutine (or of the top level and a subroutine).");
  458.                     }
  459.                     instructionsAssigned.add(element);
  460.                 }
  461.             }
  462.             if (actual != all[0]) { // If we don't deal with the top-level 'subroutine'
  463.                 ((SubroutineImpl) getSubroutine(actual)).setLeavingRET();
  464.             }
  465.         }

  466.         if (enableJustIceCheck) {
  467.             // Now make sure no instruction of a Subroutine is protected by exception handling code
  468.             // as is mandated by JustIces notion of subroutines.
  469.             for (final CodeExceptionGen handler : handlers) {
  470.                 InstructionHandle protectedIh = handler.getStartPC();
  471.                 while (protectedIh != handler.getEndPC().getNext()) {
  472.                     // Note the inclusive/inclusive notation of "generic API" exception handlers!
  473.                     for (final Subroutine sub : subroutines.values()) {
  474.                         if (sub != subroutines.get(all[0]) && sub.contains(protectedIh)) {
  475.                             throw new StructuralCodeConstraintException("Subroutine instruction '" + protectedIh + "' is protected by an exception handler, '"
  476.                                 + handler + "'. This is forbidden by the JustIce verifier due to its clear definition of subroutines.");
  477.                         }
  478.                     }
  479.                     protectedIh = protectedIh.getNext();
  480.                 }
  481.             }
  482.         }

  483.         // Now make sure no subroutine is calling a subroutine
  484.         // that uses the same local variable for the RET as themselves
  485.         // (recursively).
  486.         // This includes that subroutines may not call themselves
  487.         // recursively, even not through intermediate calls to other
  488.         // subroutines.
  489.         noRecursiveCalls(getTopLevel(), new HashSet<>());

  490.     }

  491.     /**
  492.      * Returns the Subroutine object associated with the given leader (that is, the first instruction of the subroutine).
  493.      * You must not use this to get the top-level instructions modeled as a Subroutine object.
  494.      *
  495.      * @see #getTopLevel()
  496.      */
  497.     public Subroutine getSubroutine(final InstructionHandle leader) {
  498.         final Subroutine ret = subroutines.get(leader);

  499.         if (ret == null) {
  500.             throw new AssertionViolatedException("Subroutine requested for an InstructionHandle that is not a leader of a subroutine.");
  501.         }

  502.         if (ret == TOPLEVEL) {
  503.             throw new AssertionViolatedException("TOPLEVEL special subroutine requested; use getTopLevel().");
  504.         }

  505.         return ret;
  506.     }

  507.     /**
  508.      * For easy handling, the piece of code that is <B>not</B> a subroutine, the top-level, is also modeled as a Subroutine
  509.      * object. It is a special Subroutine object where <B>you must not invoke getEnteringJsrInstructions() or
  510.      * getLeavingRET()</B>.
  511.      *
  512.      * @see Subroutine#getEnteringJsrInstructions()
  513.      * @see Subroutine#getLeavingRET()
  514.      */
  515.     public Subroutine getTopLevel() {
  516.         return TOPLEVEL;
  517.     }

  518.     /**
  519.      * This (recursive) utility method makes sure that no subroutine is calling a subroutine that uses the same local
  520.      * variable for the RET as themselves (recursively). This includes that subroutines may not call themselves recursively,
  521.      * even not through intermediate calls to other subroutines.
  522.      *
  523.      * @throws StructuralCodeConstraintException if the above constraint is not satisfied.
  524.      */
  525.     private void noRecursiveCalls(final Subroutine sub, final Set<Integer> set) {
  526.         final Subroutine[] subs = sub.subSubs();

  527.         for (final Subroutine sub2 : subs) {
  528.             final int index = ((RET) sub2.getLeavingRET().getInstruction()).getIndex();

  529.             if (!set.add(Integer.valueOf(index))) {
  530.                 // Don't use toString() here because of possibly infinite recursive subSubs() calls then.
  531.                 final SubroutineImpl si = (SubroutineImpl) sub2;
  532.                 throw new StructuralCodeConstraintException("Subroutine with local variable '" + si.localVariable + "', JSRs '" + si.theJSRs + "', RET '"
  533.                     + si.theRET + "' is called by a subroutine which uses the same local variable index as itself; maybe even a recursive call?"
  534.                     + " JustIce's clean definition of a subroutine forbids both.");
  535.             }

  536.             noRecursiveCalls(sub2, set);

  537.             set.remove(Integer.valueOf(index));
  538.         }
  539.     }

  540.     /**
  541.      * Returns the subroutine object associated with the given instruction. This is a costly operation, you should consider
  542.      * using getSubroutine(InstructionHandle). Returns 'null' if the given InstructionHandle lies in so-called 'dead code',
  543.      * i.e. code that can never be executed.
  544.      *
  545.      * @see #getSubroutine(InstructionHandle)
  546.      * @see #getTopLevel()
  547.      */
  548.     public Subroutine subroutineOf(final InstructionHandle any) {
  549.         for (final Subroutine s : subroutines.values()) {
  550.             if (s.contains(any)) {
  551.                 return s;
  552.             }
  553.         }
  554.         System.err.println("DEBUG: Please verify '" + any.toString(true) + "' lies in dead code.");
  555.         return null;
  556.         // throw new AssertionViolatedException("No subroutine for InstructionHandle found (DEAD CODE?).");
  557.     }

  558.     /**
  559.      * Returns a String representation of this object; merely for debugging puposes.
  560.      */
  561.     @Override
  562.     public String toString() {
  563.         return "---\n" + subroutines + "\n---\n";
  564.     }
  565. }