Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/apache/bcel/verifier/structurals/Subroutines.java


1   /*
2    * Copyright  2000-2004 The Apache Software Foundation
3    *
4    *  Licensed under the Apache License, Version 2.0 (the "License"); 
5    *  you may not use this file except in compliance with the License.
6    *  You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *  Unless required by applicable law or agreed to in writing, software
11   *  distributed under the License is distributed on an "AS IS" BASIS,
12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *  See the License for the specific language governing permissions and
14   *  limitations under the License. 
15   *
16   */ 
17  package org.apache.bcel.verifier.structurals;
18  
19  import java.util.ArrayList;
20  import java.util.HashSet;
21  import java.util.Hashtable;
22  import java.util.Iterator;
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    /**
42     * Instances of this class contain information about the subroutines
43     * found in a code array of a method.
44     * This implementation considers the top-level (the instructions
45     * reachable without a JSR or JSR_W starting off from the first
46     * instruction in a code array of a method) being a special subroutine;
47     * see getTopLevel() for that.
48     * Please note that the definition of subroutines in the Java Virtual
49     * Machine Specification, Second Edition is somewhat incomplete.
50     * Therefore, JustIce uses an own, more rigid notion.
51     * Basically, a subroutine is a piece of code that starts at the target
52     * of a JSR of JSR_W instruction and ends at a corresponding RET
53     * instruction. Note also that the control flow of a subroutine
54     * may be complex and non-linear; and that subroutines may be nested.
55     * JustIce also mandates subroutines not to be protected by exception
56     * handling code (for the sake of control flow predictability).
57     * To understand JustIce's notion of subroutines, please read
58     *
59     * TODO: refer to the paper.
60     *
61     * @version $Id: Subroutines.java 386056 2006-03-15 11:31:56Z tcurdt $
62     * @author Enver Haase
63     * @see #getTopLevel()
64     */
65  public class Subroutines{
66    /**
67     * This inner class implements the Subroutine interface.
68     */
69    private class SubroutineImpl implements Subroutine{
70      /**
71       * UNSET, a symbol for an uninitialized localVariable
72       * field. This is used for the "top-level" Subroutine;
73       * i.e. no subroutine.
74       */
75      private static final int UNSET = -1;
76  
77      /**
78       * The Local Variable slot where the first
79       * instruction of this subroutine (an ASTORE) stores
80       * the JsrInstruction's ReturnAddress in and
81       * the RET of this subroutine operates on.
82       */
83      private int localVariable = UNSET;
84  
85      /** The instructions that belong to this subroutine. */
86      private Set instructions = new HashSet(); // Elements: InstructionHandle
87      
88      /*
89       * Refer to the Subroutine interface for documentation.
90       */
91      public boolean contains(InstructionHandle inst){
92        return instructions.contains(inst);
93      }
94      
95      /**
96       * The JSR or JSR_W instructions that define this
97       * subroutine by targeting it.
98       */
99      private Set theJSRs = new HashSet();
100     
101     /**
102      * The RET instruction that leaves this subroutine.
103      */
104     private InstructionHandle theRET;
105     
106     /**
107      * Returns a String representation of this object, merely
108      * for debugging purposes.
109      * (Internal) Warning: Verbosity on a problematic subroutine may cause
110      * stack overflow errors due to recursive subSubs() calls.
111      * Don't use this, then.
112      */
113     public String toString(){
114       String ret = "Subroutine: Local variable is '"+localVariable+"', JSRs are '"+theJSRs+"', RET is '"+theRET+"', Instructions: '"+instructions.toString()+"'.";
115       
116       ret += " Accessed local variable slots: '";
117       int[] alv = getAccessedLocalsIndices();
118       for (int i=0; i<alv.length; i++){
119         ret += alv[i]+" ";
120       }
121       ret+="'.";
122 
123       ret += " Recursively (via subsub...routines) accessed local variable slots: '";
124       alv = getRecursivelyAccessedLocalsIndices();
125       for (int i=0; i<alv.length; i++){
126         ret += alv[i]+" ";
127       }
128       ret+="'.";
129 
130       return ret;
131     }
132     
133     /**
134      * Sets the leaving RET instruction. Must be invoked after all instructions are added.
135      * Must not be invoked for top-level 'subroutine'.
136      */
137     void setLeavingRET(){
138       if (localVariable == UNSET){
139         throw new AssertionViolatedException("setLeavingRET() called for top-level 'subroutine' or forgot to set local variable first.");
140       }
141       Iterator iter = instructions.iterator();
142       InstructionHandle ret = null;
143       while(iter.hasNext()){
144         InstructionHandle actual = (InstructionHandle) iter.next();
145         if (actual.getInstruction() instanceof RET){
146           if (ret != null){
147             throw new StructuralCodeConstraintException("Subroutine with more then one RET detected: '"+ret+"' and '"+actual+"'.");
148           }
149           else{
150             ret = actual;
151           }
152         }
153       }
154       if (ret == null){
155         throw new StructuralCodeConstraintException("Subroutine without a RET detected.");
156       }
157       if (((RET) ret.getInstruction()).getIndex() != localVariable){
158         throw new StructuralCodeConstraintException("Subroutine uses '"+ret+"' which does not match the correct local variable '"+localVariable+"'.");
159       }
160       theRET = ret;
161     }
162         
163     /*
164      * Refer to the Subroutine interface for documentation.
165      */
166     public InstructionHandle[] getEnteringJsrInstructions(){
167       if (this == TOPLEVEL) {
168         throw new AssertionViolatedException("getLeavingRET() called on top level pseudo-subroutine.");
169       }
170       InstructionHandle[] jsrs = new InstructionHandle[theJSRs.size()];
171       return (InstructionHandle[]) (theJSRs.toArray(jsrs));
172     }
173   
174     /**
175      * Adds a new JSR or JSR_W that has this subroutine as its target.
176      */
177     public void addEnteringJsrInstruction(InstructionHandle jsrInst){
178       if ( (jsrInst == null) || (! (jsrInst.getInstruction() instanceof JsrInstruction))){
179         throw new AssertionViolatedException("Expecting JsrInstruction InstructionHandle.");
180       }
181       if (localVariable == UNSET){
182         throw new AssertionViolatedException("Set the localVariable first!");
183       }
184       else{
185         // Something is wrong when an ASTORE is targeted that does not operate on the same local variable than the rest of the
186         // JsrInstruction-targets and the RET.
187         // (We don't know out leader here so we cannot check if we're really targeted!)
188         if (localVariable != ((ASTORE) (((JsrInstruction) jsrInst.getInstruction()).getTarget().getInstruction())).getIndex()){
189           throw new AssertionViolatedException("Setting a wrong JsrInstruction.");
190         }
191       }
192       theJSRs.add(jsrInst);
193     }
194 
195     /*
196      * Refer to the Subroutine interface for documentation.
197      */
198     public InstructionHandle getLeavingRET(){
199       if (this == TOPLEVEL) {
200         throw new AssertionViolatedException("getLeavingRET() called on top level pseudo-subroutine.");
201       }
202       return theRET;
203     }
204     
205     /*
206      * Refer to the Subroutine interface for documentation.
207      */
208     public InstructionHandle[] getInstructions(){
209       InstructionHandle[] ret = new InstructionHandle[instructions.size()];
210       return (InstructionHandle[]) instructions.toArray(ret);
211     }
212     
213     /*
214      * Adds an instruction to this subroutine.
215      * All instructions must have been added before invoking setLeavingRET().
216      * @see #setLeavingRET
217      */
218     void addInstruction(InstructionHandle ih){
219       if (theRET != null){
220         throw new AssertionViolatedException("All instructions must have been added before invoking setLeavingRET().");
221       }
222       instructions.add(ih);
223     }
224 
225     /* Satisfies Subroutine.getRecursivelyAccessedLocalsIndices(). */
226     public int[] getRecursivelyAccessedLocalsIndices(){
227       Set s = new HashSet();
228       int[] lvs = getAccessedLocalsIndices();
229       for (int j=0; j<lvs.length; j++){
230         s.add(new Integer(lvs[j]));
231       }
232       _getRecursivelyAccessedLocalsIndicesHelper(s, this.subSubs());
233       int[] ret = new int[s.size()];
234       Iterator i = s.iterator();
235       int j=-1;
236       while (i.hasNext()){
237         j++;
238         ret[j] = ((Integer) i.next()).intValue();
239       }
240       return ret;
241     }
242 
243     /**
244      * A recursive helper method for getRecursivelyAccessedLocalsIndices().
245      * @see #getRecursivelyAccessedLocalsIndices()
246      */
247     private void _getRecursivelyAccessedLocalsIndicesHelper(Set s, Subroutine[] subs){
248       for (int i=0; i<subs.length; i++){
249         int[] lvs = subs[i].getAccessedLocalsIndices();
250         for (int j=0; j<lvs.length; j++){
251           s.add(new Integer(lvs[j]));
252         }
253         if(subs[i].subSubs().length != 0){
254           _getRecursivelyAccessedLocalsIndicesHelper(s, subs[i].subSubs());
255         }
256       }
257     }
258 
259     /*
260      * Satisfies Subroutine.getAccessedLocalIndices().
261      */
262     public int[] getAccessedLocalsIndices(){
263       //TODO: Implement caching.
264       Set acc = new HashSet();
265       if (theRET == null && this != TOPLEVEL){
266         throw new AssertionViolatedException("This subroutine object must be built up completely before calculating accessed locals.");
267       }
268       Iterator i = instructions.iterator();
269       while (i.hasNext()){
270         InstructionHandle ih = (InstructionHandle) i.next();
271         // RET is not a LocalVariableInstruction in the current version of BCEL.
272         if (ih.getInstruction() instanceof LocalVariableInstruction || ih.getInstruction() instanceof RET){
273           int idx = ((IndexedInstruction) (ih.getInstruction())).getIndex();
274           acc.add(new Integer(idx));
275           // LONG? DOUBLE?.
276           try{
277             // LocalVariableInstruction instances are typed without the need to look into
278             // the constant pool.
279             if (ih.getInstruction() instanceof LocalVariableInstruction){
280               int s = ((LocalVariableInstruction) ih.getInstruction()).getType(null).getSize();
281               if (s==2) {
282                                 acc.add(new Integer(idx+1));
283                             }
284             }
285           }
286           catch(RuntimeException re){
287             throw new AssertionViolatedException("Oops. BCEL did not like NULL as a ConstantPoolGen object.");
288           }
289         }
290       }
291       
292       int[] ret = new int[acc.size()];
293       i = acc.iterator();
294       int j=-1;
295       while (i.hasNext()){
296         j++;
297         ret[j] = ((Integer) i.next()).intValue();
298       }
299       return ret;
300     }
301 
302     /*
303      * Satisfies Subroutine.subSubs().
304      */
305     public Subroutine[] subSubs(){
306       Set h = new HashSet();
307 
308       Iterator i = instructions.iterator();
309       while (i.hasNext()){
310         Instruction inst = ((InstructionHandle) i.next()).getInstruction();
311         if (inst instanceof JsrInstruction){
312           InstructionHandle targ = ((JsrInstruction) inst).getTarget();
313           h.add(getSubroutine(targ));
314         }
315       }
316       Subroutine[] ret = new Subroutine[h.size()];
317       return (Subroutine[]) h.toArray(ret);
318     }
319     
320     /*
321      * Sets the local variable slot the ASTORE that is targeted
322      * by the JsrInstructions of this subroutine operates on.
323      * This subroutine's RET operates on that same local variable
324      * slot, of course.
325      */
326     void setLocalVariable(int i){
327       if (localVariable != UNSET){
328         throw new AssertionViolatedException("localVariable set twice.");
329       }
330       else{
331         localVariable = i;
332       }
333     }
334     
335     /**
336      * The default constructor.
337      */
338     public SubroutineImpl(){
339     }
340 
341   }// end Inner Class SubrouteImpl
342 
343   //Node coloring constants
344   private static final Integer WHITE = new Integer(0);
345   private static final Integer GRAY = new Integer(1);
346   private static final Integer BLACK = new Integer(2);
347   
348   /**
349    * The Hashtable containing the subroutines found.
350    * Key: InstructionHandle of the leader of the subroutine.
351    * Elements: SubroutineImpl objects.
352    */
353   private Hashtable subroutines = new Hashtable();
354 
355   /**
356    * This is referring to a special subroutine, namely the
357    * top level. This is not really a subroutine but we use
358    * it to distinguish between top level instructions and
359    * unreachable instructions.
360    */
361   public final Subroutine TOPLEVEL;
362 
363   /**
364    * Constructor.
365    * @param mg A MethodGen object representing method to
366    * create the Subroutine objects of.
367    */
368   public Subroutines(MethodGen mg){
369   
370     InstructionHandle[] all = mg.getInstructionList().getInstructionHandles();
371     CodeExceptionGen[] handlers = mg.getExceptionHandlers();
372 
373     // Define our "Toplevel" fake subroutine.
374     TOPLEVEL = new SubroutineImpl();
375 
376     // Calculate "real" subroutines.
377     Set sub_leaders = new HashSet(); // Elements: InstructionHandle
378     for (int i=0; i<all.length; i++){
379       Instruction inst = all[i].getInstruction();
380       if (inst instanceof JsrInstruction){
381         sub_leaders.add(((JsrInstruction) inst).getTarget());
382       }
383     }
384  
385     // Build up the database.
386     Iterator iter = sub_leaders.iterator();
387     while (iter.hasNext()){
388       SubroutineImpl sr = new SubroutineImpl();
389       InstructionHandle astore = (InstructionHandle) (iter.next());
390       sr.setLocalVariable( ((ASTORE) (astore.getInstruction())).getIndex() );
391       subroutines.put(astore, sr);
392     }
393 
394     // Fake it a bit. We want a virtual "TopLevel" subroutine.
395     subroutines.put(all[0], TOPLEVEL);
396     sub_leaders.add(all[0]);
397 
398     // Tell the subroutines about their JsrInstructions.
399     // Note that there cannot be a JSR targeting the top-level
400     // since "Jsr 0" is disallowed in Pass 3a.
401     // Instructions shared by a subroutine and the toplevel are
402     // disallowed and checked below, after the BFS.
403     for (int i=0; i<all.length; i++){
404       Instruction inst = all[i].getInstruction();
405       if (inst instanceof JsrInstruction){
406         InstructionHandle leader = ((JsrInstruction) inst).getTarget();
407         ((SubroutineImpl) getSubroutine(leader)).addEnteringJsrInstruction(all[i]);
408       }
409     }
410     
411     // Now do a BFS from every subroutine leader to find all the
412     // instructions that belong to a subroutine.
413     Set instructions_assigned = new HashSet(); // we don't want to assign an instruction to two or more Subroutine objects.
414     
415     Hashtable colors = new Hashtable(); //Graph colouring. Key: InstructionHandle, Value: Integer .
416     
417     iter = sub_leaders.iterator();
418     while (iter.hasNext()){
419       // Do some BFS with "actual" as the root of the graph.
420       InstructionHandle actual = (InstructionHandle) (iter.next());
421       // Init colors
422       for (int i=0; i<all.length; i++){
423         colors.put(all[i], WHITE);
424       }
425       colors.put(actual, GRAY);
426       // Init Queue
427       ArrayList Q = new ArrayList();
428       Q.add(actual); // add(Obj) adds to the end, remove(0) removes from the start.
429       
430       /* BFS ALGORITHM MODIFICATION: Start out with multiple "root" nodes, as exception handlers are starting points of top-level code, too. [why top-level? TODO: Refer to the special JustIce notion of subroutines.]*/
431       if (actual == all[0]){
432         for (int j=0; j<handlers.length; j++){
433           colors.put(handlers[j].getHandlerPC(), GRAY);
434           Q.add(handlers[j].getHandlerPC());
435         }
436       }
437       /* CONTINUE NORMAL BFS ALGORITHM */
438       
439       // Loop until Queue is empty
440       while (Q.size() != 0){
441         InstructionHandle u = (InstructionHandle) Q.remove(0);
442         InstructionHandle[] successors = getSuccessors(u);
443         for (int i=0; i<successors.length; i++){
444           if (((Integer) colors.get(successors[i])) == WHITE){
445             colors.put(successors[i], GRAY);
446             Q.add(successors[i]);
447           }
448         }
449         colors.put(u, BLACK);
450       }
451       // BFS ended above.
452       for (int i=0; i<all.length; i++){
453         if (colors.get(all[i]) == BLACK){
454           ((SubroutineImpl) (actual==all[0]?getTopLevel():getSubroutine(actual))).addInstruction(all[i]);
455           if (instructions_assigned.contains(all[i])){
456             throw new StructuralCodeConstraintException("Instruction '"+all[i]+"' is part of more than one subroutine (or of the top level and a subroutine).");
457           }
458           else{
459             instructions_assigned.add(all[i]);
460           }
461         }
462       }
463       if (actual != all[0]){// If we don't deal with the top-level 'subroutine'
464         ((SubroutineImpl) getSubroutine(actual)).setLeavingRET();
465       }
466     }
467     
468     // Now make sure no instruction of a Subroutine is protected by exception handling code
469     // as is mandated by JustIces notion of subroutines.
470     for (int i=0; i<handlers.length; i++){
471       InstructionHandle _protected = handlers[i].getStartPC();
472       while (_protected != handlers[i].getEndPC().getNext()){// Note the inclusive/inclusive notation of "generic API" exception handlers!
473         Iterator subs = subroutines.values().iterator();
474         while (subs.hasNext()){
475           Subroutine sub = (Subroutine) subs.next();
476           if (sub != subroutines.get(all[0])){  // We don't want to forbid top-level exception handlers.
477             if (sub.contains(_protected)){
478               throw new StructuralCodeConstraintException("Subroutine instruction '"+_protected+"' is protected by an exception handler, '"+handlers[i]+"'. This is forbidden by the JustIce verifier due to its clear definition of subroutines.");
479             }
480           }
481         }
482         _protected = _protected.getNext();
483       }
484     }
485     
486     // Now make sure no subroutine is calling a subroutine
487     // that uses the same local variable for the RET as themselves
488     // (recursively).
489     // This includes that subroutines may not call themselves
490     // recursively, even not through intermediate calls to other
491     // subroutines.
492     noRecursiveCalls(getTopLevel(), new HashSet());
493 
494   }
495 
496   /**
497    * This (recursive) utility method makes sure that
498    * no subroutine is calling a subroutine
499    * that uses the same local variable for the RET as themselves
500    * (recursively).
501    * This includes that subroutines may not call themselves
502    * recursively, even not through intermediate calls to other
503    * subroutines.
504    *
505    * @throws StructuralCodeConstraintException if the above constraint is not satisfied.
506    */
507   private void noRecursiveCalls(Subroutine sub, Set set){
508     Subroutine[] subs = sub.subSubs();
509 
510     for (int i=0; i<subs.length; i++){
511       int index = ((RET) (subs[i].getLeavingRET().getInstruction())).getIndex();
512       
513       if (!set.add(new Integer(index))){
514         // Don't use toString() here because of possibly infinite recursive subSubs() calls then.
515         SubroutineImpl si = (SubroutineImpl) subs[i];
516         throw new StructuralCodeConstraintException("Subroutine with local variable '"+si.localVariable+"', JSRs '"+si.theJSRs+"', RET '"+si.theRET+"' is called by a subroutine which uses the same local variable index as itself; maybe even a recursive call? JustIce's clean definition of a subroutine forbids both.");
517       }
518 
519       noRecursiveCalls(subs[i], set);
520       
521       set.remove(new Integer(index));
522     }
523   } 
524   
525   /**
526    * Returns the Subroutine object associated with the given
527    * leader (that is, the first instruction of the subroutine).
528    * You must not use this to get the top-level instructions
529    * modeled as a Subroutine object.
530    *
531    * @see #getTopLevel()
532    */
533   public Subroutine getSubroutine(InstructionHandle leader){
534     Subroutine ret = (Subroutine) subroutines.get(leader);
535     
536     if (ret == null){
537       throw new AssertionViolatedException("Subroutine requested for an InstructionHandle that is not a leader of a subroutine.");
538     }
539 
540     if (ret == TOPLEVEL){
541       throw new AssertionViolatedException("TOPLEVEL special subroutine requested; use getTopLevel().");
542     }
543     
544     return ret;
545   }
546 
547   /**
548    * Returns the subroutine object associated with the
549    * given instruction. This is a costly operation, you
550    * should consider using getSubroutine(InstructionHandle).
551    * Returns 'null' if the given InstructionHandle lies
552    * in so-called 'dead code', i.e. code that can never
553    * be executed.
554    *
555    * @see #getSubroutine(InstructionHandle)
556    * @see #getTopLevel()
557    */
558   public Subroutine subroutineOf(InstructionHandle any){
559     Iterator i = subroutines.values().iterator();
560     while (i.hasNext()){
561       Subroutine s = (Subroutine) i.next();
562       if (s.contains(any)) {
563                 return s;
564             }
565     }
566 System.err.println("DEBUG: Please verify '"+any+"' lies in dead code.");
567     return null;
568     //throw new AssertionViolatedException("No subroutine for InstructionHandle found (DEAD CODE?).");
569   }
570 
571   /**
572    * For easy handling, the piece of code that is <B>not</B> a
573    * subroutine, the top-level, is also modeled as a Subroutine
574    * object.
575    * It is a special Subroutine object where <B>you must not invoke
576    * getEnteringJsrInstructions() or getLeavingRET()</B>.
577    *
578    * @see Subroutine#getEnteringJsrInstructions()
579    * @see Subroutine#getLeavingRET()
580    */
581   public Subroutine getTopLevel(){
582     return TOPLEVEL;
583   }
584   /**
585    * A utility method that calculates the successors of a given InstructionHandle
586    * <B>in the same subroutine</B>. That means, a RET does not have any successors
587    * as defined here. A JsrInstruction has its physical successor as its successor
588    * (opposed to its target) as defined here.
589    */
590   private static InstructionHandle[] getSuccessors(InstructionHandle instruction){
591     final InstructionHandle[] empty = new InstructionHandle[0];
592     final InstructionHandle[] single = new InstructionHandle[1];
593     final InstructionHandle[] pair = new InstructionHandle[2];
594     
595     Instruction inst = instruction.getInstruction();
596     
597     if (inst instanceof RET){
598       return empty;
599     }
600     
601     // Terminates method normally.
602     if (inst instanceof ReturnInstruction){
603       return empty;
604     }
605     
606     // Terminates method abnormally, because JustIce mandates
607     // subroutines not to be protected by exception handlers.
608     if (inst instanceof ATHROW){
609       return empty;
610     }
611     
612     // See method comment.
613     if (inst instanceof JsrInstruction){
614       single[0] = instruction.getNext();
615       return single;
616     }
617 
618     if (inst instanceof GotoInstruction){
619       single[0] = ((GotoInstruction) inst).getTarget();
620       return single;
621     }
622 
623     if (inst instanceof BranchInstruction){
624       if (inst instanceof Select){
625         // BCEL's getTargets() returns only the non-default targets,
626         // thanks to Eli Tilevich for reporting.
627         InstructionHandle[] matchTargets = ((Select) inst).getTargets();
628         InstructionHandle[] ret = new InstructionHandle[matchTargets.length+1];
629         ret[0] = ((Select) inst).getTarget();
630         System.arraycopy(matchTargets, 0, ret, 1, matchTargets.length);
631         return ret;
632       }
633       else{
634         pair[0] = instruction.getNext();
635         pair[1] = ((BranchInstruction) inst).getTarget();
636         return pair;
637       }
638     }
639 
640     // default case: Fall through.    
641     single[0] = instruction.getNext();
642     return single;
643   }
644 
645   /**
646    * Returns a String representation of this object; merely for debugging puposes.
647    */
648   public String toString(){
649     return "---\n"+subroutines.toString()+"\n---\n";
650   }
651 }