KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > polyglot > visit > InitChecker


1 package polyglot.visit;
2
3 import java.util.*;
4 import java.util.Map.Entry;
5
6 import polyglot.ast.*;
7 import polyglot.frontend.Job;
8 import polyglot.types.*;
9
10 /**
11  * Visitor which checks that all local variables must be defined before use,
12  * and that final variables and fields are initialized correctly.
13  *
14  * The checking of the rules is implemented in the methods leaveCall(Node)
15  * and check(FlowGraph, Term, Item, Item).
16  *
17  */

18 public class InitChecker extends DataFlow
19 {
20     public InitChecker(Job job, TypeSystem ts, NodeFactory nf) {
21         super(job, ts, nf,
22               true /* forward analysis */,
23               false /* perform dataflow when leaving CodeDecls, not when entering */);
24     }
25     
26     protected ClassBodyInfo currCBI = null;
27     
28     /**
29      * This class is just a data structure containing relevant information
30      * needed for performing initialization checking of a class declaration.
31      *
32      * These objects form a stack, since class declarations can be nested.
33      */

34     protected static class ClassBodyInfo {
35         /**
36          * The info for the outer ClassBody. The <code>ClassBodyInfo</code>s
37          * form a stack.
38          */

39         ClassBodyInfo outer = null;
40         
41         /** The current CodeDecl being processed by the dataflow equations */
42         CodeDecl currCodeDecl = null;
43         /**
44          * A Map of all the final fields in the class currently being processed
45          * to MinMaxInitCounts. This Map is used as the basis for the Maps returned
46          * in createInitialItem().
47          * */

48         Map currClassFinalFieldInitCounts = new HashMap();
49         /**
50          * List of all the constructors. These will be checked once all the
51          * initializer blocks have been processed.
52          */

53         List allConstructors = new ArrayList();
54
55         /**
56          * Map from ConstructorInstances to ConstructorInstances detailing
57          * which constructors call which constructors.
58          * This is used in checking the initialization of final fields.
59          */

60         Map constructorCalls = new HashMap();
61         
62         /**
63          * Map from ConstructorInstances to Sets of FieldInstances, detailing
64          * which final non-static fields each constructor initializes.
65          * This is used in checking the initialization of final fields.
66          */

67         Map fieldsConstructorInitializes = new HashMap();
68         
69         /**
70          * Set of LocalInstances from the outer class body that were used
71          * during the declaration of this class. We need to track this
72          * in order to correctly populate <code>localsUsedInClassBodies</code>
73          */

74         Set outerLocalsUsed = new HashSet();
75         
76         /**
77          * Map from <code>ClassBody</code>s to <code>Set</code>s of
78          * <code>LocalInstance</code>s. If localsUsedInClassBodies(C) = S, then
79          * the class body C is an inner class declared in the current code
80          * declaration, and S is the set of LocalInstances that are defined
81          * in the current code declaration, but are used in the declaration
82          * of the class C. We need this information in order to ensure that
83          * these local variables are definitely assigned before the class
84          * declaration of C.
85          */

86         Map localsUsedInClassBodies = new HashMap();
87         
88         /**
89          * Set of LocalInstances that we have seen declarations for in this
90          * class. This set allows us to determine which local instances
91          * are simply being used before they are declared (if they are used in
92          * their own initialization) or are locals declared in an enclosing
93          * class.
94          */

95         Set localDeclarations = new HashSet();
96     }
97
98
99     /**
100      * Class representing the initialization counts of variables. The
101      * different values of the counts that we are interested in are ZERO,
102      * ONE and MANY.
103      */

104     protected static class InitCount {
105         static InitCount ZERO = new InitCount(0);
106         static InitCount ONE = new InitCount(1);
107         static InitCount MANY = new InitCount(2);
108         protected int count;
109         protected InitCount(int i) {
110             count = i;
111         }
112
113         public int hashCode() {
114             return count;
115         }
116         
117         public boolean equals(Object JavaDoc o) {
118             if (o instanceof InitCount) {
119                 return this.count == ((InitCount)o).count;
120             }
121             return false;
122         }
123         
124         public String JavaDoc toString() {
125             if (count == 0) {
126                 return "0";
127             }
128             else if (count == 1) {
129                 return "1";
130             }
131             else if (count == 2) {
132                 return "many";
133             }
134             throw new RuntimeException JavaDoc("Unexpected value for count");
135         }
136         
137         public InitCount increment() {
138             if (count == 0) {
139                 return ONE;
140             }
141             return MANY;
142         }
143         public static InitCount min(InitCount a, InitCount b) {
144             if (ZERO.equals(a) || ZERO.equals(b)) {
145                 return ZERO;
146             }
147             if (ONE.equals(a) || ONE.equals(b)) {
148                 return ONE;
149             }
150             return MANY;
151         }
152         public static InitCount max(InitCount a, InitCount b) {
153             if (MANY.equals(a) || MANY.equals(b)) {
154                 return MANY;
155             }
156             if (ONE.equals(a) || ONE.equals(b)) {
157                 return ONE;
158             }
159             return ZERO;
160         }
161         
162     }
163     
164     /**
165      * Class to record counts of the minimum and maximum number of times
166      * a variable or field has been initialized or assigned to.
167      */

168     protected static class MinMaxInitCount {
169         protected InitCount min, max;
170         MinMaxInitCount(InitCount min, InitCount max) {
171             MinMaxInitCount.this.min = min;
172             MinMaxInitCount.this.max = max;
173         }
174         InitCount getMin() { return min; }
175         InitCount getMax() { return max; }
176         public int hashCode() {
177             return min.hashCode() * 4 + max.hashCode();
178         }
179         public String JavaDoc toString() {
180             return "[ min: " + min + "; max: " + max + " ]";
181         }
182         public boolean equals(Object JavaDoc o) {
183             if (o instanceof MinMaxInitCount) {
184                 return this.min.equals(((MinMaxInitCount)o).min) &&
185                        this.max.equals(((MinMaxInitCount)o).max);
186             }
187             return false;
188         }
189         static MinMaxInitCount join(MinMaxInitCount initCount1, MinMaxInitCount initCount2) {
190             if (initCount1 == null) {
191                 return initCount2;
192             }
193             if (initCount2 == null) {
194                 return initCount1;
195             }
196             MinMaxInitCount t = new MinMaxInitCount(
197                                   InitCount.min(initCount1.getMin(), initCount2.getMin()),
198                                   InitCount.max(initCount1.getMax(), initCount2.getMax()));
199             return t;
200
201         }
202     }
203         
204     /**
205      * Dataflow items for this dataflow are maps of VarInstances to counts
206      * of the min and max number of times those variables/fields have
207      * been initialized. These min and max counts are then used to determine
208      * if variables have been initialized before use, and that final variables
209      * are not initialized too many times.
210      *
211      * This class is immutable.
212      */

213     static class DataFlowItem extends Item {
214         Map initStatus; // map of VarInstances to MinMaxInitCount
215

216         DataFlowItem(Map m) {
217             this.initStatus = Collections.unmodifiableMap(m);
218         }
219
220         public String JavaDoc toString() {
221             return initStatus.toString();
222         }
223
224         public boolean equals(Object JavaDoc o) {
225             if (o instanceof DataFlowItem) {
226                 return this.initStatus.equals(((DataFlowItem)o).initStatus);
227             }
228             return false;
229         }
230         
231         public int hashCode() {
232             return (initStatus.hashCode());
233         }
234
235     }
236     
237     protected static final Item BOTTOM = new Item() {
238         public boolean equals(Object JavaDoc i) {
239             return i == this;
240         }
241         public int hashCode() {
242             return -5826349;
243         }};
244
245     /**
246      * Initialise the FlowGraph to be used in the dataflow analysis.
247      * @return null if no dataflow analysis should be performed for this
248      * code declaration; otherwise, an apropriately initialized
249      * FlowGraph.
250      */

251     protected FlowGraph initGraph(CodeDecl code, Term root) {
252         currCBI.currCodeDecl = code;
253         return new FlowGraph(root, forward);
254     }
255
256     /**
257      * Overridden superclass method.
258      *
259      * Set up the state that must be tracked during a Class Declaration.
260      */

261     protected NodeVisitor enterCall(Node n) throws SemanticException {
262         if (n instanceof ClassBody) {
263             // we are starting to process a class declaration, but have yet
264
// to do any of the dataflow analysis.
265

266             // set up the new ClassBodyInfo, and make sure that it forms
267
// a stack.
268
setupClassBody((ClassBody)n);
269         }
270       
271         return super.enterCall(n);
272     }
273
274     /**
275      * Postpone the checking of constructors until the end of the class
276      * declaration is encountered, to ensure that all initializers are
277      * processed first.
278      *
279      * Also, at the end of the class declaration, check that all static final
280      * fields have been initialized at least once, and that for each constructor
281      * all non-static final fields must have been initialized at least once,
282      * taking into account the constructor calls.
283      *
284      */

285     public Node leaveCall(Node n) throws SemanticException {
286         if (n instanceof ConstructorDecl) {
287             // postpone the checking of the constructors until all the
288
// initializer blocks have been processed.
289
currCBI.allConstructors.add(n);
290             return n;
291         }
292         
293         if (n instanceof ClassBody) {
294             // Now that we are at the end of the class declaration, and can
295
// be sure that all of the initializer blocks have been processed,
296
// we can now process the constructors.
297

298             for (Iterator iter = currCBI.allConstructors.iterator();
299                     iter.hasNext(); ) {
300                 ConstructorDecl cd = (ConstructorDecl)iter.next();
301                 
302                 // rely on the fact that our dataflow does not change the AST,
303
// so we can discard the result of this call.
304
dataflow(cd);
305             }
306             
307             // check that all static fields have been initialized exactly once
308
checkStaticFinalFieldsInit((ClassBody)n);
309             
310             // check that at the end of each constructor all non-static final
311
// fields are initialzed.
312
checkNonStaticFinalFieldsInit((ClassBody)n);
313             
314             // copy the locals used to the outer scope
315
if (currCBI.outer != null) {
316                 currCBI.outer.localsUsedInClassBodies.put(n,
317                                                     currCBI.outerLocalsUsed);
318             }
319             
320             // pop the stack
321
currCBI = currCBI.outer;
322         }
323
324         return super.leaveCall(n);
325     }
326
327     protected void setupClassBody(ClassBody n) throws SemanticException {
328         ClassBodyInfo newCDI = new ClassBodyInfo();
329         newCDI.outer = currCBI;
330         currCBI = newCDI;
331             
332
333         // set up currClassFinalFieldInitCounts to contain mappings
334
// for all the final fields of the class.
335
Iterator classMembers = n.members().iterator();
336         while (classMembers.hasNext()) {
337             ClassMember cm = (ClassMember)classMembers.next();
338             if (cm instanceof FieldDecl) {
339                 FieldDecl fd = (FieldDecl)cm;
340                 if (fd.flags().isFinal()) {
341                     MinMaxInitCount initCount;
342                     if (fd.init() != null) {
343                         // the field has an initializer
344
initCount = new MinMaxInitCount(InitCount.ONE,InitCount.ONE);
345                             
346                         // do dataflow over the initialization expression
347
// to pick up any uses of outer local variables.
348
if (currCBI.outer != null)
349                             dataflow(fd.init());
350                     }
351                     else {
352                         // the field does not have an initializer
353
initCount = new MinMaxInitCount(InitCount.ZERO,InitCount.ZERO);
354                     }
355                     newCDI.currClassFinalFieldInitCounts.put(fd.fieldInstance(),
356                                                          initCount);
357                 }
358             }
359         }
360     }
361     
362     /**
363      * Check that each static final field is initialized exactly once.
364      *
365      * @param cb The ClassBody of the class declaring the fields to check.
366      * @throws SemanticException
367      */

368     protected void checkStaticFinalFieldsInit(ClassBody cb) throws SemanticException {
369         // check that all static fields have been initialized exactly once.
370
for (Iterator iter = currCBI.currClassFinalFieldInitCounts.entrySet().iterator();
371                 iter.hasNext(); ) {
372             Map.Entry e = (Map.Entry)iter.next();
373             if (e.getKey() instanceof FieldInstance) {
374                 FieldInstance fi = (FieldInstance)e.getKey();
375                 if (fi.flags().isStatic() && fi.flags().isFinal()) {
376                     MinMaxInitCount initCount = (MinMaxInitCount)e.getValue();
377                     if (InitCount.ZERO.equals(initCount.getMin())) {
378                         throw new SemanticException("field \"" + fi.name() +
379                             "\" might not have been initialized",
380                             cb.position());
381                     }
382                 }
383             }
384         }
385     }
386     
387     /**
388      * Check that each non static final field has been initialized exactly once,
389      * taking into account the fact that constructors may call other
390      * constructors.
391      *
392      * @param cb The ClassBody of the class declaring the fields to check.
393      * @throws SemanticException
394      */

395     protected void checkNonStaticFinalFieldsInit(ClassBody cb) throws SemanticException {
396         // for each non-static final field instance, check that all
397
// constructors intialize it exactly once, taking into account constructor calls.
398
for (Iterator iter = currCBI.currClassFinalFieldInitCounts.keySet().iterator();
399                 iter.hasNext(); ) {
400             FieldInstance fi = (FieldInstance)iter.next();
401             if (fi.flags().isFinal() && !fi.flags().isStatic()) {
402                 // the field is final and not static
403
// it must be initialized exactly once.
404
// navigate up through all of the the constructors
405
// that this constructor calls.
406

407                 boolean fieldInitializedBeforeConstructors = false;
408                 MinMaxInitCount ic = (MinMaxInitCount)
409                     currCBI.currClassFinalFieldInitCounts.get(fi);
410                 if (ic != null && !InitCount.ZERO.equals(ic.getMin())) {
411                     fieldInitializedBeforeConstructors = true;
412                 }
413                             
414                 for (Iterator iter2 = currCBI.allConstructors.iterator();
415                         iter2.hasNext(); ) {
416                     ConstructorDecl cd = (ConstructorDecl)iter2.next();
417                     ConstructorInstance ciStart = cd.constructorInstance();
418                     ConstructorInstance ci = ciStart;
419                         
420                     boolean isInitialized = fieldInitializedBeforeConstructors;
421                         
422                     while (ci != null) {
423                         Set s = (Set)currCBI.fieldsConstructorInitializes.get(ci);
424                         if (s != null && s.contains(fi)) {
425                             if (isInitialized) {
426                                 throw new SemanticException("field \"" + fi.name() +
427                                         "\" might have already been initialized",
428                                         cd.position());
429                             }
430                             isInitialized = true;
431                         }
432                         ci = (ConstructorInstance)currCBI.constructorCalls.get(ci);
433                     }
434                     if (!isInitialized) {
435                         throw new SemanticException("field \"" + fi.name() +
436                                 "\" might not have been initialized",
437                                 ciStart.position());
438                                 
439                     }
440                 }
441             }
442         }
443     }
444     
445     /**
446      * Construct a flow graph for the <code>Expr</code> provided, and call
447      * <code>dataflow(FlowGraph)</code>. Is also responsible for calling
448      * <code>post(FlowGraph, Term)</code> after
449      * <code>dataflow(FlowGraph)</code> has been called.
450      * There is no need to push a CFG onto the stack, as dataflow is not
451      * performed on entry in this analysis.
452      */

453     protected void dataflow(Expr root) throws SemanticException {
454         // Build the control flow graph.
455
FlowGraph g = new FlowGraph(root, forward);
456         CFGBuilder v = createCFGBuilder(ts, g);
457         v.visitGraph();
458         dataflow(g);
459         post(g, root);
460     }
461     
462     /**
463      * The initial item to be given to the entry point of the dataflow contains
464      * the init counts for the final fields.
465      */

466     public Item createInitialItem(FlowGraph graph, Term node) {
467         if (node == graph.startNode()) {
468             return createInitDFI();
469         }
470         return BOTTOM;
471     }
472
473     private DataFlowItem createInitDFI() {
474         return new DataFlowItem(new HashMap(currCBI.currClassFinalFieldInitCounts));
475     }
476     
477     /**
478      * The confluence operator for <code>Initializer</code>s and
479      * <code>Constructor</code>s needs to be a
480      * little special, as we are only concerned with non-exceptional flows in
481      * these cases.
482      * This method ensures that a slightly different confluence is performed
483      * for these <code>Term</code>s, otherwise
484      * <code>confluence(List, Term)</code> is called instead.
485      */

486     protected Item confluence(List items, List itemKeys, Term node, FlowGraph graph) {
487         if (node instanceof Initializer || node instanceof ConstructorDecl) {
488             List filtered = filterItemsNonException(items, itemKeys);
489             if (filtered.isEmpty()) {
490                 return createInitDFI();
491             }
492             else if (filtered.size() == 1) {
493                 return (Item)filtered.get(0);
494             }
495             else {
496                return confluence(filtered, node, graph);
497             }
498         }
499         return confluence(items, node, graph);
500     }
501
502     /**
503      * The confluence operator is essentially the union of all of the
504      * inItems. However, if two or more of the initCount maps from
505      * the inItems each have a MinMaxInitCounts entry for the same
506      * VarInstance, the conflict must be resolved, by using the
507      * minimum of all mins and the maximum of all maxs.
508      */

509     public Item confluence(List inItems, Term node, FlowGraph graph) {
510         // Resolve any conflicts pairwise.
511
Iterator iter = inItems.iterator();
512         Map m = null;
513         while (iter.hasNext()) {
514             Item itm = (Item)iter.next();
515             if (itm == BOTTOM) continue;
516             if (m == null) {
517                 m = new HashMap(((DataFlowItem)itm).initStatus);
518             }
519             else {
520                 Map n = ((DataFlowItem)itm).initStatus;
521                 for (Iterator iter2 = n.entrySet().iterator(); iter2.hasNext(); ) {
522                     Map.Entry entry = (Map.Entry)iter2.next();
523                     VarInstance v = (VarInstance)entry.getKey();
524                     MinMaxInitCount initCount1 = (MinMaxInitCount)m.get(v);
525                     MinMaxInitCount initCount2 = (MinMaxInitCount)entry.getValue();
526                     m.put(v, MinMaxInitCount.join(initCount1, initCount2));
527                 }
528             }
529         }
530         
531         if (m == null) return BOTTOM;
532         
533         return new DataFlowItem(m);
534     }
535     
536
537     protected Map flow(List inItems, List inItemKeys, FlowGraph graph, Term n, Set edgeKeys) {
538         return this.flowToBooleanFlow(inItems, inItemKeys, graph, n, edgeKeys);
539     }
540
541     /**
542      * Perform the appropriate flow operations for the Terms. This method
543      * delegates to other appropriate methods in this class, for modularity.
544      *
545      * To summarize:
546      * - Formals: declaration of a Formal param, just insert a new
547      * MinMaxInitCount for the LocalInstance.
548      * - LocalDecl: a declaration of a local variable, just insert a new
549      * MinMaxInitCount for the LocalInstance as appropriate
550      * based on whether the declaration has an initializer or not.
551      * - Assign: if the LHS of the assign is a local var or a field that we
552      * are interested in, then increment the min and max counts
553      * for that local var or field.
554      */

555     public Map flow(Item trueItem, Item falseItem, Item otherItem, FlowGraph graph, Term n, Set succEdgeKeys) {
556         Item inItem = safeConfluence(trueItem, FlowGraph.EDGE_KEY_TRUE,
557                                      falseItem, FlowGraph.EDGE_KEY_FALSE,
558                                      otherItem, FlowGraph.EDGE_KEY_OTHER,
559                                      n, graph);
560         if (inItem == BOTTOM) {
561             return itemToMap(BOTTOM, succEdgeKeys);
562         }
563         
564         DataFlowItem inDFItem = ((DataFlowItem)inItem);
565         Map ret = null;
566         if (n instanceof Formal) {
567             // formal argument declaration.
568
ret = flowFormal(inDFItem, graph, (Formal)n, succEdgeKeys);
569         }
570         else if (n instanceof LocalDecl) {
571             // local variable declaration.
572
ret = flowLocalDecl(inDFItem, graph, (LocalDecl)n, succEdgeKeys);
573         }
574         else if (n instanceof LocalAssign) {
575             // assignment to a local variable
576
ret = flowLocalAssign(inDFItem, graph, (LocalAssign)n, succEdgeKeys);
577         }
578         else if (n instanceof FieldAssign) {
579             // assignment to a field
580
ret = flowFieldAssign(inDFItem, graph, (FieldAssign)n, succEdgeKeys);
581         }
582         else if (n instanceof ConstructorCall) {
583             // call to another constructor.
584
ret = flowConstructorCall(inDFItem, graph, (ConstructorCall)n, succEdgeKeys);
585         }
586         else if (n instanceof Expr && ((Expr)n).type().isBoolean() &&
587                     (n instanceof Binary || n instanceof Unary)) {
588             if (trueItem == null) trueItem = inDFItem;
589             if (falseItem == null) falseItem = inDFItem;
590             ret = flowBooleanConditions(trueItem, falseItem, inDFItem, graph, (Expr)n, succEdgeKeys);
591         }
592         if (ret != null) {
593             return ret;
594         }
595         return itemToMap(inItem, succEdgeKeys);
596     }
597
598     /**
599      * Perform the appropriate flow operations for declaration of a formal
600      * parameter
601      */

602     protected Map flowFormal(DataFlowItem inItem, FlowGraph graph, Formal f, Set succEdgeKeys) {
603         Map m = new HashMap(inItem.initStatus);
604         // a formal argument is always defined.
605
m.put(f.localInstance(), new MinMaxInitCount(InitCount.ONE,InitCount.ONE));
606             
607         // record the fact that we have seen the formal declaration
608
currCBI.localDeclarations.add(f.localInstance());
609
610         return itemToMap(new DataFlowItem(m), succEdgeKeys);
611     }
612     
613     /**
614      * Perform the appropriate flow operations for declaration of a local
615      * variable
616      */

617     protected Map flowLocalDecl(DataFlowItem inItem,
618                                 FlowGraph graph,
619                                 LocalDecl ld,
620                                 Set succEdgeKeys) {
621         Map m = new HashMap(inItem.initStatus);
622         MinMaxInitCount initCount = (MinMaxInitCount)m.get(ld.localInstance());
623         //if (initCount == null) {
624
if (ld.init() != null) {
625                 // declaration of local var with initialization.
626
initCount = new MinMaxInitCount(InitCount.ONE,
627                                                 InitCount.ONE);
628             }
629             else {
630                 // declaration of local var with no initialization.
631
initCount = new MinMaxInitCount(InitCount.ZERO,InitCount.ZERO);
632             }
633
634             m.put(ld.localInstance(), initCount);
635 // }
636
// else {
637
// the initCount is not null. We now have a problem. Why is the
638
// initCount not null? Has this variable been assigned in its own
639
// initialization, or is this a declaration inside a loop body?
640
// XXX@@@ THIS IS A BUG THAT NEEDS TO BE FIXED.
641
// Currently, the declaration "final int i = (i=5);" will
642
// not be rejected, as we cannot distinguish between that and
643
// "while (true) {final int i = 4;}"
644
// }
645

646         // record the fact that we have seen a local declaration
647
currCBI.localDeclarations.add(ld.localInstance());
648         
649         return itemToMap(new DataFlowItem(m), succEdgeKeys);
650     }
651     
652     /**
653      * Perform the appropriate flow operations for assignment to a local
654      * variable
655      */

656     protected Map flowLocalAssign(DataFlowItem inItem,
657                                   FlowGraph graph,
658                                   LocalAssign a,
659                                   Set succEdgeKeys) {
660           Local l = (Local) a.left();
661           Map m = new HashMap(inItem.initStatus);
662           MinMaxInitCount initCount = (MinMaxInitCount)m.get(l.localInstance());
663
664           // initcount could be null if the local is defined in the outer
665
// class, or if we have not yet seen its declaration (i.e. the
666
// local is used in its own initialization)
667
if (initCount == null) {
668               initCount = new MinMaxInitCount(InitCount.ZERO,InitCount.ZERO);
669           }
670
671           initCount = new MinMaxInitCount(initCount.getMin().increment(),
672                                           initCount.getMax().increment());
673
674           m.put(l.localInstance(), initCount);
675           return itemToMap(new DataFlowItem(m), succEdgeKeys);
676     }
677
678     /**
679      * Perform the appropriate flow operations for assignment to a field
680      */

681     protected Map flowFieldAssign(DataFlowItem inItem,
682                                   FlowGraph graph,
683                                   FieldAssign a,
684                                   Set succEdgeKeys) {
685         Field f = (Field)a.left();
686         FieldInstance fi = f.fieldInstance();
687         
688         if (fi.flags().isFinal() && isFieldsTargetAppropriate(f)) {
689             // this field is final and the target for this field is
690
// appropriate for what we are interested in.
691
Map m = new HashMap(inItem.initStatus);
692             MinMaxInitCount initCount = (MinMaxInitCount)m.get(fi);
693             // initCount may be null if the field is defined in an
694
// outer class.
695
if (initCount != null) {
696                 initCount = new MinMaxInitCount(initCount.getMin().increment(),
697                           initCount.getMax().increment());
698                 m.put(fi, initCount);
699                 return itemToMap(new DataFlowItem(m), succEdgeKeys);
700             }
701         }
702         return null;
703     }
704                                   
705     /**
706      * Perform the appropriate flow operations for a constructor call
707      */

708     protected Map flowConstructorCall(DataFlowItem inItem,
709                                       FlowGraph graph,
710                                       ConstructorCall cc,
711                                       Set succEdgeKeys) {
712         if (ConstructorCall.THIS.equals(cc.kind())) {
713             // currCodeDecl must be a ConstructorDecl, as that
714
// is the only place constructor calls are allowed
715
// record the fact that the current constructor calls the other
716
// constructor
717
currCBI.constructorCalls.put(((ConstructorDecl)currCBI.currCodeDecl).constructorInstance(),
718                                  cc.constructorInstance());
719         }
720         return null;
721     }
722     
723     /**
724      * Determine if we are interested in this field on the basis of the
725      * target of the field. To wit, if the field
726      * is static, then the target of the field must be the current class; if
727      * the field is not static then the target must be "this".
728      */

729     protected boolean isFieldsTargetAppropriate(Field f) {
730         if (f.fieldInstance().flags().isStatic()) {
731             ClassType containingClass = (ClassType)currCBI.currCodeDecl.codeInstance().container();
732             return containingClass.equals(f.fieldInstance().container());
733         }
734         else {
735             return (f.target() instanceof Special &&
736                     Special.THIS.equals(((Special)f.target()).kind()));
737         }
738     }
739     /**
740      * Check that the conditions of initialization are not broken.
741      *
742      * To summarize the conditions:
743      * - Local variables must be initialized before use, (i.e. min count > 0)
744      * - Final local variables (including Formals) cannot be assigned to more
745      * than once (i.e. max count <= 1)
746      * - Final non-static fields whose target is this cannot be assigned to
747      * more than once
748      * - Final static fields whose target is the current class cannot be
749      * assigned to more than once
750      *
751      *
752      * This method is also responsible for maintaining state between the
753      * dataflows over Initializers, by copying back the appropriate
754      * MinMaxInitCounts to the map currClassFinalFieldInitCounts.
755      */

756     public void check(FlowGraph graph, Term n, Item inItem, Map outItems) throws SemanticException {
757         DataFlowItem dfIn = (DataFlowItem)inItem;
758         if (dfIn == null) {
759             // There is no input data flow item. This can happen if we are
760
// checking an unreachable term, and so no Items have flowed
761
// through the term. For example, in the code fragment:
762
// a: do { break a; } while (++i < 10);
763
// the expression "++i < 10" is unreachable, but the as there is
764
// no unreachable statement, the Java Language Spec permits it.
765

766             // Set inItem to a default Item
767
dfIn = createInitDFI();
768         }
769         
770         DataFlowItem dfOut = null;
771         if (outItems != null && !outItems.isEmpty()) {
772             // due to the flow equations, all DataFlowItems in the outItems map
773
// are the same, so just take the first one.
774
dfOut = (DataFlowItem)outItems.values().iterator().next();
775         }
776         
777         if (n instanceof Local) {
778             checkLocal(graph, (Local)n, dfIn, dfOut);
779         }
780         else if (n instanceof LocalAssign) {
781             checkLocalAssign(graph, (LocalAssign)n, dfIn, dfOut);
782         }
783         else if (n instanceof FieldAssign) {
784             checkFieldAssign(graph, (FieldAssign)n, dfIn, dfOut);
785         }
786         else if (n instanceof ClassBody) {
787             // we need to check that the locals used inside this class body
788
// have all been defined at this point.
789
Set localsUsed = (Set)currCBI.localsUsedInClassBodies.get(n);
790             
791             if (localsUsed != null) {
792                 checkLocalsUsedByInnerClass(graph,
793                                             (ClassBody)n,
794                                             localsUsed,
795                                             dfIn,
796                                             dfOut);
797             }
798         }
799         
800         if (n == graph.finishNode()) {
801             if (currCBI.currCodeDecl instanceof Initializer) {
802                 finishInitializer(graph,
803                                 (Initializer)currCBI.currCodeDecl,
804                                 dfIn,
805                                 dfOut);
806             }
807             if (currCBI.currCodeDecl instanceof ConstructorDecl) {
808                 finishConstructorDecl(graph,
809                                     (ConstructorDecl)currCBI.currCodeDecl,
810                                     dfIn,
811                                     dfOut);
812             }
813         }
814     }
815
816     /**
817      * Perform necessary actions upon seeing the Initializer
818      * <code>initializer</code>.
819      */

820     protected void finishInitializer(FlowGraph graph,
821                                     Initializer initializer,
822                                     DataFlowItem dfIn,
823                                     DataFlowItem dfOut) {
824         // We are finishing the checking of an intializer.
825
// We need to copy back the init counts of any fields back into
826
// currClassFinalFieldInitCounts, so that the counts are
827
// correct for the next initializer or constructor.
828
Iterator iter = dfOut.initStatus.entrySet().iterator();
829         while (iter.hasNext()) {
830             Map.Entry e = (Map.Entry)iter.next();
831             if (e.getKey() instanceof FieldInstance) {
832                 FieldInstance fi = (FieldInstance)e.getKey();
833                 if (fi.flags().isFinal()) {
834                     // we don't need to join the init counts, as all
835
// dataflows will go through all of the
836
// initializers
837
currCBI.currClassFinalFieldInitCounts.put(fi,
838                             e.getValue());
839                 }
840             }
841         }
842     }
843
844     /**
845      * Perform necessary actions upon seeing the ConstructorDecl
846      * <code>cd</code>.
847      */

848     protected void finishConstructorDecl(FlowGraph graph,
849                                         ConstructorDecl cd,
850                                         DataFlowItem dfIn,
851                                         DataFlowItem dfOut) {
852         ConstructorInstance ci = cd.constructorInstance();
853         
854         // we need to set currCBI.fieldsConstructorInitializes correctly.
855
// It is meant to contain the non-static final fields that the
856
// constructor ci initializes.
857
//
858
// Note that dfOut.initStatus contains only the MinMaxInitCounts
859
// for _normal_ termination of the constructor (see the
860
// method confluence). This means that if dfOut says the min
861
// count of the initialization for a final non-static field
862
// is one, and that is different from what is recoreded in
863
// currCBI.currClassFinalFieldInitCounts (which is the counts
864
// of the initializations performed by initializers), then
865
// the constructor does indeed initialize the field.
866

867         Set s = new HashSet();
868                 
869         // go through every final non-static field in dfOut.initStatus
870
Iterator iter = dfOut.initStatus.entrySet().iterator();
871         while (iter.hasNext()) {
872             Entry e = (Entry)iter.next();
873             if (e.getKey() instanceof FieldInstance &&
874                     ((FieldInstance)e.getKey()).flags().isFinal() &&
875                     !((FieldInstance)e.getKey()).flags().isStatic()) {
876                 // we have a final non-static field
877
FieldInstance fi = (FieldInstance)e.getKey();
878                 MinMaxInitCount initCount = (MinMaxInitCount)e.getValue();
879                 MinMaxInitCount origInitCount = (MinMaxInitCount)currCBI.currClassFinalFieldInitCounts.get(fi);
880                 if (initCount.getMin() == InitCount.ONE &&
881                      (origInitCount == null || origInitCount.getMin() == InitCount.ZERO)) {
882                     // the constructor initialized this field
883
s.add(fi);
884                 }
885             }
886         }
887         if (!s.isEmpty()) {
888             currCBI.fieldsConstructorInitializes.put(ci, s);
889         }
890     }
891     
892     /**
893      * Check that the local variable <code>l</code> is used correctly.
894      */

895     protected void checkLocal(FlowGraph graph,
896                               Local l,
897                               DataFlowItem dfIn,
898                               DataFlowItem dfOut)
899         throws SemanticException {
900         if (!currCBI.localDeclarations.contains(l.localInstance())) {
901             // it's a local variable that has not been declared within
902
// this scope. The only way this can arise is from an
903
// inner class that is not a member of a class (typically
904
// a local class, or an anonymous class declared in a method,
905
// constructor or initializer).
906
// We need to check that it is a final local, and also
907
// keep track of it, to ensure that it has been definitely
908
// assigned at this point.
909
currCBI.outerLocalsUsed.add(l.localInstance());
910         }
911         else {
912             MinMaxInitCount initCount = (MinMaxInitCount)
913                       dfIn.initStatus.get(l.localInstance());
914             if (initCount != null && InitCount.ZERO.equals(initCount.getMin())) {
915                 // the local variable may not have been initialized.
916
// However, we only want to complain if the local is reachable
917
if (l.reachable()) {
918                     throw new SemanticException("Local variable \"" + l.name() +
919                             "\" may not have been initialized",
920                             l.position());
921                 }
922             }
923         }
924     }
925         
926     /**
927      * Check that the assignment to a local variable is correct.
928      */

929     protected void checkLocalAssign(FlowGraph graph,
930                                     LocalAssign a,
931                                     DataFlowItem dfIn,
932                                     DataFlowItem dfOut)
933         throws SemanticException {
934         LocalInstance li = ((Local)a.left()).localInstance();
935         if (!currCBI.localDeclarations.contains(li)) {
936             throw new SemanticException("Final local variable \"" + li.name() +
937                     "\" cannot be assigned to in an inner class.",
938                     a.position());
939         }
940
941         MinMaxInitCount initCount = (MinMaxInitCount)
942                                dfOut.initStatus.get(li);
943
944         if (li.flags().isFinal() && InitCount.MANY.equals(initCount.getMax())) {
945             throw new SemanticException("variable \"" + li.name() +
946                                         "\" might already have been assigned to",
947                                         a.position());
948         }
949     }
950
951     /**
952      * Check that the assignment to a field is correct.
953      */

954     protected void checkFieldAssign(FlowGraph graph,
955                                     FieldAssign a,
956                                     DataFlowItem dfIn,
957                                     DataFlowItem dfOut)
958         throws SemanticException {
959         Field f = (Field)a.left();
960         FieldInstance fi = f.fieldInstance();
961         if (fi.flags().isFinal()) {
962             if ((currCBI.currCodeDecl instanceof ConstructorDecl ||
963                     currCBI.currCodeDecl instanceof Initializer) &&
964                     isFieldsTargetAppropriate(f)) {
965                 // we are in a constructor or initializer block and
966
// if the field is static then the target is the class
967
// at hand, and if it is not static then the
968
// target of the field is this.
969
// So a final field in this situation can be
970
// assigned to at most once.
971
MinMaxInitCount initCount = (MinMaxInitCount)
972                                        dfOut.initStatus.get(fi);
973                 if (InitCount.MANY.equals(initCount.getMax())) {
974                     throw new SemanticException("field \"" + fi.name() +
975                             "\" might already have been assigned to",
976                             a.position());
977                 }
978             }
979             else {
980                 // not in a constructor or intializer, or the target is
981
// not appropriate. So we cannot assign
982
// to a final field at all.
983
throw new SemanticException("Cannot assign a value " +
984                            "to final field \"" + fi.name() + "\"",
985                            a.position());
986             }
987         }
988     }
989     
990     /**
991      * Check that the set of <code>LocalInstance</code>s
992      * <code>localsUsed</code>, which is the set of locals used in the inner
993      * class declared by <code>cb</code>
994      * are initialized before the class declaration.
995      */

996     protected void checkLocalsUsedByInnerClass(FlowGraph graph,
997                                                ClassBody cb,
998                                                Set localsUsed,
999                                                DataFlowItem dfIn,
1000                                               DataFlowItem dfOut)
1001    throws SemanticException {
1002        for (Iterator iter = localsUsed.iterator(); iter.hasNext(); ) {
1003            LocalInstance li = (LocalInstance)iter.next();
1004            MinMaxInitCount initCount = (MinMaxInitCount)
1005                                            dfOut.initStatus.get(li);
1006            if (!currCBI.localDeclarations.contains(li)) {
1007                // the local wasn't defined in this scope.
1008
currCBI.outerLocalsUsed.add(li);
1009            }
1010            else if (initCount == null || InitCount.ZERO.equals(initCount.getMin())) {
1011                // initCount will in general not be null, as the local variable
1012
// li is declared in the current class; however, if the inner
1013
// class is declared in the initializer of the local variable
1014
// declaration, then initCount could in fact be null, as we
1015
// leave the inner class before we have performed flowLocalDecl
1016
// for the local variable declaration.
1017

1018                throw new SemanticException("Local variable \"" + li.name() +
1019                        "\" must be initialized before the class " +
1020                        "declaration.",
1021                        cb.position());
1022            }
1023        }
1024    }
1025
1026    
1027}
1028
Popular Tags