KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > polyglot > visit > DataFlow


1 package polyglot.visit;
2
3 import java.util.*;
4
5 import polyglot.ast.*;
6 import polyglot.frontend.Job;
7 import polyglot.main.Report;
8 import polyglot.types.SemanticException;
9 import polyglot.types.Type;
10 import polyglot.types.TypeSystem;
11 import polyglot.util.IdentityKey;
12 import polyglot.util.InternalCompilerError;
13 import polyglot.util.StringUtil;
14 import polyglot.visit.FlowGraph.Edge;
15 import polyglot.visit.FlowGraph.EdgeKey;
16 import polyglot.visit.FlowGraph.ExceptionEdgeKey;
17 import polyglot.visit.FlowGraph.Peer;
18
19 /**
20  * Abstract dataflow Visitor, to allow simple dataflow equations to be easily
21  * implemented.
22  */

23 public abstract class DataFlow extends ErrorHandlingVisitor
24 {
25     /**
26      * Indicates whether this dataflow is a forward analysis.
27      */

28     protected final boolean forward;
29     
30     /**
31      * Indicates whether the dataflow should be performed on entering a
32      * <code>CodeDecl</code>, or on leaving a <code>CodeDecl</code>.
33      * If dataflow is performed on entry, then the control flow graph
34      * will be available when visiting children of the
35      * <code>CodeDecl</code>, via the <code>currentFlowGraph</code>
36      * method. If dataflow is performed on leaving, then the control
37      * flow graph will not be available, but nested
38      * <code>CodeDecl</code>s will have already been processed.
39      */

40     protected final boolean dataflowOnEntry;
41     
42     /**
43      * A stack of <code>FlowGraphSource</code>. The flow graph is constructed
44      * upon entering a CodeDecl AST node, and dataflow performed on that flow
45      * graph immediately. The flow graph is available during the visiting of
46      * children of the CodeDecl, if subclasses want to use this information
47      * to update AST nodes. The stack is only maintained if
48      * <code>dataflowOnEntry</code> is true.
49      */

50     protected LinkedList flowgraphStack;
51     
52     protected static class FlowGraphSource {
53         FlowGraphSource(FlowGraph g, CodeDecl s) {
54             flowgraph = g;
55             source = s;
56         }
57         FlowGraph flowgraph;
58         CodeDecl source;
59         public FlowGraph flowGraph() { return flowgraph; }
60         public CodeDecl source() { return source; }
61     }
62     
63     /**
64      * Constructor.
65      */

66     public DataFlow(Job job, TypeSystem ts, NodeFactory nf, boolean forward) {
67         this(job, ts, nf, forward, false);
68     }
69
70     /**
71      * Constructor.
72      */

73     public DataFlow(Job job,
74                     TypeSystem ts,
75                     NodeFactory nf,
76                     boolean forward,
77                     boolean dataflowOnEntry) {
78         super(job, ts, nf);
79         this.forward = forward;
80         this.dataflowOnEntry = dataflowOnEntry;
81         if (dataflowOnEntry)
82             this.flowgraphStack = new LinkedList();
83         else
84             this.flowgraphStack = null;
85     }
86
87     /**
88      * An <code>Item</code> contains the data which flows during the dataflow
89      * analysis. Each
90      * node in the flow graph will have two items associated with it: the input
91      * item, and the output item, which results from calling flow with the
92      * input item. The input item may itself be the result of a call to the
93      * confluence method, if many paths flow into the same node.
94      *
95      * NOTE: the <code>equals(Item)</code> method and <code>hashCode()</code>
96      * method must be implemented to ensure that the dataflow algorithm works
97      * correctly.
98      */

99     public static abstract class Item {
100         public abstract boolean equals(Object JavaDoc i);
101         public abstract int hashCode();
102     }
103
104     /**
105      * Create an initial Item for the term node. This is generally how the Item that will be given
106      * to the start node of a graph is created, although this method may also be called for other
107      * (non-start) nodes.
108      *
109      * @return a (possibly null) Item.
110      */

111     protected abstract Item createInitialItem(FlowGraph graph, Term node);
112     
113     /**
114      * Produce new <code>Item</code>s as appropriate for the
115      * <code>Term n</code> and the input <code>Item in</code>.
116      *
117      * @param in the Item flowing into the node. Note that if the Term n
118      * has many flows going into it, the Item in may be the result
119      * of a call to confluence(List, List, Term)
120      * @param graph the FlowGraph which the dataflow is operating on
121      * @param n the Term which this method must calculate the flow for.
122      * @param edgeKeys a set of FlowGraph.EdgeKeys, being all the
123      * EdgeKeys of the edges leaving this node. The
124      * returned Map must have mappings for all objects in this set.
125      * @return a Map from FlowGraph.EdgeKeys to Items. The map must have
126      * entries for all EdgeKeys in edgeKeys.
127      */

128     protected Map flow(Item in, FlowGraph graph, Term n, Set edgeKeys) {
129         throw new InternalCompilerError("Unimplemented: should be " +
130                                         "implemented by subclasses if " +
131                                         "needed");
132     }
133     
134     /**
135      * Produce new <code>Item</code>s as appropriate for the
136      * <code>Term n</code> and the input <code>Item</code>s. The default
137      * implementation of this method is simply to call <code>confluence</code>
138      * for the list of inItems, and pass the result to flow(Item, FlowGraph,
139      * Term, Set). Subclasses may want to override this method if a finer grain
140      * dataflow is required. Some subclasses may wish to override this method
141      * to call <code>flowToBooleanFlow</code>.
142      *
143      * @param inItems all the Items flowing into the node.
144      * @param inItemKeys the FlowGraph.EdgeKeys for the items in the list inItems
145      * @param graph the FlowGraph which the dataflow is operating on
146      * @param n the Term which this method must calculate the flow for.
147      * @param edgeKeys a set of FlowGraph.EdgeKeys, being all the
148      * EdgeKeys of the edges leaving this node. The
149      * returned Map must have mappings for all objects in this set.
150      * @return a Map from FlowGraph.EdgeKeys to Items. The map must have
151      * entries for all EdgeKeys in edgeKeys.
152      */

153     protected Map flow(List inItems, List inItemKeys, FlowGraph graph, Term n, Set edgeKeys) {
154         Item inItem = this.safeConfluence(inItems, inItemKeys, n, graph);
155         
156         return this.flow(inItem, graph, n, edgeKeys);
157     }
158
159         
160     
161
162     /**
163      * A utility method that simply collects together all the
164      * TRUE items, FALSE items, and all other items (including ExceptionEdgeKey
165      * items), calls <code>confluence</code> on each of these three collections
166      * as neccessary, and passes the results to
167      * flow(Item, Item, Item, FlowGraph, Term, Set). It is expected that
168      * this method will typically be called by subclasses overriding the
169      * flow(List, List, FlowGraph, Term, Set) method, due to the need for
170      * a finer grain dataflow analysis.
171      *
172      * @param inItems all the Items flowing into the node.
173      * @param inItemKeys the FlowGraph.EdgeKeys for the items in the list inItems
174      * @param graph the FlowGraph which the dataflow is operating on
175      * @param n the Term which this method must calculate the flow for.
176      * @param edgeKeys a set of FlowGraph.EdgeKeys, being all the
177      * EdgeKeys of the edges leaving this node. The
178      * returned Map must have mappings for all objects in this set.
179      * @return a Map from FlowGraph.EdgeKeys to Items. The map must have
180      * entries for all EdgeKeys in edgeKeys.
181      */

182     protected final Map flowToBooleanFlow(List inItems, List inItemKeys, FlowGraph graph, Term n, Set edgeKeys) {
183         List trueItems = new ArrayList();
184         List trueItemKeys = new ArrayList();
185         List falseItems = new ArrayList();
186         List falseItemKeys = new ArrayList();
187         List otherItems = new ArrayList();
188         List otherItemKeys = new ArrayList();
189         
190         Iterator i = inItems.iterator();
191         Iterator j = inItemKeys.iterator();
192         while (i.hasNext() || j.hasNext()) {
193             Item item = (Item)i.next();
194             EdgeKey key = (EdgeKey)j.next();
195             
196             if (FlowGraph.EDGE_KEY_TRUE.equals(key)) {
197                 trueItems.add(item);
198                 trueItemKeys.add(key);
199             }
200             else if (FlowGraph.EDGE_KEY_FALSE.equals(key)) {
201                 falseItems.add(item);
202                 falseItemKeys.add(key);
203             }
204             else {
205                 otherItems.add(item);
206                 otherItemKeys.add(key);
207             }
208         }
209         
210         Item trueItem = trueItems.isEmpty() ? null : this.safeConfluence(trueItems, trueItemKeys, n, graph);
211         Item falseItem = falseItems.isEmpty() ? null : this.safeConfluence(falseItems, falseItemKeys, n, graph);
212         Item otherItem = otherItems.isEmpty() ? null : this.safeConfluence(otherItems, otherItemKeys, n, graph);
213
214         return this.flow(trueItem, falseItem, otherItem, graph, n, edgeKeys);
215     }
216
217     protected Map flow(Item trueItem, Item falseItem, Item otherItem,
218                        FlowGraph graph, Term n, Set edgeKeys) {
219        throw new InternalCompilerError("Unimplemented: should be " +
220                                        "implemented by subclasses if " +
221                                        "needed");
222     }
223     
224     /**
225      *
226      * @param trueItem The item for flows coming into n for true conditions. Cannot be null.
227      * @param falseItem The item for flows coming into n for false conditions. Cannot be null.
228      * @param otherItem The item for all other flows coming into n
229      * @param n The boolean expression.
230      * @param edgeKeys The outgoing edges
231      */

232     protected Map flowBooleanConditions(Item trueItem, Item falseItem, Item otherItem,
233                                         FlowGraph graph, Expr n, Set edgeKeys) {
234         if (!n.type().isBoolean() || !(n instanceof Binary || n instanceof Unary)) {
235             throw new InternalCompilerError("This method only takes binary " +
236                       "or unary operators of boolean type");
237         }
238         
239         if (trueItem == null || falseItem == null) {
240             throw new IllegalArgumentException JavaDoc("The trueItem and falseItem " +
241                                   "for flowBooleanConditions must be non-null.");
242         }
243         
244         if (n instanceof Unary) {
245             Unary u = (Unary)n;
246             if (u.operator() == Unary.NOT) {
247                 return itemsToMap(falseItem, trueItem, otherItem, edgeKeys);
248             }
249         }
250         else {
251             Binary b = (Binary)n;
252             if (b.operator() == Binary.COND_AND) {
253                 // the only true item coming into this node should be
254
// if the second operand was true.
255
return itemsToMap(trueItem, falseItem, otherItem, edgeKeys);
256             }
257             else if (b.operator() == Binary.COND_OR) {
258                 // the only false item coming into this node should be
259
// if the second operand was false.
260
return itemsToMap(trueItem, falseItem, otherItem, edgeKeys);
261             }
262             else if (b.operator() == Binary.BIT_AND) {
263                 // there is both a true and a false item coming into this node,
264
// from the second operand. However, this operator could be false
265
// if either the first or the second argument returned false.
266
Item bitANDFalse =
267                      this.safeConfluence(trueItem, FlowGraph.EDGE_KEY_TRUE,
268                                          falseItem, FlowGraph.EDGE_KEY_FALSE,
269                                          n, graph);
270                 return itemsToMap(trueItem, bitANDFalse, otherItem, edgeKeys);
271             }
272             else if (b.operator() == Binary.BIT_OR) {
273                 // there is both a true and a false item coming into this node,
274
// from the second operand. However, this operator could be true
275
// if either the first or the second argument returned true.
276
Item bitORTrue =
277                     this.safeConfluence(trueItem, FlowGraph.EDGE_KEY_TRUE,
278                                         falseItem, FlowGraph.EDGE_KEY_FALSE,
279                                         n, graph);
280                 return itemsToMap(bitORTrue, falseItem, otherItem, edgeKeys);
281             }
282         }
283         return null;
284     }
285     
286     /**
287      * The confluence operator for many flows. This method produces a single
288      * Item from a List of Items, for the confluence just before flow enters
289      * node.
290      *
291      * @param items List of <code>Item</code>s that flow into <code>node</code>.
292      * this method will only be called if the list has at least 2
293      * elements.
294      * @param node <code>Term</code> for which the <code>items</code> are
295      * flowing into.
296      * @return a non-null Item.
297      */

298     protected abstract Item confluence(List items, Term node, FlowGraph graph);
299     
300     /**
301      * The confluence operator for many flows. This method produces a single
302      * Item from a List of Items, for the confluence just before flow enters
303      * node.
304      *
305      * @param items List of <code>Item</code>s that flow into <code>node</code>.
306      * This method will only be called if the list has at least 2
307      * elements.
308      * @param itemKeys List of <code>FlowGraph.ExceptionEdgeKey</code>s for
309      * the edges that the corresponding <code>Item</code>s in
310      * <code>items</code> flowed from.
311      * @param node <code>Term</code> for which the <code>items</code> are
312      * flowing into.
313      * @return a non-null Item.
314      */

315     protected Item confluence(List items, List itemKeys, Term node, FlowGraph graph) {
316         return confluence(items, node, graph);
317     }
318     
319     /**
320      * The confluence operator for many flows. This method produces a single
321      * Item from a List of Items, for the confluence just before flow enters
322      * node.
323      *
324      * @param items List of <code>Item</code>s that flow into <code>node</code>.
325      * This method will only be called if the list has at least 2
326      * elements.
327      * @param itemKeys List of <code>FlowGraph.ExceptionEdgeKey</code>s for
328      * the edges that the corresponding <code>Item</code>s in
329      * <code>items</code> flowed from.
330      * @param node <code>Term</code> for which the <code>items</code> are
331      * flowing into.
332      * @return a non-null Item.
333      */

334     protected Item safeConfluence(List items, List itemKeys, Term node, FlowGraph graph) {
335         if (items.isEmpty()) {
336             return this.createInitialItem(graph, node);
337         }
338         if (items.size() == 1) {
339             return (Item)items.get(0);
340         }
341         return confluence(items, itemKeys, node, graph);
342     }
343
344     protected Item safeConfluence(Item item1, FlowGraph.EdgeKey key1,
345                                   Item item2, FlowGraph.EdgeKey key2,
346                                   Term node, FlowGraph graph) {
347         return safeConfluence(item1, key1, item2, key2, null, null, node, graph);
348     }
349                                   
350     protected Item safeConfluence(Item item1, FlowGraph.EdgeKey key1,
351                                   Item item2, FlowGraph.EdgeKey key2,
352                                   Item item3, FlowGraph.EdgeKey key3,
353                                   Term node, FlowGraph graph) {
354         List items = new ArrayList(3);
355         List itemKeys = new ArrayList(3);
356         
357         if (item1 != null) {
358             items.add(item1);
359             itemKeys.add(key1);
360         }
361         if (item2 != null) {
362             items.add(item2);
363             itemKeys.add(key2);
364         }
365         if (item3 != null) {
366             items.add(item3);
367             itemKeys.add(key3);
368         }
369         return safeConfluence(items, itemKeys, node, graph);
370     }
371     
372     /**
373      * Check that the term n satisfies whatever properties this
374      * dataflow is checking for. This method is called for each term
375      * in a code declaration block after the dataflow for that block of code
376      * has been performed.
377      *
378      * @throws <code>SemanticException</code> if the properties this dataflow
379      * analysis is checking for is not satisfied.
380      */

381     protected abstract void check(FlowGraph graph, Term n, Item inItem, Map outItems) throws SemanticException;
382
383     /**
384      * Construct a flow graph for the <code>CodeDecl</code> provided, and call
385      * <code>dataflow(FlowGraph)</code>. Is also responsible for calling
386      * <code>post(FlowGraph, Block)</code> after
387      * <code>dataflow(FlowGraph)</code> has been called, and for pushing
388      * the <code>FlowGraph</code> onto the stack of <code>FlowGraph</code>s if
389      * dataflow analysis is performed on entry to <code>CodeDecl</code> nodes.
390      */

391     protected void dataflow(CodeDecl cd) throws SemanticException {
392         // only bother to do the flow analysis if the body is not null...
393
if (cd.body() != null) {
394             // Compute the successor of each child node.
395
FlowGraph g = initGraph(cd, cd);
396
397             if (g != null) {
398                 // Build the control flow graph.
399
CFGBuilder v = createCFGBuilder(ts, g);
400
401                 try {
402                     v.visitGraph();
403                 }
404                 catch (CFGBuildError e) {
405                     throw new SemanticException(e.message(), e.position());
406                 }
407
408                 dataflow(g);
409
410                 post(g, cd);
411
412                 // push the CFG onto the stack if we are dataflowing on entry
413
if (dataflowOnEntry)
414                     flowgraphStack.addFirst(new FlowGraphSource(g, cd));
415             }
416         }
417     }
418
419     /** A "stack frame" for recursive DFS */
420     static private class Frame {
421     Peer peer;
422     Iterator edges;
423     Frame(Peer p, boolean forward) {
424         peer = p;
425         if (forward) edges = p.succs().iterator();
426         else edges = p.preds().iterator();
427     }
428     }
429
430     /** Returns the linked list [by_scc, scc_head] where
431      * by_scc is an array in which SCCs occur in topologically
432      * order.
433      * scc_head[n] where n is the first peer in an SCC is set to -1.
434      * scc_head[n] where n is the last peer in a (non-singleton) SCC is set
435      * to the index of the first peer. Otherwise it is -2. */

436     protected LinkedList findSCCs(FlowGraph graph) {
437     Collection peers = graph.peers();
438     Peer[] sorted = new Peer[peers.size()];
439         Collection start = graph.peers(graph.startNode());
440       // if start == peers, making all nodes reachable,
441
// the problem still arises.
442

443     //System.out.println("scc: npeers = " + peers.size());
444

445 // First, topologically sort the nodes (put in postorder)
446
int n = 0;
447     LinkedList stack = new LinkedList();
448     Set reachable = new HashSet();
449     for (Iterator i = start.iterator(); i.hasNext(); ) {
450       Peer peer = (Peer)i.next();
451       if (!reachable.contains(peer)) {
452         reachable.add(peer);
453         stack.addFirst(new Frame(peer, true));
454         while (stack.size() != 0) {
455         Frame top = (Frame)stack.getFirst();
456         if (top.edges.hasNext()) {
457             Edge e = (Edge)top.edges.next();
458             Peer q = e.getTarget();
459             if (!reachable.contains(q)) {
460             reachable.add(q);
461             stack.addFirst(new Frame(q, true));
462             }
463         } else {
464             stack.removeFirst();
465             sorted[n++] = top.peer;
466         }
467         }
468       }
469     }
470     //System.out.println("scc: reached " + n);
471
// Now, walk the transposed graph picking nodes in reverse
472
// postorder, thus picking out one SCC at a time and
473
// appending it to "by_scc".
474
Peer[] by_scc = new Peer[n];
475     int[] scc_head = new int[n];
476     Set visited = new HashSet();
477     int head = 0;
478     for (int i=n-1; i>=0; i--) {
479         if (!visited.contains(sorted[i])) {
480         // First, find all the nodes in the SCC
481
Set SCC = new HashSet();
482         visited.add(sorted[i]);
483         stack.add(new Frame(sorted[i], false));
484         while (stack.size() != 0) {
485             Frame top = (Frame)stack.getFirst();
486             if (top.edges.hasNext()) {
487             Edge e = (Edge)top.edges.next();
488             Peer q = e.getTarget();
489             if (reachable.contains(q) && !visited.contains(q)) {
490                 visited.add(q);
491                 Frame f = new Frame(q, false);
492                 stack.addFirst(f);
493             }
494             } else {
495             stack.removeFirst();
496             SCC.add(top.peer);
497             }
498         }
499         // Now, topologically sort the SCC (as much as possible)
500
// and place into by_scc[head..head+scc_size-1]
501
stack.add(new Frame(sorted[i], true));
502         Set revisited = new HashSet();
503         revisited.add(sorted[i]);
504         int scc_size = SCC.size();
505         int nsorted = 0;
506         while (stack.size() != 0) {
507             Frame top = (Frame)stack.getFirst();
508             if (top.edges.hasNext()) {
509             Edge e = (Edge)top.edges.next();
510             Peer q = e.getTarget();
511             if (SCC.contains(q) && !revisited.contains(q)) {
512                 revisited.add(q);
513                 Frame f = new Frame(q, true);
514                 stack.addFirst(f);
515             }
516             } else {
517             stack.removeFirst();
518             int n3 = head + scc_size - nsorted - 1;
519             scc_head[n3] = -2;
520             by_scc[n3] = top.peer;
521             nsorted++;
522             }
523         }
524         scc_head[head+scc_size-1] = head;
525         scc_head[head] = -1;
526         head = head + scc_size;
527         }
528     }
529     if (Report.should_report(Report.dataflow, 2)) {
530         for (int j = 0; j < n; j++) {
531         switch(scc_head[j]) {
532             case -1: Report.report(2, j + "[HEAD] : " + by_scc[j]); break;
533             case -2: Report.report(2, j + " : " + by_scc[j]); break;
534             default: Report.report(2, j + " ->"+ scc_head[j] + " : " + by_scc[j]);
535         }
536         for (Iterator i = by_scc[j].succs().iterator(); i.hasNext(); ) {
537             Report.report(3, " successor: " + ((Edge)i.next()).getTarget());
538         }
539         }
540     }
541     LinkedList ret = new LinkedList();
542     ret.addFirst(scc_head);
543     ret.addFirst(by_scc);
544     return ret;
545     }
546
547     /**
548      * Perform the dataflow on the flowgraph provided.
549      */

550     protected void dataflow(FlowGraph graph) {
551     if (Report.should_report(Report.dataflow, 1)) {
552         Report.report(1, "Finding strongly connected components");
553     }
554     LinkedList pair = findSCCs(graph);
555     Peer[] by_scc = (Peer[])pair.getFirst();
556     int[] scc_head = (int[])pair.getLast();
557     int npeers = by_scc.length;
558
559     /* by_scc contains the peers grouped by SCC.
560        scc_head marks where the SCCs are. The SCC
561        begins with a -1 and ends with the index of
562        the beginning of the SCC.
563     */

564     if (Report.should_report(Report.dataflow, 1)) {
565         Report.report(1, "Iterating dataflow equations");
566     }
567
568     int current = 0;
569     boolean change = false;
570
571     while (current < npeers) {
572             Peer p = by_scc[current];
573         if (scc_head[current] == -1) {
574         change = false; // just started working on a new SCC
575
}
576
577             // get the in items by examining the out items of all
578
// the predecessors of p
579
List inItems = new ArrayList(p.preds.size());
580             List inItemKeys = new ArrayList(p.preds.size());
581             for (Iterator i = p.preds.iterator(); i.hasNext(); ) {
582                 Edge e = (Edge)i.next();
583                 Peer o = e.getTarget();
584                 if (o.outItems != null) {
585                     if (!o.outItems.keySet().contains(e.getKey())) {
586                         throw new InternalCompilerError("There should have " +
587                                 "an out Item with edge key " + e.getKey() +
588                                 "; instead there were only " +
589                                 o.outItems.keySet());
590                     }
591                     Item it = (Item)o.outItems.get(e.getKey());
592                     if (it != null) {
593                         inItems.add(it);
594                         inItemKeys.add(e.getKey());
595                     }
596                 }
597             }
598                 
599             // calculate the out item
600
Map oldOutItems = p.outItems;
601             p.inItem = this.safeConfluence(inItems, inItemKeys, p.node, graph);
602             p.outItems = this.flow(inItems, inItemKeys, graph, p.node, p.succEdgeKeys());
603                                 
604             if (!p.succEdgeKeys().equals(p.outItems.keySet())) {
605                 // This check is more for developers to ensure that they
606
// have implemented their dataflow correctly. If performance
607
// is an issue, maybe we should remove this check.
608
throw new InternalCompilerError("The flow only defined " +
609                         "outputs for " + p.outItems.keySet() + "; needs to " +
610                         "define outputs for all of: " + p.succEdgeKeys());
611             }
612
613             if (oldOutItems != p.outItems &&
614                  (oldOutItems == null || !oldOutItems.equals(p.outItems))) {
615                 // the outItems of p has changed, so we will
616
// loop when we get to the end of the current SCC.
617
change = true;
618             }
619         if (change && scc_head[current] >= 0) {
620         current = scc_head[current]; // loop!
621
/* now scc_head[current] == -1 */
622         } else {
623         current++;
624         }
625         }
626     if (Report.should_report(Report.dataflow, 1)) {
627         Report.report(1, "Done.");
628     }
629     }
630
631     /**
632      * Initialise the <code>FlowGraph</code> to be used in the dataflow
633      * analysis.
634      *
635      * @return null if no dataflow analysis should be performed for this
636      * code declaration; otherwise, an apropriately initialized
637      * <code>FlowGraph.</code>
638      */

639     protected FlowGraph initGraph(CodeDecl code, Term root) {
640         return new FlowGraph(root, forward);
641     }
642
643     /**
644      * Construct a CFGBuilder.
645      *
646      * @param ts The type system
647      * @param g The flow graph to that the CFGBuilder will construct.
648      * @return a new CFGBuilder
649      */

650     protected CFGBuilder createCFGBuilder(TypeSystem ts, FlowGraph g) {
651         return new CFGBuilder(ts, g, this);
652     }
653
654     /**
655      * Overridden superclass method, to build the flow graph, perform dataflow
656      * analysis, and check the analysis for CodeDecl nodes.
657      */

658     protected NodeVisitor enterCall(Node n) throws SemanticException {
659         if (dataflowOnEntry && n instanceof CodeDecl) {
660             dataflow((CodeDecl)n);
661         }
662         
663         return this;
664     }
665
666     /**
667      * Overridden superclass method, to make sure that if a subclass has changed
668      * a Term, that we update the peermaps appropriately, since they are based
669      * on <code>IdentityKey</code>s.
670      */

671     public Node leave(Node parent, Node old, Node n, NodeVisitor v) {
672         if (old != n) {
673             if (dataflowOnEntry && currentFlowGraph() != null) {
674                 // We currently only update the key in the peerMap.
675
// We DO NOT update the Terms inside the peers, nor the
676
// List of Terms that are the path maps.
677
Object JavaDoc o = currentFlowGraph().peerMap.get(new IdentityKey(old));
678                 if (o != null) {
679                     currentFlowGraph().peerMap.put(new IdentityKey(n), o);
680                 }
681             }
682         }
683         return super.leave(parent, old, n, v);
684     }
685
686     /**
687      * Overridden superclass method, to pop from the stack of
688      * <code>FlowGraph</code>s if necessary.
689      */

690     protected Node leaveCall(Node n) throws SemanticException {
691         if (n instanceof CodeDecl) {
692             if (!dataflowOnEntry) {
693                 dataflow((CodeDecl)n);
694             }
695             else if (dataflowOnEntry && !flowgraphStack.isEmpty()) {
696                 FlowGraphSource fgs = (FlowGraphSource)flowgraphStack.getFirst();
697                 if (fgs.source.equals(n)) {
698                     // we are leaving the code decl that pushed this flowgraph
699
// on the stack. pop tbe stack.
700
flowgraphStack.removeFirst();
701                 }
702             }
703         }
704         return n;
705     }
706
707     /**
708      * Check all of the Peers in the graph, after the dataflow analysis has
709      * been performed.
710      */

711     protected void post(FlowGraph graph, Term root) throws SemanticException {
712         if (Report.should_report(Report.cfg, 2)) {
713             dumpFlowGraph(graph, root);
714         }
715         
716         // Check the nodes in approximately flow order.
717
Set uncheckedPeers = new HashSet(graph.peers());
718         LinkedList peersToCheck = new LinkedList(graph.peers(graph.startNode()));
719         while (!peersToCheck.isEmpty()) {
720             Peer p = (Peer) peersToCheck.removeFirst();
721             uncheckedPeers.remove(p);
722
723             this.check(graph, p.node, p.inItem, p.outItems);
724             
725             for (Iterator iter = p.succs.iterator(); iter.hasNext(); ) {
726                 Peer q = ((Edge)iter.next()).getTarget();
727                 if (uncheckedPeers.contains(q) && !peersToCheck.contains(q)) {
728                     // q hasn't been checked yet.
729
peersToCheck.addLast(q);
730                 }
731             }
732             
733             if (peersToCheck.isEmpty() && !uncheckedPeers.isEmpty()) {
734                 // done all the we can reach...
735
Iterator i = uncheckedPeers.iterator();
736                 peersToCheck.add(i.next());
737                 i.remove();
738             }
739             
740         }
741     }
742     
743     /**
744      * Return the <code>FlowGraph</code> at the top of the stack. This method
745      * should not be called if dataflow is not being performed on entry to
746      * the <code>CodeDecl</code>s, as the stack is not maintained in that case.
747      * If this
748      * method is called by a subclass from the <code>enterCall</code>
749      * or <code>leaveCall</code> methods, for an AST node that is a child
750      * of a <code>CodeDecl</code>, then the <code>FlowGraph</code> returned
751      * should be the <code>FlowGraph</code> for the dataflow for innermost
752      * <code>CodeDecl</code>.
753      */

754     protected FlowGraph currentFlowGraph() {
755         if (!dataflowOnEntry) {
756             throw new InternalCompilerError("currentFlowGraph() cannot be" +
757                 " called when dataflow is not performed on entry");
758         }
759         if (flowgraphStack.isEmpty()) {
760             return null;
761         }
762         return ((FlowGraphSource)flowgraphStack.getFirst()).flowgraph;
763     }
764     
765     /**
766      * This utility methods is for subclasses to convert a single Item into
767      * a <code>Map</code>, to return from the
768      * <code>flow</code> methods. This
769      * method should be used when the same output <code>Item</code> from the
770      * flow is to be used for all edges leaving the node.
771      *
772      * @param i the <code>Item</code> to be placed in the returned
773      * <code>Map</code> as the value for every <code>EdgeKey</code> in
774      * <code>edgeKeys.</code>
775      * @param edgeKeys the <code>Set</code> of <code>EdgeKey</code>s to be used
776      * as keys in the returned <code>Map</code>.
777      * @return a <code>Map</code> containing a mapping from every
778      * <code>EdgeKey</code> in <code>edgeKeys</code> to the
779      * <code>Item i</code>.
780      */

781     protected static final Map itemToMap(Item i, Set edgeKeys) {
782         Map m = new HashMap();
783         for (Iterator iter = edgeKeys.iterator(); iter.hasNext(); ) {
784             Object JavaDoc o = iter.next();
785             m.put(o, i);
786         }
787         return m;
788     }
789
790     /**
791      * This utility methods is for subclasses to convert a Items into
792      * a <code>Map</code>, to return from the
793      * <code>flow</code> methods.
794      *
795      * @param trueItem the <code>Item</code> to be placed in the returned
796      * <code>Map</code> as the value for the
797      * <code>FlowGraph.EDGE_KEY_TRUE</code>, if that key is present in
798      * <code>edgeKeys.</code>
799      * @param falseItem the <code>Item</code> to be placed in the returned
800      * <code>Map</code> as the value for the
801      * <code>FlowGraph.EDGE_KEY_FALSE</code>, if that key is present in
802      * <code>edgeKeys.</code>
803      * @param remainingItem the <code>Item</code> to be placed in the returned
804      * <code>Map</code> as the value for any edge key other than
805      * <code>FlowGraph.EDGE_KEY_TRUE</code> or
806      * <code>FlowGraph.EDGE_KEY_FALSE</code>, if any happen to be
807      * present in
808      * <code>edgeKeys.</code>
809      * @param edgeKeys the <code>Set</code> of <code>EdgeKey</code>s to be used
810      * as keys in the returned <code>Map</code>.
811      * @return a <code>Map</code> containing a mapping from every
812      * <code>EdgeKey</code> in <code>edgeKeys</code> to the
813      * <code>Item i</code>.
814      */

815     protected static final Map itemsToMap(Item trueItem, Item falseItem, Item remainingItem, Set edgeKeys) {
816         Map m = new HashMap();
817         
818         for (Iterator iter = edgeKeys.iterator(); iter.hasNext(); ) {
819             FlowGraph.EdgeKey k = (EdgeKey)iter.next();
820             if (FlowGraph.EDGE_KEY_TRUE.equals(k)) {
821                 m.put(k, trueItem);
822             }
823             else if (FlowGraph.EDGE_KEY_FALSE.equals(k)) {
824                 m.put(k, falseItem);
825             }
826             else {
827                 m.put(k, remainingItem);
828             }
829         }
830         return m;
831     }
832
833     /**
834      * Filter a list of <code>Item</code>s to contain only <code>Item</code>s
835      * that are not associated with error flows, that is, only
836      * <code>Item</code>s whose associated <code>EdgeKey</code>s are not
837      * <code>FlowGraph.ExceptionEdgeKey</code>s with a type that is a subclass
838      * of <code>TypeSystem.Error()</code>.
839      *
840      * @param items List of Items to filter
841      * @param itemKeys List of <code>EdgeKey</code>s corresponding
842      * to the edge keys for the <code>Item</code>s in <code>items</code>.
843      * @return a filtered list of items, containing only those whose edge keys
844      * are not <code>FlowGraph.ExceptionEdgeKey</code>s with
845      * whose exception types are <code>Error</code>s.
846      */

847     protected final List filterItemsNonError(List items, List itemKeys) {
848         List filtered = new ArrayList(items.size());
849         Iterator i = items.iterator();
850         Iterator j = itemKeys.iterator();
851         while (i.hasNext() && j.hasNext()) {
852             Item item = (Item)i.next();
853             EdgeKey key = (EdgeKey)j.next();
854             
855             if (!(key instanceof ExceptionEdgeKey &&
856                ((ExceptionEdgeKey)key).type().isSubtype(ts.Error()))) {
857                 // the key is not an error edge key.
858
filtered.add(item);
859             }
860         }
861         
862         if (i.hasNext() || j.hasNext()) {
863             throw new InternalCompilerError("item and item key lists " +
864                                             "have different sizes.");
865         }
866         
867         return filtered;
868     }
869     
870     /**
871      * Filter a list of <code>Item</code>s to contain only <code>Item</code>s
872      * that are not associated with exception flows, that is, only
873      * <code>Item</code>s whose associated <code>EdgeKey</code>s are not
874      * <code>FlowGraph.ExceptionEdgeKey</code>s.
875      *
876      * @param items List of Items to filter
877      * @param itemKeys List of <code>EdgeKey</code>s corresponding
878      * to the edge keys for the <code>Item</code>s in <code>items</code>.
879      * @return a filtered list of items, containing only those whose edge keys
880      * are not <code>FlowGraph.ExceptionEdgeKey</code>s.
881      */

882     protected final List filterItemsNonException(List items, List itemKeys) {
883         List filtered = new ArrayList(items.size());
884         Iterator i = items.iterator();
885         Iterator j = itemKeys.iterator();
886         while (i.hasNext() && j.hasNext()) {
887             Item item = (Item)i.next();
888             EdgeKey key = (EdgeKey)j.next();
889             
890             if (!(key instanceof ExceptionEdgeKey)) {
891                 // the key is not an exception edge key.
892
filtered.add(item);
893             }
894         }
895         
896         if (i.hasNext() || j.hasNext()) {
897             throw new InternalCompilerError("item and item key lists " +
898                                             "have different sizes.");
899         }
900         
901         return filtered;
902     }
903  
904     /**
905      * Filter a list of <code>Item</code>s to contain only <code>Item</code>s
906      * that are associated with exception flows, whose exception is a subclass
907      * of <code>excType</code>. That is, only
908      * <code>Item</code>s whose associated <code>EdgeKey</code>s are
909      * <code>FlowGraph.ExceptionEdgeKey</code>s, with the type a subclass
910      * of <code>excType</code>.
911      *
912      * @param items List of Items to filter
913      * @param itemKeys List of <code>EdgeKey</code>s corresponding
914      * to the edge keys for the <code>Item</code>s in <code>items</code>.
915      * @param excType an Exception <code>Type</code>.
916      * @return a filtered list of items, containing only those whose edge keys
917      * are not <code>FlowGraph.ExceptionEdgeKey</code>s.
918      */

919     protected final List filterItemsExceptionSubclass(List items, List itemKeys, Type excType) {
920         List filtered = new ArrayList(items.size());
921         Iterator i = items.iterator();
922         Iterator j = itemKeys.iterator();
923         while (i.hasNext() && j.hasNext()) {
924             Item item = (Item)i.next();
925             EdgeKey key = (EdgeKey)j.next();
926             
927             if (key instanceof ExceptionEdgeKey) {
928                 // the key is an exception edge key.
929
ExceptionEdgeKey eek = (ExceptionEdgeKey)key;
930                 if (eek.type().isImplicitCastValid(excType)) {
931                     filtered.add(item);
932                 }
933             }
934         }
935         
936         if (i.hasNext() || j.hasNext()) {
937             throw new InternalCompilerError("item and item key lists " +
938                                             "have different sizes.");
939         }
940         
941         return filtered;
942     }
943  
944     /**
945      * Filter a list of <code>Item</code>s to contain only <code>Item</code>s
946      * that are associated with the given <code>EdgeKey</code>.
947      *
948      * @param items List of Items to filter
949      * @param itemKeys List of <code>EdgeKey</code>s corresponding
950      * to the edge keys for the <code>Item</code>s in <code>items</code>.
951      * @param filterEdgeKey the <code>EdgeKey</code> to use as a filter.
952      * @return a filtered list of items, containing only those whose edge keys
953      * are the same as <code>filterEdgeKey</code>s.
954      */

955     protected final List filterItems(List items, List itemKeys, FlowGraph.EdgeKey filterEdgeKey) {
956         List filtered = new ArrayList(items.size());
957         Iterator i = items.iterator();
958         Iterator j = itemKeys.iterator();
959         while (i.hasNext() && j.hasNext()) {
960             Item item = (Item)i.next();
961             EdgeKey key = (EdgeKey)j.next();
962             
963             if (filterEdgeKey.equals(key)) {
964                 // the key matches the filter
965
filtered.add(item);
966             }
967         }
968         
969         if (i.hasNext() || j.hasNext()) {
970             throw new InternalCompilerError("item and item key lists " +
971                                             "have different sizes.");
972         }
973         
974         return filtered;
975     }
976  
977     
978     /**
979      * This utility method is for subclasses to determine if the node currently
980      * under consideration has both true and false edges leaving it. That is,
981      * the flow graph at this node has successor edges with the
982      * <code>EdgeKey</code>s <code>Edge_KEY_TRUE</code> and
983      * <code>Edge_KEY_FALSE</code>.
984      *
985      * @param edgeKeys the <code>Set</code> of <code>EdgeKey</code>s of the
986      * successor edges of a given node.
987      * @return true if the <code>edgeKeys</code> contains both
988      * <code>Edge_KEY_TRUE</code> and
989      * <code>Edge_KEY_FALSE</code>
990      */

991     protected static final boolean hasTrueFalseBranches(Set edgeKeys) {
992         return edgeKeys.contains(FlowGraph.EDGE_KEY_FALSE) &&
993                edgeKeys.contains(FlowGraph.EDGE_KEY_TRUE);
994     }
995         
996     /**
997      * This utility method is meant to be used by subclasses to help them
998      * produce appropriate <code>Item</code>s for the
999      * <code>FlowGraph.EDGE_KEY_TRUE</code> and
1000     * <code>FlowGraph.EDGE_KEY_FALSE</code> edges from a boolean condition.
1001     *
1002     * @param booleanCond the boolean condition that is used to branch on. The
1003     * type of the expression must be boolean.
1004     * @param startingItem the <code>Item</code> at the start of the flow for
1005     * the expression <code>booleanCond</code>.
1006     * @param succEdgeKeys the set of <code>EdgeKeys</code> of the successor
1007     * nodes of the current node. Must contain both
1008     * <code>FlowGraph.EDGE_KEY_TRUE</code>
1009     * and <code>FlowGraph.EDGE_KEY_FALSE</code>.
1010     * @param navigator an instance of <code>ConditionNavigator</code> to be
1011     * used to generate appropriate <code>Item</code>s from the
1012     * boolean condition.
1013     * @return a <code>Map</code> containing mappings for all entries in
1014     * <code>succEdgeKeys</code>.
1015     * <code>FlowGraph.EDGE_KEY_TRUE</code> and
1016     * <code>FlowGraph.EDGE_KEY_FALSE</code>
1017     * map to <code>Item</code>s calculated for them using
1018     * navigator, and all other objects in
1019     * <code>succEdgeKeys</code> are mapped to
1020     * <code>startingItem</code>.
1021     */

1022    protected static Map constructItemsFromCondition(Expr booleanCond,
1023                                                     Item startingItem,
1024                                                     Set succEdgeKeys,
1025                                                     ConditionNavigator navigator) {
1026        // check the arguments to make sure this method is used correctly
1027
if (!booleanCond.type().isBoolean()) {
1028            throw new IllegalArgumentException JavaDoc("booleanCond must be a boolean expression");
1029        }
1030        if (!hasTrueFalseBranches(succEdgeKeys)) {
1031            throw new IllegalArgumentException JavaDoc("succEdgeKeys does not have true and false branches.");
1032        }
1033        
1034        
1035        BoolItem results = navigator.navigate(booleanCond, startingItem);
1036        
1037        Map m = new HashMap();
1038        m.put(FlowGraph.EDGE_KEY_TRUE, results.trueItem);
1039        m.put(FlowGraph.EDGE_KEY_FALSE, results.falseItem);
1040        
1041        // put the starting item in the map for any EdgeKeys other than
1042
// FlowGraph.EDGE_KEY_TRUE and FlowGraph.EDGE_KEY_FALSE
1043
for (Iterator iter = succEdgeKeys.iterator(); iter.hasNext(); ) {
1044            EdgeKey e = (EdgeKey)iter.next();
1045            if (!FlowGraph.EDGE_KEY_TRUE.equals(e) &&
1046                !FlowGraph.EDGE_KEY_FALSE.equals(e)) {
1047                m.put(e, startingItem);
1048            }
1049        }
1050        
1051        return m;
1052    }
1053    
1054    /**
1055     * This class contains two <code>Item</code>s, one being the
1056     * <code>Item</code> that is used when an expression is true, the
1057     * other being the one that is used when an expression is false. It is used
1058     * by the <code>ConditionNavigator</code>.
1059     */

1060    protected static class BoolItem {
1061        public BoolItem(Item trueItem, Item falseItem) {
1062            this.trueItem = trueItem;
1063            this.falseItem = falseItem;
1064        }
1065        Item trueItem;
1066        Item falseItem;
1067        public Item trueItem() { return trueItem; }
1068        public Item falseItem() { return falseItem; }
1069        public String JavaDoc toString() {
1070            return "[ true: " + trueItem + "; false: " + falseItem + " ]";
1071        }
1072        
1073    }
1074
1075    /**
1076     * A <code>ConditionNavigator</code> is used to traverse boolean
1077     * expressions that are
1078     * used as conditions, such as in if statements, while statements,
1079     * left branches of && and ||. The <code>ConditionNavigator</code> is used
1080     * to generate
1081     * a finer-grained analysis, so that the branching flows from a
1082     * condition can take into account the fact that the condition is true or
1083     * false. For example, in the statement <code>if (cond) s1 else s2</code>,
1084     * dataflow for <code>s1</code> can continue in the knowledge that
1085     * <code>cond</code> evaluated to true, and similarly, <code>s2</code>
1086     * can be analyzed using the knowledge that <code>cond</code> evaluated to
1087     * false.
1088     *
1089     * @deprecated
1090     */

1091    protected abstract static class ConditionNavigator {
1092        /**
1093         * Navigate the expression <code>expr</code>, where the
1094         * <code>Item</code> at the start of evaluating the expression is
1095         * <code>startingItem</code>.
1096         *
1097         * A <code>BoolItem</code> is returned, containing the
1098         * <code>Item</code>s that are appropriate when <code>expr</code>
1099         * evaluates to true and false.
1100         */

1101        public BoolItem navigate(Expr expr, Item startingItem) {
1102            if (expr.type().isBoolean()) {
1103                if (expr instanceof Binary) {
1104                    Binary b = (Binary)expr;
1105                    if (Binary.COND_AND.equals(b.operator()) ||
1106                        Binary.BIT_AND.equals(b.operator())) {
1107                        
1108                        BoolItem leftRes = navigate(b.left(), startingItem);
1109                        Item rightResStart = startingItem;
1110                        if (Binary.COND_AND.equals(b.operator())) {
1111                            // due to short circuiting, if the right
1112
// branch is evaluated, the starting item is
1113
// in fact the true part of the left result
1114
rightResStart = leftRes.trueItem;
1115                        }
1116                        BoolItem rightRes = navigate(b.right(), rightResStart);
1117                        return andResults(leftRes, rightRes, startingItem);
1118                    }
1119                    else if (Binary.COND_OR.equals(b.operator()) ||
1120                             Binary.BIT_OR.equals(b.operator())) {
1121                        
1122                        BoolItem leftRes = navigate(b.left(), startingItem);
1123                        Item rightResStart = startingItem;
1124                        if (Binary.COND_OR.equals(b.operator())) {
1125                            // due to short circuiting, if the right
1126
// branch is evaluated, the starting item is
1127
// in fact the false part of the left result
1128
rightResStart = leftRes.falseItem;
1129                        }
1130                        BoolItem rightRes = navigate(b.right(), rightResStart);
1131                        return orResults(leftRes, rightRes, startingItem);
1132                    }
1133                }
1134                else if (expr instanceof Unary) {
1135                    Unary u = (Unary)expr;
1136                    if (Unary.NOT.equals(u.operator())) {
1137                        BoolItem res = navigate(u.expr(), startingItem);
1138                        return notResult(res);
1139                    }
1140                }
1141
1142            }
1143            
1144            // either we are not a boolean expression, or not a logical
1145
// connective. Let the subclass deal with it.
1146
return handleExpression(expr, startingItem);
1147        }
1148        
1149        /**
1150         * Combine the results of analyzing the left and right arms of
1151         * an AND boolean operator (either &amp;&amp; or &amp;).
1152         */

1153        public BoolItem andResults(BoolItem left,
1154                                   BoolItem right,
1155                                   Item startingItem) {
1156            return new BoolItem(combine(left.trueItem, right.trueItem),
1157                                startingItem);
1158        }
1159
1160        /**
1161         * Combine the results of analyzing the left and right arms of
1162         * an OR boolean operator (either || or |).
1163         */

1164        public BoolItem orResults(BoolItem left,
1165                                  BoolItem right,
1166                                  Item startingItem) {
1167            return new BoolItem(startingItem,
1168                                combine(left.falseItem, right.falseItem));
1169        }
1170
1171        /**
1172         * Modify the results of analyzing the child of
1173         * a NEGATION boolean operator (a !).
1174         */

1175        public BoolItem notResult(BoolItem results) {
1176            return new BoolItem(results.falseItem, results.trueItem);
1177        }
1178
1179        /**
1180         * Combine two <code>Item</code>s together, when the information
1181         * contained in both items is true. Thus, for example, in a not-null
1182         * analysis, where <code>Item</code>s are sets of not-null variables,
1183         * combining them corresponds to unioning the sets. Note that this
1184         * could be a different operation to the confluence operation.
1185         */

1186        public abstract Item combine(Item item1, Item item2);
1187
1188        /**
1189         * Produce a <code>BoolItem</code> for an expression that is not
1190         * a boolean operator, such as &&, &, ||, | or !.
1191         */

1192        public abstract BoolItem handleExpression(Expr expr, Item startingItem);
1193    }
1194    
1195    protected static int flowCounter = 0;
1196    /**
1197     * Dump a flow graph, labeling edges with their flows, to aid in the
1198     * debugging of data flow.
1199     */

1200    protected void dumpFlowGraph(FlowGraph graph, Term root) {
1201        String JavaDoc name = StringUtil.getShortNameComponent(this.getClass().getName());
1202        name += flowCounter++;
1203
1204        String JavaDoc rootName = "";
1205        if (graph.root() instanceof CodeDecl) {
1206            CodeDecl cd = (CodeDecl)graph.root();
1207            rootName = cd.codeInstance().toString() + " in " +
1208                        cd.codeInstance().container().toString();
1209        }
1210
1211
1212        Report.report(2, "digraph DataFlow" + name + " {");
1213        Report.report(2, " label=\"Dataflow: " + name + "\\n" + rootName +
1214            "\"; fontsize=20; center=true; ratio=auto; size = \"8.5,11\";");
1215
1216        // Loop around the nodes...
1217
for (Iterator iter = graph.peers().iterator(); iter.hasNext(); ) {
1218            Peer p = (Peer)iter.next();
1219            
1220            // dump out this node
1221
Report.report(2,
1222                          p.hashCode() + " [ label = \"" +
1223                          StringUtil.escape(p.node.toString()) + "\\n(" +
1224                          StringUtil.escape(StringUtil.getShortNameComponent(p.node.getClass().getName()))+ ")\" ];");
1225            
1226            // dump out the successors.
1227
for (Iterator iter2 = p.succs.iterator(); iter2.hasNext(); ) {
1228                Edge q = (Edge)iter2.next();
1229                Report.report(2,
1230                              q.getTarget().hashCode() + " [ label = \"" +
1231                              StringUtil.escape(q.getTarget().node.toString()) + " (" +
1232                              StringUtil.escape(StringUtil.getShortNameComponent(q.getTarget().node.getClass().getName()))+ ")\" ];");
1233                String JavaDoc label = q.getKey().toString();
1234                if (p.outItems != null) {
1235                    label += "\\n" + p.outItems.get(q.getKey());
1236                }
1237                else {
1238                    label += "\\n[no dataflow available]";
1239                }
1240                Report.report(2, p.hashCode() + " -> " + q.getTarget().hashCode() +
1241                              " [label=\"" + label + "\"];");
1242            }
1243            
1244        }
1245        Report.report(2, "}");
1246    }
1247}
1248
Popular Tags