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 18 public class InitChecker extends DataFlow 19 { 20 public InitChecker(Job job, TypeSystem ts, NodeFactory nf) { 21 super(job, ts, nf, 22 true , 23 false ); 24 } 25 26 protected ClassBodyInfo currCBI = null; 27 28 34 protected static class ClassBodyInfo { 35 39 ClassBodyInfo outer = null; 40 41 42 CodeDecl currCodeDecl = null; 43 48 Map currClassFinalFieldInitCounts = new HashMap(); 49 53 List allConstructors = new ArrayList(); 54 55 60 Map constructorCalls = new HashMap(); 61 62 67 Map fieldsConstructorInitializes = new HashMap(); 68 69 74 Set outerLocalsUsed = new HashSet(); 75 76 86 Map localsUsedInClassBodies = new HashMap(); 87 88 95 Set localDeclarations = new HashSet(); 96 } 97 98 99 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 o) { 118 if (o instanceof InitCount) { 119 return this.count == ((InitCount)o).count; 120 } 121 return false; 122 } 123 124 public String 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 ("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 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 toString() { 180 return "[ min: " + min + "; max: " + max + " ]"; 181 } 182 public boolean equals(Object 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 213 static class DataFlowItem extends Item { 214 Map initStatus; 216 DataFlowItem(Map m) { 217 this.initStatus = Collections.unmodifiableMap(m); 218 } 219 220 public String toString() { 221 return initStatus.toString(); 222 } 223 224 public boolean equals(Object 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 i) { 239 return i == this; 240 } 241 public int hashCode() { 242 return -5826349; 243 }}; 244 245 251 protected FlowGraph initGraph(CodeDecl code, Term root) { 252 currCBI.currCodeDecl = code; 253 return new FlowGraph(root, forward); 254 } 255 256 261 protected NodeVisitor enterCall(Node n) throws SemanticException { 262 if (n instanceof ClassBody) { 263 266 setupClassBody((ClassBody)n); 269 } 270 271 return super.enterCall(n); 272 } 273 274 285 public Node leaveCall(Node n) throws SemanticException { 286 if (n instanceof ConstructorDecl) { 287 currCBI.allConstructors.add(n); 290 return n; 291 } 292 293 if (n instanceof ClassBody) { 294 298 for (Iterator iter = currCBI.allConstructors.iterator(); 299 iter.hasNext(); ) { 300 ConstructorDecl cd = (ConstructorDecl)iter.next(); 301 302 dataflow(cd); 305 } 306 307 checkStaticFinalFieldsInit((ClassBody)n); 309 310 checkNonStaticFinalFieldsInit((ClassBody)n); 313 314 if (currCBI.outer != null) { 316 currCBI.outer.localsUsedInClassBodies.put(n, 317 currCBI.outerLocalsUsed); 318 } 319 320 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 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 initCount = new MinMaxInitCount(InitCount.ONE,InitCount.ONE); 345 346 if (currCBI.outer != null) 349 dataflow(fd.init()); 350 } 351 else { 352 initCount = new MinMaxInitCount(InitCount.ZERO,InitCount.ZERO); 354 } 355 newCDI.currClassFinalFieldInitCounts.put(fd.fieldInstance(), 356 initCount); 357 } 358 } 359 } 360 } 361 362 368 protected void checkStaticFinalFieldsInit(ClassBody cb) throws SemanticException { 369 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 395 protected void checkNonStaticFinalFieldsInit(ClassBody cb) throws SemanticException { 396 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 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 453 protected void dataflow(Expr root) throws SemanticException { 454 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 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 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 509 public Item confluence(List inItems, Term node, FlowGraph graph) { 510 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 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 ret = flowFormal(inDFItem, graph, (Formal)n, succEdgeKeys); 569 } 570 else if (n instanceof LocalDecl) { 571 ret = flowLocalDecl(inDFItem, graph, (LocalDecl)n, succEdgeKeys); 573 } 574 else if (n instanceof LocalAssign) { 575 ret = flowLocalAssign(inDFItem, graph, (LocalAssign)n, succEdgeKeys); 577 } 578 else if (n instanceof FieldAssign) { 579 ret = flowFieldAssign(inDFItem, graph, (FieldAssign)n, succEdgeKeys); 581 } 582 else if (n instanceof ConstructorCall) { 583 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 602 protected Map flowFormal(DataFlowItem inItem, FlowGraph graph, Formal f, Set succEdgeKeys) { 603 Map m = new HashMap(inItem.initStatus); 604 m.put(f.localInstance(), new MinMaxInitCount(InitCount.ONE,InitCount.ONE)); 606 607 currCBI.localDeclarations.add(f.localInstance()); 609 610 return itemToMap(new DataFlowItem(m), succEdgeKeys); 611 } 612 613 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 (ld.init() != null) { 625 initCount = new MinMaxInitCount(InitCount.ONE, 627 InitCount.ONE); 628 } 629 else { 630 initCount = new MinMaxInitCount(InitCount.ZERO,InitCount.ZERO); 632 } 633 634 m.put(ld.localInstance(), initCount); 635 646 currCBI.localDeclarations.add(ld.localInstance()); 648 649 return itemToMap(new DataFlowItem(m), succEdgeKeys); 650 } 651 652 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 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 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 Map m = new HashMap(inItem.initStatus); 692 MinMaxInitCount initCount = (MinMaxInitCount)m.get(fi); 693 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 708 protected Map flowConstructorCall(DataFlowItem inItem, 709 FlowGraph graph, 710 ConstructorCall cc, 711 Set succEdgeKeys) { 712 if (ConstructorCall.THIS.equals(cc.kind())) { 713 currCBI.constructorCalls.put(((ConstructorDecl)currCBI.currCodeDecl).constructorInstance(), 718 cc.constructorInstance()); 719 } 720 return null; 721 } 722 723 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 756 public void check(FlowGraph graph, Term n, Item inItem, Map outItems) throws SemanticException { 757 DataFlowItem dfIn = (DataFlowItem)inItem; 758 if (dfIn == null) { 759 766 dfIn = createInitDFI(); 768 } 769 770 DataFlowItem dfOut = null; 771 if (outItems != null && !outItems.isEmpty()) { 772 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 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 820 protected void finishInitializer(FlowGraph graph, 821 Initializer initializer, 822 DataFlowItem dfIn, 823 DataFlowItem dfOut) { 824 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 currCBI.currClassFinalFieldInitCounts.put(fi, 838 e.getValue()); 839 } 840 } 841 } 842 } 843 844 848 protected void finishConstructorDecl(FlowGraph graph, 849 ConstructorDecl cd, 850 DataFlowItem dfIn, 851 DataFlowItem dfOut) { 852 ConstructorInstance ci = cd.constructorInstance(); 853 854 867 Set s = new HashSet(); 868 869 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 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 s.add(fi); 884 } 885 } 886 } 887 if (!s.isEmpty()) { 888 currCBI.fieldsConstructorInitializes.put(ci, s); 889 } 890 } 891 892 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 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 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 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 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 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 throw new SemanticException("Cannot assign a value " + 984 "to final field \"" + fi.name() + "\"", 985 a.position()); 986 } 987 } 988 } 989 990 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 currCBI.outerLocalsUsed.add(li); 1009 } 1010 else if (initCount == null || InitCount.ZERO.equals(initCount.getMin())) { 1011 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 |