001 package edu.rice.cs.cunit.threadCheck; 002 003 import edu.rice.cs.cunit.classFile.ClassFileTools; 004 import edu.rice.cs.cunit.instrumentors.DoNotInstrument; 005 import edu.rice.cs.cunit.util.Pair; 006 007 import java.awt.*; 008 import java.io.*; 009 import java.lang.annotation.Annotation; 010 import java.lang.reflect.InvocationTargetException; 011 import java.lang.reflect.Method; 012 import java.lang.reflect.Modifier; 013 import java.util.*; 014 import java.util.regex.PatternSyntaxException; 015 016 /** 017 * Class that checks whether threads may execute certain classes at runtime.. 018 * 019 * @author Mathias Ricken 020 */ 021 @DoNotInstrument 022 public class ThreadCheck { 023 public static class Violation implements Serializable { 024 public static final String LF = System.getProperty("line.separator"); 025 026 public static enum TYPE { 027 NOTRUNBY_NAME, 028 NOTRUNBY_GROUP, 029 NOTRUNBY_ID, 030 ONLYRUNBY, 031 PREDICATE, 032 REFLECTION_PREDICATE 033 } 034 035 // offending thread information 036 public final TYPE type; 037 038 public final String threadName; 039 040 public final String threadGroupName; 041 042 public final long threadId; 043 044 public final StackTraceElement[] stackTrace; 045 046 public final long checkCount; 047 048 public final long violationCount; 049 050 public Violation(Violation.TYPE type, 051 String threadName, 052 String threadGroupName, 053 long threadId, 054 StackTraceElement[] ste) { 055 this.type = type; 056 this.threadName = threadName; 057 this.threadGroupName = threadGroupName; 058 this.threadId = threadId; 059 if (ste.length>2) { 060 this.stackTrace = new StackTraceElement[ste.length-2]; 061 System.arraycopy(ste,2,this.stackTrace,0,ste.length-2); 062 } 063 else { 064 this.stackTrace = new StackTraceElement[0]; 065 } 066 this.checkCount = _checkCount; 067 this.violationCount = _violationCount; 068 } 069 070 public boolean equals(Object o) { 071 if (this == o) { 072 return true; 073 } 074 if (o == null || getClass() != o.getClass()) { 075 return false; 076 } 077 078 Violation violation = (Violation)o; 079 080 if (threadId != violation.threadId) { 081 return false; 082 } 083 if (!Arrays.equals(stackTrace, violation.stackTrace)) { 084 return false; 085 } 086 if (threadGroupName != null ? !threadGroupName.equals(violation.threadGroupName) : 087 violation.threadGroupName != null) { 088 return false; 089 } 090 if (threadName != null ? !threadName.equals(violation.threadName) : violation.threadName != null) { 091 return false; 092 } 093 if (type != violation.type) { 094 return false; 095 } 096 097 return true; 098 } 099 100 public int hashCode() { 101 int result; 102 result = type.hashCode(); 103 result = 31 * result + (threadName != null ? threadName.hashCode() : 0); 104 result = 31 * result + (threadGroupName != null ? threadGroupName.hashCode() : 0); 105 result = 31 * result + (int)(threadId ^ (threadId >>> 32)); 106 result = 31 * result + Arrays.hashCode(stackTrace); 107 return result; 108 } 109 } 110 111 public static class NotRunByThreadNameViolation extends Violation { 112 public final String disallowedThreadName; 113 114 public NotRunByThreadNameViolation(String threadName, 115 String threadGroupName, 116 long threadId, 117 String disallowedThreadName, 118 StackTraceElement[] ste) { 119 super(TYPE.NOTRUNBY_NAME, threadName, threadGroupName, threadId, ste); 120 this.disallowedThreadName = disallowedThreadName; 121 } 122 123 /** 124 * Returns a string representation of the object. 125 * 126 * @return string representation 127 */ 128 public String toString() { 129 StringBuilder sb = new StringBuilder(); 130 sb.append("Thread Name Violation: NotRunBy ("); 131 sb.append(checkCount); 132 sb.append(" "); 133 sb.append(form("check", checkCount)); 134 sb.append(", "); 135 sb.append(violationCount); 136 sb.append(" "); 137 sb.append(form("violation", violationCount)); 138 sb.append(")"); 139 sb.append(LF); 140 sb.append("\tCurrent thread '"); 141 sb.append(threadName); 142 sb.append("', id "); 143 sb.append(threadId); 144 sb.append(", group '"); 145 sb.append(threadGroupName); 146 sb.append("'"); 147 sb.append(LF); 148 sb.append("\tMatched disallowed name pattern '"); 149 sb.append(disallowedThreadName); 150 sb.append("'"); 151 for(int i = 0; i < stackTrace.length; ++i) { 152 sb.append(LF); 153 sb.append("\tat "); 154 sb.append(stackTrace[i].getClassName()); 155 sb.append("."); 156 sb.append(stackTrace[i].getMethodName()); 157 sb.append(" ("); 158 sb.append(stackTrace[i].getFileName()); 159 sb.append(":"); 160 sb.append(stackTrace[i].getLineNumber()); 161 sb.append(")"); 162 } 163 164 return sb.toString(); 165 } 166 167 168 public boolean equals(Object o) { 169 if (this == o) { 170 return true; 171 } 172 if (o == null || getClass() != o.getClass()) { 173 return false; 174 } 175 if (!super.equals(o)) { 176 return false; 177 } 178 179 NotRunByThreadNameViolation that = (NotRunByThreadNameViolation)o; 180 181 if (!disallowedThreadName.equals(that.disallowedThreadName)) { 182 return false; 183 } 184 185 return true; 186 } 187 188 public int hashCode() { 189 int result = super.hashCode(); 190 result = 31 * result + disallowedThreadName.hashCode(); 191 return result; 192 } 193 } 194 195 public static class NotRunByThreadGroupViolation extends Violation { 196 public final String disallowedThreadGroupName; 197 198 public NotRunByThreadGroupViolation(String threadName, 199 String threadGroupName, 200 long threadId, 201 StackTraceElement[] ste, 202 String disallowedThreadGroupName) { 203 super(TYPE.NOTRUNBY_GROUP, threadName, threadGroupName, threadId, ste); 204 this.disallowedThreadGroupName = disallowedThreadGroupName; 205 } 206 207 /** 208 * Returns a string representation of the object. 209 * 210 * @return string representation 211 */ 212 public String toString() { 213 StringBuilder sb = new StringBuilder(); 214 sb.append("Thread Group Name Violation: NotRunBy ("); 215 sb.append(checkCount); 216 sb.append(" "); 217 sb.append(form("check", checkCount)); 218 sb.append(", "); 219 sb.append(violationCount); 220 sb.append(" "); 221 sb.append(form("violation", violationCount)); 222 sb.append(")"); 223 sb.append(LF); 224 sb.append("\tCurrent thread '"); 225 sb.append(threadName); 226 sb.append("', id "); 227 sb.append(threadId); 228 sb.append(", group '"); 229 sb.append(threadGroupName); 230 sb.append("'"); 231 sb.append(LF); 232 sb.append("\tMatched disallowed group name pattern '"); 233 sb.append(disallowedThreadGroupName); 234 sb.append("'"); 235 for(int i = 0; i < stackTrace.length; ++i) { 236 sb.append(LF); 237 sb.append("\tat "); 238 sb.append(stackTrace[i].getClassName()); 239 sb.append("."); 240 sb.append(stackTrace[i].getMethodName()); 241 sb.append(" ("); 242 sb.append(stackTrace[i].getFileName()); 243 sb.append(":"); 244 sb.append(stackTrace[i].getLineNumber()); 245 sb.append(")"); 246 } 247 248 return sb.toString(); 249 } 250 251 public boolean equals(Object o) { 252 if (this == o) { 253 return true; 254 } 255 if (o == null || getClass() != o.getClass()) { 256 return false; 257 } 258 if (!super.equals(o)) { 259 return false; 260 } 261 262 NotRunByThreadGroupViolation that = (NotRunByThreadGroupViolation)o; 263 264 if (!disallowedThreadGroupName.equals(that.disallowedThreadGroupName)) { 265 return false; 266 } 267 268 return true; 269 } 270 271 public int hashCode() { 272 int result = super.hashCode(); 273 result = 31 * result + disallowedThreadGroupName.hashCode(); 274 return result; 275 } 276 } 277 278 public static class NotRunByIdViolation extends Violation { 279 public final long disallowedId; 280 281 public NotRunByIdViolation(String threadName, 282 String threadGroupName, 283 long threadId, 284 StackTraceElement[] ste, 285 long disallowedId) { 286 super(TYPE.NOTRUNBY_GROUP, threadName, threadGroupName, threadId, ste); 287 this.disallowedId = disallowedId; 288 } 289 290 /** 291 * Returns a string representation of the object. 292 * 293 * @return string representation 294 */ 295 public String toString() { 296 StringBuilder sb = new StringBuilder(); 297 sb.append("Thread Id Violation: NotRunBy ("); 298 sb.append(checkCount); 299 sb.append(" "); 300 sb.append(form("check", checkCount)); 301 sb.append(", "); 302 sb.append(violationCount); 303 sb.append(" "); 304 sb.append(form("violation", violationCount)); 305 sb.append(")"); 306 sb.append(LF); 307 sb.append("\tCurrent thread '"); 308 sb.append(threadName); 309 sb.append("', id "); 310 sb.append(threadId); 311 sb.append(", group '"); 312 sb.append(threadGroupName); 313 sb.append("'"); 314 sb.append(LF); 315 sb.append("\tMatched disallowed id "); 316 sb.append(disallowedId); 317 for(int i = 0; i < stackTrace.length; ++i) { 318 sb.append(LF); 319 sb.append("\tat "); 320 sb.append(stackTrace[i].getClassName()); 321 sb.append("."); 322 sb.append(stackTrace[i].getMethodName()); 323 sb.append(" ("); 324 sb.append(stackTrace[i].getFileName()); 325 sb.append(":"); 326 sb.append(stackTrace[i].getLineNumber()); 327 sb.append(")"); 328 } 329 330 return sb.toString(); 331 } 332 333 public boolean equals(Object o) { 334 if (this == o) { 335 return true; 336 } 337 if (o == null || getClass() != o.getClass()) { 338 return false; 339 } 340 if (!super.equals(o)) { 341 return false; 342 } 343 344 NotRunByIdViolation that = (NotRunByIdViolation)o; 345 346 if (disallowedId != that.disallowedId) { 347 return false; 348 } 349 350 return true; 351 } 352 353 public int hashCode() { 354 int result = super.hashCode(); 355 result = 31 * result + (int)(disallowedId ^ (disallowedId >>> 32)); 356 return result; 357 } 358 } 359 360 public static class OnlyRunByViolation extends Violation { 361 public final boolean allowedEventThread; 362 363 public final HashSet<String> allowedThreadNames; 364 365 public final HashSet<String> allowedGroupNames; 366 367 public final HashSet<Long> allowedThreadIds; 368 369 public OnlyRunByViolation(String threadName, 370 String threadGroupName, 371 long threadId, 372 StackTraceElement[] ste, 373 boolean allowedEventThread, 374 HashSet<String> allowedThreadNames, 375 HashSet<String> allowedGroupNames, 376 HashSet<Long> allowedThreadIds) { 377 super(TYPE.NOTRUNBY_GROUP, threadName, threadGroupName, threadId, ste); 378 this.allowedEventThread = allowedEventThread; 379 this.allowedThreadNames = allowedThreadNames; 380 this.allowedGroupNames = allowedGroupNames; 381 this.allowedThreadIds = allowedThreadIds; 382 } 383 384 /** 385 * Returns a string representation of the object. 386 * 387 * @return string representation 388 */ 389 public String toString() { 390 StringBuilder sb = new StringBuilder(); 391 sb.append("Thread Violation: OnlyRunBy ("); 392 sb.append(checkCount); 393 sb.append(" "); 394 sb.append(form("check", checkCount)); 395 sb.append(", "); 396 sb.append(violationCount); 397 sb.append(" "); 398 sb.append(form("violation", violationCount)); 399 sb.append(")"); 400 sb.append(LF); 401 sb.append("\tCurrent thread '"); 402 sb.append(threadName); 403 sb.append("', id "); 404 sb.append(threadId); 405 sb.append(", group '"); 406 sb.append(threadGroupName); 407 sb.append("' did not match"); 408 409 if (allowedEventThread) { 410 sb.append(LF); 411 sb.append("\tthe event thread"); 412 } 413 if ((allowedThreadNames != null) && (allowedThreadNames.size() > 0)) { 414 sb.append(LF); 415 sb.append("\tany of the allowed thread name patterns"); 416 for(String regex : allowedThreadNames) { 417 sb.append(LF); 418 sb.append("\t\t'"); 419 sb.append(regex); 420 sb.append("'"); 421 } 422 } 423 if ((allowedThreadIds != null) && (allowedThreadIds.size() > 0)) { 424 sb.append(LF); 425 sb.append("\tany of the allowed ids"); 426 for(long id : allowedThreadIds) { 427 sb.append(LF); 428 sb.append("\t\t"); 429 sb.append(id); 430 } 431 } 432 if ((allowedGroupNames != null) && (allowedGroupNames.size() > 0)) { 433 sb.append(LF); 434 sb.append("\tany of the allowed group name patterns"); 435 for(String regex : allowedGroupNames) { 436 sb.append(LF); 437 sb.append("\t\t'"); 438 sb.append(regex); 439 sb.append("'"); 440 } 441 } 442 for(int i = 0; i < stackTrace.length; ++i) { 443 sb.append(LF); 444 sb.append("\tat "); 445 sb.append(stackTrace[i].getClassName()); 446 sb.append("."); 447 sb.append(stackTrace[i].getMethodName()); 448 sb.append(" ("); 449 sb.append(stackTrace[i].getFileName()); 450 sb.append(":"); 451 sb.append(stackTrace[i].getLineNumber()); 452 sb.append(")"); 453 } 454 455 return sb.toString(); 456 } 457 458 public boolean equals(Object o) { 459 if (this == o) { 460 return true; 461 } 462 if (o == null || getClass() != o.getClass()) { 463 return false; 464 } 465 if (!super.equals(o)) { 466 return false; 467 } 468 469 OnlyRunByViolation that = (OnlyRunByViolation)o; 470 471 if (allowedEventThread != that.allowedEventThread) { 472 return false; 473 } 474 if (allowedGroupNames != null ? !allowedGroupNames.equals(that.allowedGroupNames) : 475 that.allowedGroupNames != null) { 476 return false; 477 } 478 if (allowedThreadIds != null ? !allowedThreadIds.equals(that.allowedThreadIds) : 479 that.allowedThreadIds != null) { 480 return false; 481 } 482 if (allowedThreadNames != null ? !allowedThreadNames.equals(that.allowedThreadNames) : 483 that.allowedThreadNames != null) { 484 return false; 485 } 486 487 return true; 488 } 489 490 public int hashCode() { 491 int result = super.hashCode(); 492 result = 31 * result + (allowedEventThread ? 1 : 0); 493 result = 31 * result + (allowedThreadNames != null ? allowedThreadNames.hashCode() : 0); 494 result = 31 * result + (allowedGroupNames != null ? allowedGroupNames.hashCode() : 0); 495 result = 31 * result + (allowedThreadIds != null ? allowedThreadIds.hashCode() : 0); 496 return result; 497 } 498 } 499 500 public static class PredicateViolation extends Violation { 501 public final String predicateClass; 502 503 public PredicateViolation(String threadName, 504 String threadGroupName, 505 long threadId, 506 StackTraceElement[] ste, 507 String predicateClass) { 508 super(TYPE.PREDICATE, threadName, threadGroupName, threadId, ste); 509 this.predicateClass = predicateClass; 510 } 511 512 /** 513 * Returns a string representation of the object. 514 * 515 * @return string representation 516 */ 517 public String toString() { 518 StringBuilder sb = new StringBuilder(); 519 sb.append("Thread Predicate Violation: ("); 520 sb.append(checkCount); 521 sb.append(" "); 522 sb.append(form("check", checkCount)); 523 sb.append(", "); 524 sb.append(violationCount); 525 sb.append(" "); 526 sb.append(form("violation", violationCount)); 527 sb.append(")"); 528 sb.append(LF); 529 sb.append("\tCurrent thread '"); 530 sb.append(threadName); 531 sb.append("', id "); 532 sb.append(threadId); 533 sb.append(", group '"); 534 sb.append(threadGroupName); 535 sb.append("'"); 536 sb.append(LF); 537 sb.append("\tViolated predicate @"); 538 sb.append(predicateClass.substring(1, predicateClass.length()-1).replace('/','.')); 539 for(int i = 0; i < stackTrace.length; ++i) { 540 sb.append(LF); 541 sb.append("\tat "); 542 sb.append(stackTrace[i].getClassName()); 543 sb.append("."); 544 sb.append(stackTrace[i].getMethodName()); 545 sb.append(" ("); 546 sb.append(stackTrace[i].getFileName()); 547 sb.append(":"); 548 sb.append(stackTrace[i].getLineNumber()); 549 sb.append(")"); 550 } 551 552 return sb.toString(); 553 } 554 555 public boolean equals(Object o) { 556 if (this == o) { 557 return true; 558 } 559 if (o == null || getClass() != o.getClass()) { 560 return false; 561 } 562 if (!super.equals(o)) { 563 return false; 564 } 565 566 PredicateViolation that = (PredicateViolation)o; 567 568 if (predicateClass != null ? !predicateClass.equals(that.predicateClass) : that.predicateClass != null) { 569 return false; 570 } 571 572 return true; 573 } 574 575 public int hashCode() { 576 int result = super.hashCode(); 577 result = 31 * result + (predicateClass != null ? predicateClass.hashCode() : 0); 578 return result; 579 } 580 } 581 582 public static class ExceptionInPredicateViolation extends PredicateViolation { 583 public final Throwable t; 584 585 public ExceptionInPredicateViolation(String threadName, 586 String threadGroupName, 587 long threadId, 588 StackTraceElement[] ste, 589 String predicateClass, 590 Throwable t) { 591 super(threadName, threadGroupName, threadId, ste, predicateClass); 592 this.t = t; 593 } 594 595 /** 596 * Returns a string representation of the object. 597 * 598 * @return string representation 599 */ 600 public String toString() { 601 StringBuilder sb = new StringBuilder(); 602 sb.append("Exception in Thread Predicate Violation: ("); 603 sb.append(checkCount); 604 sb.append(" "); 605 sb.append(form("check", checkCount)); 606 sb.append(", "); 607 sb.append(violationCount); 608 sb.append(" "); 609 sb.append(form("violation", violationCount)); 610 sb.append(")"); 611 sb.append(LF); 612 sb.append("\tCurrent thread '"); 613 sb.append(threadName); 614 sb.append("', id "); 615 sb.append(threadId); 616 sb.append(", group '"); 617 sb.append(threadGroupName); 618 sb.append("'"); 619 sb.append(LF); 620 sb.append("\tViolated predicate @"); 621 sb.append(predicateClass.substring(1, predicateClass.length()-1).replace('/','.')); 622 sb.append(LF); 623 sb.append("\tThrew "); 624 sb.append(t.toString()); 625 for(int i = 0; i < stackTrace.length; ++i) { 626 sb.append(LF); 627 sb.append("\tat "); 628 sb.append(stackTrace[i].getClassName()); 629 sb.append("."); 630 sb.append(stackTrace[i].getMethodName()); 631 sb.append(" ("); 632 sb.append(stackTrace[i].getFileName()); 633 sb.append(":"); 634 sb.append(stackTrace[i].getLineNumber()); 635 sb.append(")"); 636 } 637 638 return sb.toString(); 639 } 640 641 642 public boolean equals(Object o) { 643 if (this == o) { 644 return true; 645 } 646 if (o == null || getClass() != o.getClass()) { 647 return false; 648 } 649 if (!super.equals(o)) { 650 return false; 651 } 652 653 ExceptionInPredicateViolation that = (ExceptionInPredicateViolation)o; 654 655 // Probably incorrect - comparing Object[] arrays with Arrays.equals 656 return t.equals(that.t); 657 } 658 659 public int hashCode() { 660 int result = super.hashCode(); 661 result = 31 * result + (t != null ? t.hashCode() : 0); 662 return result; 663 } 664 } 665 666 public static class ArgumentPredicateViolation extends PredicateViolation { 667 public final Object[] args; 668 669 public ArgumentPredicateViolation(String threadName, 670 String threadGroupName, 671 long threadId, 672 StackTraceElement[] ste, 673 String predicateClass, 674 Object[] args) { 675 super(threadName, threadGroupName, threadId, ste, predicateClass); 676 this.args = args; 677 } 678 679 /** 680 * Returns a string representation of the object. 681 * 682 * @return string representation 683 */ 684 public String toString() { 685 StringBuilder sb = new StringBuilder(); 686 sb.append("Thread Predicate Violation: ("); 687 sb.append(checkCount); 688 sb.append(" "); 689 sb.append(form("check", checkCount)); 690 sb.append(", "); 691 sb.append(violationCount); 692 sb.append(" "); 693 sb.append(form("violation", violationCount)); 694 sb.append(")"); 695 sb.append(LF); 696 sb.append("\tCurrent thread '"); 697 sb.append(threadName); 698 sb.append("', id "); 699 sb.append(threadId); 700 sb.append(", group '"); 701 sb.append(threadGroupName); 702 sb.append("'"); 703 sb.append(LF); 704 sb.append("\tViolated predicate @"); 705 sb.append(predicateClass.substring(1, predicateClass.length()-1).replace('/','.')); 706 sb.append(LF); 707 sb.append("\tMethod arguments"); 708 for(Object a: args) { 709 sb.append(LF); 710 sb.append("\t\t'"); 711 sb.append(a.toString()); 712 sb.append("' : "); 713 sb.append(a.getClass().getName()); 714 } 715 for(int i = 0; i < stackTrace.length; ++i) { 716 sb.append(LF); 717 sb.append("\tat "); 718 sb.append(stackTrace[i].getClassName()); 719 sb.append("."); 720 sb.append(stackTrace[i].getMethodName()); 721 sb.append(" ("); 722 sb.append(stackTrace[i].getFileName()); 723 sb.append(":"); 724 sb.append(stackTrace[i].getLineNumber()); 725 sb.append(")"); 726 } 727 728 return sb.toString(); 729 } 730 731 732 public boolean equals(Object o) { 733 if (this == o) { 734 return true; 735 } 736 if (o == null || getClass() != o.getClass()) { 737 return false; 738 } 739 if (!super.equals(o)) { 740 return false; 741 } 742 743 ArgumentPredicateViolation that = (ArgumentPredicateViolation)o; 744 745 // Probably incorrect - comparing Object[] arrays with Arrays.equals 746 if (!Arrays.equals(args, that.args)) { 747 return false; 748 } 749 750 return true; 751 } 752 753 public int hashCode() { 754 int result = super.hashCode(); 755 result = 31 * result + (args != null ? Arrays.hashCode(args) : 0); 756 return result; 757 } 758 } 759 760 public static class ExceptionInArgumentPredicateViolation extends PredicateViolation { 761 public final Object[] args; 762 public final Throwable t; 763 764 public ExceptionInArgumentPredicateViolation(String threadName, 765 String threadGroupName, 766 long threadId, 767 StackTraceElement[] ste, 768 String predicateClass, 769 Object[] args, 770 Throwable t) { 771 super(threadName, threadGroupName, threadId, ste, predicateClass); 772 this.args = args; 773 this.t = t; 774 } 775 776 /** 777 * Returns a string representation of the object. 778 * 779 * @return string representation 780 */ 781 public String toString() { 782 StringBuilder sb = new StringBuilder(); 783 sb.append("Exception in Thread Predicate Violation: ("); 784 sb.append(checkCount); 785 sb.append(" "); 786 sb.append(form("check", checkCount)); 787 sb.append(", "); 788 sb.append(violationCount); 789 sb.append(" "); 790 sb.append(form("violation", violationCount)); 791 sb.append(")"); 792 sb.append(LF); 793 sb.append("\tCurrent thread '"); 794 sb.append(threadName); 795 sb.append("', id "); 796 sb.append(threadId); 797 sb.append(", group '"); 798 sb.append(threadGroupName); 799 sb.append("'"); 800 sb.append(LF); 801 sb.append("\tViolated predicate @"); 802 sb.append(predicateClass.substring(1, predicateClass.length()-1).replace('/','.')); 803 sb.append(LF); 804 sb.append("\tMethod arguments"); 805 for(Object a: args) { 806 sb.append(LF); 807 sb.append("\t\t'"); 808 sb.append(a.toString()); 809 sb.append("' : "); 810 sb.append(a.getClass().getName()); 811 } 812 sb.append(LF); 813 sb.append("\tThrew "); 814 sb.append(t.toString()); 815 for(int i = 0; i < stackTrace.length; ++i) { 816 sb.append(LF); 817 sb.append("\tat "); 818 sb.append(stackTrace[i].getClassName()); 819 sb.append("."); 820 sb.append(stackTrace[i].getMethodName()); 821 sb.append(" ("); 822 sb.append(stackTrace[i].getFileName()); 823 sb.append(":"); 824 sb.append(stackTrace[i].getLineNumber()); 825 sb.append(")"); 826 } 827 828 return sb.toString(); 829 } 830 831 832 public boolean equals(Object o) { 833 if (this == o) { 834 return true; 835 } 836 if (o == null || getClass() != o.getClass()) { 837 return false; 838 } 839 if (!super.equals(o)) { 840 return false; 841 } 842 843 ArgumentPredicateViolation that = (ArgumentPredicateViolation)o; 844 845 // Probably incorrect - comparing Object[] arrays with Arrays.equals 846 if (!Arrays.equals(args, that.args)) { 847 return false; 848 } 849 850 return true; 851 } 852 853 public int hashCode() { 854 int result = super.hashCode(); 855 result = 31 * result + (args != null ? Arrays.hashCode(args) : 0); 856 return result; 857 } 858 } 859 860 public static class ReflectionPredicateViolation extends Violation { 861 public final String predicateString; 862 public final Object[] methodArgs; 863 864 public ReflectionPredicateViolation(String threadName, 865 String threadGroupName, 866 long threadId, 867 StackTraceElement[] ste, 868 String predicateString, 869 Object[] methodArgs) { 870 super(Violation.TYPE.REFLECTION_PREDICATE, threadName, threadGroupName, threadId, ste); 871 this.predicateString = predicateString; 872 this.methodArgs = methodArgs; 873 } 874 875 /** 876 * Returns a string representation of the object. 877 * 878 * @return string representation 879 */ 880 public String toString() { 881 StringBuilder sb = new StringBuilder(); 882 sb.append("Thread Reflection Predicate Violation: ("); 883 sb.append(checkCount); 884 sb.append(" "); 885 sb.append(form("check", checkCount)); 886 sb.append(", "); 887 sb.append(violationCount); 888 sb.append(" "); 889 sb.append(form("violation", violationCount)); 890 sb.append(")"); 891 sb.append(LF); 892 sb.append("\tCurrent thread '"); 893 sb.append(threadName); 894 sb.append("', id "); 895 sb.append(threadId); 896 sb.append(", group '"); 897 sb.append(threadGroupName); 898 sb.append("'"); 899 sb.append(LF); 900 sb.append("\tViolated predicate @"); 901 sb.append(predicateString); 902 if (methodArgs!=null) { 903 sb.append(LF); 904 sb.append("\tMethod arguments: "+methodArgs.length); 905 for(Object a: methodArgs) { 906 sb.append(LF); 907 sb.append("\t\t'"); 908 sb.append(a.toString()); 909 sb.append("' : "); 910 sb.append(a.getClass().getName()); 911 } 912 } 913 for(int i = 0; i < stackTrace.length; ++i) { 914 sb.append(LF); 915 sb.append("\tat "); 916 sb.append(stackTrace[i].getClassName()); 917 sb.append("."); 918 sb.append(stackTrace[i].getMethodName()); 919 sb.append(" ("); 920 sb.append(stackTrace[i].getFileName()); 921 sb.append(":"); 922 sb.append(stackTrace[i].getLineNumber()); 923 sb.append(")"); 924 } 925 926 return sb.toString(); 927 } 928 929 public boolean equals(Object o) { 930 if (this == o) { 931 return true; 932 } 933 if (o == null || getClass() != o.getClass()) { 934 return false; 935 } 936 if (!super.equals(o)) { 937 return false; 938 } 939 940 PredicateViolation that = (PredicateViolation)o; 941 942 if (predicateString != null ? !predicateString.equals(that.predicateClass) : that.predicateClass != null) { 943 return false; 944 } 945 946 return true; 947 } 948 949 public int hashCode() { 950 int result = super.hashCode(); 951 result = 31 * result + (predicateString != null ? predicateString.hashCode() : 0); 952 return result; 953 } 954 } 955 956 /** 957 * Name of the Java property that determines the log filename. 958 */ 959 public static final String LOG_FILENAME_PROPERTY = "edu.rice.cs.cunit.threadCheck.filename"; 960 961 /** 962 * Default filename for the log, if none specified by Java property. 963 */ 964 public static final String DEFAULT_LOG_FILENAME = "threadCheck.log"; 965 966 /** 967 * Default filename for the data log, if none specified by Java property. 968 */ 969 public static final String DEFAULT_DAT_SUFFIX = ".dat"; 970 971 /** 972 * True if the code currently executing is due to thread checking; used to avoid infinite recursions. 973 */ 974 private static volatile HashSet<Thread> _inCheckCode = new HashSet<Thread>(); 975 976 /** 977 * PrintWriter with the warning log. 978 */ 979 private static volatile PrintWriter _log = null; 980 981 /** 982 * ObjectOutputStream with the warning log as data. 983 */ 984 private static volatile ObjectOutputStream _logData = null; 985 986 /** 987 * Number of checks performed. 988 */ 989 private static volatile long _checkCount = 0; 990 991 /** 992 * Number of violationms found. 993 */ 994 private static volatile long _violationCount = 0; 995 996 /** 997 * Check if the current thread matches the regex for the thread name, and if that is the case, records a warning. 998 * 999 * @param threadName regex of a thread name that the current thread's name may NOT match 1000 */ 1001 public static synchronized void checkCurrentThreadName(String threadName) { 1002 final Thread ct = Thread.currentThread(); 1003 if (_inCheckCode.contains(ct)) { 1004 return; 1005 } 1006 _inCheckCode.add(ct); 1007 ++_checkCount; 1008 try { 1009 final String curName = ct.getName(); 1010 final long curId = ct.getId(); 1011 final String curGroupName = ct.getThreadGroup().getName(); 1012 if (curName.matches(threadName)) { 1013 ++_violationCount; 1014 Violation v = new NotRunByThreadNameViolation(curName, curGroupName, curId, threadName, 1015 ct.getStackTrace()); 1016 writeLog(v); 1017 } 1018 } 1019 catch(PatternSyntaxException pte) { 1020 pte.printStackTrace(); 1021 System.exit(1); 1022 } 1023 finally { 1024 _inCheckCode.remove(ct); 1025 } 1026 } 1027 1028 /** 1029 * Check if the current thread matches the thread ID, and if this is true, records a warning. 1030 * 1031 * @param threadId ID number that the current thread's ID may not match 1032 */ 1033 public static synchronized void checkCurrentThreadId(long threadId) { 1034 final Thread ct = Thread.currentThread(); 1035 if (_inCheckCode.contains(ct)) { 1036 return; 1037 } 1038 _inCheckCode.add(ct); 1039 ++_checkCount; 1040 try { 1041 final String curName = ct.getName(); 1042 final long curId = ct.getId(); 1043 final String curGroupName = ct.getThreadGroup().getName(); 1044 if (curId == threadId) { 1045 ++_violationCount; 1046 Violation v = new NotRunByIdViolation(curName, curGroupName, curId, ct.getStackTrace(), threadId); 1047 writeLog(v); 1048 } 1049 } 1050 catch(PatternSyntaxException pte) { 1051 pte.printStackTrace(); 1052 System.exit(1); 1053 } 1054 finally { 1055 _inCheckCode.remove(ct); 1056 } 1057 } 1058 1059 /** 1060 * Check if the current thread matches the regex for the thread group name, and if this is true, records a warning. 1061 * 1062 * @param threadGroupName regex of a thread group name that the current thread's group name may NOT match 1063 */ 1064 public static synchronized void checkCurrentThreadGroup(String threadGroupName) { 1065 final Thread ct = Thread.currentThread(); 1066 if (_inCheckCode.contains(ct)) { 1067 return; 1068 } 1069 _inCheckCode.add(ct); 1070 ++_checkCount; 1071 try { 1072 final String curName = ct.getName(); 1073 final long curId = ct.getId(); 1074 final String curGroupName = ct.getThreadGroup().getName(); 1075 if (curGroupName.matches(threadGroupName)) { 1076 ++_violationCount; 1077 Violation v = new NotRunByThreadGroupViolation(curName, curGroupName, curId, ct.getStackTrace(), 1078 threadGroupName); 1079 writeLog(v); 1080 } 1081 } 1082 catch(PatternSyntaxException pte) { 1083 pte.printStackTrace(); 1084 System.exit(1); 1085 } 1086 finally { 1087 _inCheckCode.remove(ct); 1088 } 1089 } 1090 1091 /** 1092 * Write to the error log, and potentially initialize it first. 1093 * @param v violation to log 1094 */ 1095 public static synchronized void writeLog(Violation v) { 1096 initLog(); 1097 _log.println(v.toString()); 1098 _log.flush(); 1099 try { 1100 _logData.writeObject(v); 1101 _logData.flush(); 1102 } 1103 catch(IOException e) { 1104 e.printStackTrace(); 1105 System.exit(1); 1106 } 1107 } 1108 1109 /** 1110 * Initialize the error log. 1111 */ 1112 private static synchronized void initLog() { 1113 if (_log == null) { 1114 String filename = System.getProperty(LOG_FILENAME_PROPERTY); 1115 if (filename == null) { 1116 filename = DEFAULT_LOG_FILENAME; 1117 } 1118 try { 1119 _log = new PrintWriter(new FileOutputStream(filename, true)); 1120 _log.println("========================================"); 1121 _log.format("Log opened %04d-%02d-%02d, %02d:%02d:%02d %s (GMT%d)", Calendar.getInstance().get( 1122 Calendar.YEAR), Calendar.getInstance().get(Calendar.MONTH), Calendar.getInstance().get( 1123 Calendar.DAY_OF_MONTH), Calendar.getInstance().get(Calendar.HOUR_OF_DAY), 1124 Calendar.getInstance().get(Calendar.MINUTE), Calendar.getInstance().get( 1125 Calendar.SECOND), Calendar.getInstance().getTimeZone().getDisplayName(Calendar.getInstance().get( 1126 Calendar.DST_OFFSET) == 0, TimeZone.SHORT), ((Calendar.getInstance().get(Calendar.ZONE_OFFSET) 1127 + Calendar.getInstance().get(Calendar.DST_OFFSET)) 1128 / 1000 / 60 / 60)); 1129 _log.println(); 1130 _log.println("----------------------------------------"); 1131 _log.flush(); 1132 } 1133 catch(FileNotFoundException e) { 1134 e.printStackTrace(); 1135 System.exit(1); 1136 } 1137 1138 filename = filename + DEFAULT_DAT_SUFFIX; 1139 try { 1140 _logData = new ObjectOutputStream(new FileOutputStream(filename, true)); 1141 } 1142 catch(IOException e) { 1143 e.printStackTrace(); 1144 System.exit(1); 1145 } 1146 } 1147 else { 1148 _log.println("----------------------------------------"); 1149 _log.flush(); 1150 } 1151 } 1152 1153 /** 1154 * Map from thread to a list of allowed thread names for the next call of checkCurrentThread_OnlyRunBy. 1155 */ 1156 private static volatile Map<Thread, HashSet<String>> _allowedNames = new HashMap<Thread, HashSet<String>>(); 1157 1158 /** 1159 * Add an allowed thread name pattern to the list. 1160 * 1161 * @param allowed regex that is allowed 1162 */ 1163 public static synchronized void addAllowedName_OnlyRunBy(String allowed) { 1164 Thread ct = Thread.currentThread(); 1165 if (_allowedNames.get(ct) == null) { 1166 _allowedNames.put(ct, new HashSet<String>()); 1167 } 1168 _allowedNames.get(ct).add(allowed); 1169 } 1170 1171 /** 1172 * Map from thread to a list of allowed ids for the next call of checkCurrentThread_OnlyRunBy. 1173 */ 1174 private static volatile Map<Thread, HashSet<Long>> _allowedIds = new HashMap<Thread, HashSet<Long>>(); 1175 1176 /** 1177 * Add an allowed id to the list. 1178 * 1179 * @param allowed id that is allowed 1180 */ 1181 public static synchronized void addAllowedId_OnlyRunBy(long allowed) { 1182 Thread ct = Thread.currentThread(); 1183 if (_allowedIds.get(ct) == null) { 1184 _allowedIds.put(ct, new HashSet<Long>()); 1185 } 1186 _allowedIds.get(ct).add(allowed); 1187 } 1188 1189 /** 1190 * Map from thread to a list of allowed thread group names for the next call of checkCurrentThread_OnlyRunBy. 1191 */ 1192 private static volatile Map<Thread, HashSet<String>> _allowedGroups = new HashMap<Thread, HashSet<String>>(); 1193 1194 /** 1195 * Add an allowed group name pattern to the list. 1196 * 1197 * @param allowed regex that is allowed 1198 */ 1199 public static synchronized void addAllowedGroup_OnlyRunBy(String allowed) { 1200 Thread ct = Thread.currentThread(); 1201 if (_allowedGroups.get(ct) == null) { 1202 _allowedGroups.put(ct, new HashSet<String>()); 1203 } 1204 _allowedGroups.get(ct).add(allowed); 1205 } 1206 1207 /** 1208 * Map from thread to a boolean indicating whether the event thread is allowed to run. 1209 */ 1210 private static volatile Map<Thread, Boolean> _allowEventThread = new HashMap<Thread, Boolean>(); 1211 1212 /** 1213 * Allow the event thread at the next call of checkCurrentThread_OnlyRunBy. 1214 * 1215 * @param realized true if the component has been realized (or if the check should always be performed) 1216 */ 1217 public static synchronized void setAllowedEventThread_OnlyRunBy(boolean realized) { 1218 _allowEventThread.put(Thread.currentThread(), realized); 1219 } 1220 1221 /** 1222 * Check if the current thread's name, id or, or group matches any of items in the three _allowed* lists, and if 1223 * that is NOT the case, records a warning. 1224 */ 1225 public static synchronized void checkCurrentThread_OnlyRunBy() { 1226 final Thread ct = Thread.currentThread(); 1227 if (_inCheckCode.contains(ct) || ((!_allowEventThread.containsKey(ct)) && ((_allowedNames.get(ct) == null) || ( 1228 _allowedNames.get(ct).size() == 0)) && ((_allowedIds.get(ct) == null) || (_allowedIds.get(ct).size() == 0)) 1229 && ((_allowedGroups.get(ct) == null) || (_allowedGroups.get(ct).size() 1230 == 0)))) { 1231 return; 1232 } 1233 _inCheckCode.add(ct); 1234 ++_checkCount; 1235 try { 1236 final String curName = ct.getName(); 1237 final long curId = ct.getId(); 1238 final String curGroupName = ct.getThreadGroup().getName(); 1239 boolean matches = false; 1240 if (_allowEventThread.containsKey(ct)) { 1241 if (_allowEventThread.get(ct)) { 1242 // realized == true, we care whether it's the event thread 1243 matches = EventQueue.isDispatchThread(); 1244 } 1245 else { 1246 // realized == false, we don't care 1247 matches = true; 1248 } 1249 } 1250 if ((!matches) && (_allowedNames.get(ct) != null)) { 1251 for(String regex : _allowedNames.get(ct)) { 1252 try { 1253 matches |= curName.matches(regex); 1254 if (matches) { 1255 break; 1256 } 1257 } 1258 catch(PatternSyntaxException pte) { 1259 pte.printStackTrace(); 1260 System.exit(1); 1261 } 1262 } 1263 } 1264 if ((!matches) && (_allowedIds.get(ct) != null)) { 1265 for(long id : _allowedIds.get(ct)) { 1266 matches |= (curId == id); 1267 if (matches) { 1268 break; 1269 } 1270 } 1271 } 1272 if ((!matches) && (_allowedGroups.get(ct) != null)) { 1273 for(String regex : _allowedGroups.get(ct)) { 1274 try { 1275 matches |= curGroupName.matches(regex); 1276 if (matches) { 1277 break; 1278 } 1279 } 1280 catch(PatternSyntaxException pte) { 1281 pte.printStackTrace(); 1282 System.exit(1); 1283 } 1284 } 1285 } 1286 if (!matches) { 1287 ++_violationCount; 1288 Violation v = new OnlyRunByViolation(curName, curGroupName, curId, ct.getStackTrace(), 1289 _allowEventThread.containsKey(ct), _allowedNames.get(ct), 1290 _allowedGroups.get(ct), _allowedIds.get(ct)); 1291 writeLog(v); 1292 } 1293 } 1294 finally { 1295 if (_allowedNames.get(ct) != null) { 1296 _allowedNames.get(ct).clear(); 1297 } 1298 if (_allowedIds.get(ct) != null) { 1299 _allowedIds.get(ct).clear(); 1300 } 1301 if (_allowedGroups.get(ct) != null) { 1302 _allowedGroups.get(ct).clear(); 1303 } 1304 _allowEventThread.remove(ct); 1305 _inCheckCode.remove(ct); 1306 } 1307 } 1308 1309 /** 1310 * Check if a predicate has failed, and if so, log a violation. 1311 * @param predicateResult result of the call to the predicate method 1312 * @param predicateClass name of the predicate (name of the annotation) 1313 */ 1314 public static synchronized void checkCurrentThread_Predicate(boolean predicateResult, String predicateClass) { 1315 final Thread ct = Thread.currentThread(); 1316 if (_inCheckCode.contains(ct)) { 1317 return; 1318 } 1319 _inCheckCode.add(ct); 1320 ++_checkCount; 1321 if (!predicateResult) { 1322 final String curName = ct.getName(); 1323 final long curId = ct.getId(); 1324 final String curGroupName = ct.getThreadGroup().getName(); 1325 ++_violationCount; 1326 Violation v = new PredicateViolation(curName, curGroupName, curId, ct.getStackTrace(), predicateClass); 1327 writeLog(v); 1328 } 1329 _inCheckCode.remove(ct); 1330 } 1331 1332 /** 1333 * Check if a predicate has failed, and if so, log a violation. 1334 * @param args method arguments 1335 * @param predicateResult result of the call to the predicate method 1336 * @param predicateClass name of the predicate (name of the annotation) 1337 */ 1338 public static synchronized void checkCurrentThread_Predicate(Object[] args, boolean predicateResult, String predicateClass) { 1339 final Thread ct = Thread.currentThread(); 1340 if (_inCheckCode.contains(ct)) { 1341 return; 1342 } 1343 _inCheckCode.add(ct); 1344 ++_checkCount; 1345 if (!predicateResult) { 1346 final String curName = ct.getName(); 1347 final long curId = ct.getId(); 1348 final String curGroupName = ct.getThreadGroup().getName(); 1349 ++_violationCount; 1350 Violation v = new ArgumentPredicateViolation(curName, curGroupName, curId, ct.getStackTrace(), predicateClass, args); 1351 writeLog(v); 1352 } 1353 _inCheckCode.remove(ct); 1354 } 1355 1356 /** 1357 * Log an exception in a predicate. 1358 * @param t exception thrown 1359 * @param predicateClass name of the predicate (name of the annotation) 1360 */ 1361 public static synchronized void checkCurrentThread_PredicateException(Throwable t, String predicateClass) { 1362 final Thread ct = Thread.currentThread(); 1363 if (_inCheckCode.contains(ct)) { 1364 return; 1365 } 1366 _inCheckCode.add(ct); 1367 ++_checkCount; 1368 final String curName = ct.getName(); 1369 final long curId = ct.getId(); 1370 final String curGroupName = ct.getThreadGroup().getName(); 1371 ++_violationCount; 1372 Violation v = new ExceptionInPredicateViolation(curName, curGroupName, curId, ct.getStackTrace(), predicateClass, t); 1373 writeLog(v); 1374 _inCheckCode.remove(ct); 1375 } 1376 1377 /** 1378 * Log an exception in a predicate. 1379 * @param t exception thrown 1380 * @param args method arguments 1381 * @param predicateClass name of the predicate (name of the annotation) 1382 */ 1383 public static synchronized void checkCurrentThread_PredicateException(Throwable t, Object[] args, String predicateClass) { 1384 final Thread ct = Thread.currentThread(); 1385 if (_inCheckCode.contains(ct)) { 1386 return; 1387 } 1388 _inCheckCode.add(ct); 1389 ++_checkCount; 1390 final String curName = ct.getName(); 1391 final long curId = ct.getId(); 1392 final String curGroupName = ct.getThreadGroup().getName(); 1393 ++_violationCount; 1394 Violation v = new ExceptionInArgumentPredicateViolation(curName, curGroupName, curId, ct.getStackTrace(), predicateClass, args, t); 1395 writeLog(v); 1396 _inCheckCode.remove(ct); 1397 } 1398 1399 /** 1400 * Checks if the current thread may execute the method from where this method was called purely using reflection. 1401 * If it may not execute, a violation is logged. 1402 * @param callerClass class of the caller 1403 * @param methodName name of the method 1404 * @param methodDesc descriptor of the method 1405 * @param thisO value of this, or null in a static context 1406 * @param methodArgs array of method arguments, or null if none passed 1407 */ 1408 public static synchronized void checkCurrentThreadReflection(Class callerClass, String methodName, String methodDesc, Object thisO, Object[] methodArgs) { 1409 final Thread ct = Thread.currentThread(); 1410 if (_inCheckCode.contains(ct)) { 1411 return; 1412 } 1413 _inCheckCode.add(ct); 1414 try { 1415 // System.out.println("Class = "+callerClass.getName()); 1416 // System.out.println("Method = "+methodName); 1417 // System.out.println("Desc = "+methodDesc); 1418 // System.out.println("this = "+thisO); 1419 1420 // determine the calling method 1421 Method callerMethod = getMethodWithNameAndDescriptor(callerClass, methodName, methodDesc); 1422 if (callerMethod==null) { 1423 initLog(); 1424 _log.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); 1425 _log.println("Thread Checker could not find method "+methodName+methodDesc+" in class "+callerClass.getName()); 1426 _log.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); 1427 _log.flush(); 1428 return; 1429 } 1430 1431 // System.out.println("Caller method = "+callerMethod); 1432 1433 // handle @PredicateLink and @Combine annotations 1434 ThreadCheckAnnotationRecord ar = getMethodAnnotations(callerClass, callerMethod); 1435 1436 // System.out.println("@PredicateLinks: "+ar.predicateLinkAnnotations.size()); 1437 // System.out.println("@Combines: "+ar.combineAnnotations.size()); 1438 1439 for(Pair<Annotation,PredicateLink> p: ar.predicateLinkAnnotations) { 1440 // System.out.println("@PredicateLink: "+p.first()+", "+p.second()); 1441 ++_checkCount; 1442 Boolean ret = checkPredicateLinkAnnotation(p.first(), p.second(), thisO, methodArgs); 1443 if ((ret!=null) && (ret==false)) { 1444 // we have a violation 1445 // System.out.println("\tViolation!"); 1446 ++_violationCount; 1447 Violation v = new ReflectionPredicateViolation(ct.getName(), ct.getThreadGroup().getName(), ct.getId(), ct.getStackTrace(), 1448 p.first().toString(), methodArgs); 1449 writeLog(v); 1450 } 1451 } 1452 for(Pair<Annotation,Combine> p: ar.combineAnnotations) { 1453 // System.out.println("@Combine: "+p.first()+", "+p.second()); 1454 ++_checkCount; 1455 Boolean ret = checkCombineAnnotation(p.first(), p.second(), thisO, methodArgs); 1456 if ((ret!=null) && (ret==false)) { 1457 // we have a violation 1458 ++_violationCount; 1459 Violation v = new ReflectionPredicateViolation(ct.getName(), ct.getThreadGroup().getName(), ct.getId(), ct.getStackTrace(), 1460 p.first().toString(), methodArgs); 1461 writeLog(v); 1462 } 1463 } 1464 } 1465 finally { 1466 _inCheckCode.remove(ct); 1467 } 1468 } 1469 1470 /** 1471 * Handle a @PredicateLink-style annotation. 1472 * @param ann annotation 1473 * @param pl associated @PredicateLink 1474 * @param thisO value of this, or null 1475 * @return false if there was a violation, or null if there was an error 1476 */ 1477 public static Boolean checkPredicateLinkAnnotation(Annotation ann, PredicateLink pl, Object thisO, Object[] methodArgs) { 1478 Class predicateClass = pl.value(); 1479 String predicateMethodName = pl.method(); 1480 Class annClass = ann.annotationType(); 1481 Method[] annMethods = annClass.getDeclaredMethods(); 1482 int parenIndex = predicateMethodName.indexOf('('); 1483 if (parenIndex>=0) { 1484 if (!predicateMethodName.endsWith(")")) { 1485 initLog(); 1486 _log.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); 1487 _log.println("Thread Checker found a bad predicate method name with parameter list that did not end in ')': "+predicateMethodName); 1488 _log.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); 1489 _log.flush(); 1490 return null; 1491 } 1492 // strip parameter name list in parentheses from name: "methodName(param1,param2)" 1493 String s = predicateMethodName.substring(parenIndex + 1, predicateMethodName.length() - 1); 1494 String[] paramNames = s.split(","); 1495 ArrayList<Method> tempMethods = new ArrayList<Method>(); 1496 predicateMethodName = predicateMethodName.substring(0,parenIndex); 1497 if (s.length()>0) { 1498 for(int i=0; i<annMethods.length; ++i) { 1499 if (paramNames[i].length()>0) { 1500 for(Method m: annClass.getDeclaredMethods()) { 1501 if (m.getName().equals(paramNames[i])) { 1502 tempMethods.add(m); 1503 break; 1504 } 1505 } 1506 if (annMethods[i]==null) { 1507 initLog(); 1508 _log.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); 1509 _log.println("Thread Checker found a bad predicate method name with parameter list: that contained"); 1510 _log.println("the parameter "+paramNames[i]+", which wasn't found in the annotation."); 1511 _log.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); 1512 _log.flush(); 1513 return null; 1514 } 1515 } 1516 } 1517 } 1518 annMethods = tempMethods.toArray(new Method[0]); 1519 } 1520 Method predicateMethod = null; 1521 int start = 1; 1522 if (pl.arguments()) { 1523 ++start; 1524 } 1525 for(Method m: predicateClass.getMethods()) { 1526 // initLog(); 1527 // _log.println("Considering "+m+"..."); 1528 if ((m.getName().equals(predicateMethodName)) && 1529 ((m.getModifiers() & Modifier.STATIC)!=0) && 1530 (m.getReturnType().isPrimitive()) && 1531 (m.getReturnType().getName().equals("boolean")) && 1532 (m.getParameterTypes().length >= start) && 1533 (m.getParameterTypes()[0].equals(Object.class))) { 1534 // _log.println("- static, boolean, takes Object"); 1535 // _log.println("- "+m.getParameterTypes()[1].getName()); 1536 if (pl.arguments()) { 1537 if (!m.getParameterTypes()[1].getName().equals("[Ljava.lang.Object;")) { 1538 continue; 1539 } 1540 } 1541 // _log.println("- takes Object[]"); 1542 // name matches, is static, returns boolean, and takes Object as first parameter 1543 // check parameters 1544 // _log.println("- m.getParameterTypes().length = "+m.getParameterTypes().length+", start = "+start+ 1545 // ", annMethods.length = "+annMethods.length); 1546 if (m.getParameterTypes().length-start!=annMethods.length) { 1547 // wrong number of parameters 1548 // _log.println("- wrong number of parameters"); 1549 continue; 1550 } 1551 boolean found = true; 1552 for (int i=start; i<m.getParameterTypes().length; ++i) { 1553 Class pt = m.getParameterTypes()[i]; 1554 if (!pt.equals(annMethods[i-start].getReturnType())) { 1555 found = false; 1556 break; 1557 } 1558 } 1559 if (found) { 1560 // parameters matched 1561 predicateMethod = m; 1562 break; 1563 } 1564 } 1565 } 1566 if (predicateMethod==null) { 1567 initLog(); 1568 // TODO: allow for subclassing 1569 _log.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); 1570 _log.println("Thread Checker could not find predicate method with name "+predicateMethodName+ 1571 " in class "+predicateClass.getName()+" suitable for annotation "+annClass.getName()); 1572 _log.println("To be suitable, a predicate method has to have the following parameters in the right order:"); 1573 _log.println("\tjava.lang.Object"); 1574 if (pl.arguments()) { 1575 _log.println("\tjava.lang.Object[]"); 1576 } 1577 for(int i=0; i<annMethods.length; ++i) { 1578 Method annMethod = annMethods[i]; 1579 _log.println("\t"+annMethod.getReturnType()); 1580 } 1581 _log.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); 1582 _log.flush(); 1583 return null; 1584 } 1585 1586 // System.out.println("Predicate method: "+predicateMethod); 1587 1588 Object[] params = new Object[predicateMethod.getParameterTypes().length]; 1589 params[0] = thisO; 1590 if (pl.arguments()) { 1591 params[1] = methodArgs; 1592 } 1593 for(int i=start; i<params.length; ++i) { 1594 Method annMethod = annMethods[i-start]; 1595 // System.out.println("Invoking annotation method "+annMethod); 1596 try { 1597 annMethod.setAccessible(true); 1598 params[i] = annMethod.invoke(ann); 1599 } 1600 catch(IllegalAccessException e) { 1601 initLog(); 1602 _log.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); 1603 _log.println("Thread Checker could not invoke annotation method "+annMethod+": "+ 1604 e.toString()); 1605 _log.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); 1606 _log.flush(); 1607 return null; 1608 } 1609 catch(InvocationTargetException e) { 1610 initLog(); 1611 _log.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); 1612 _log.println("Thread Checker could not invoke annotation method "+annMethod+": "+ 1613 e.toString()); 1614 _log.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); 1615 _log.flush(); 1616 return null; 1617 } 1618 } 1619 try { 1620 predicateMethod.setAccessible(true); 1621 Object retval = predicateMethod.invoke(null, params); 1622 if (!retval.getClass().equals(Boolean.class)) { 1623 initLog(); 1624 _log.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); 1625 _log.println("Thread Checker invoked predicate method "+predicateMethod+" in class "+predicateClass.getName()+ 1626 ", but the result was not a boolean."); 1627 _log.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); 1628 _log.flush(); 1629 return null; 1630 } 1631 return (Boolean)retval; 1632 } 1633 catch(IllegalAccessException e) { 1634 initLog(); 1635 _log.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); 1636 _log.println("Thread Checker could not invoke invoked predicate method "+predicateMethod+" in class "+predicateClass.getName()+ 1637 ": "+e.toString()); 1638 _log.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); 1639 _log.flush(); 1640 return null; 1641 } 1642 catch(InvocationTargetException e) { 1643 initLog(); 1644 _log.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); 1645 _log.println("Thread Checker could not invoke invoked predicate method "+predicateMethod+" in class "+predicateClass.getName()+ 1646 ": "+e.toString()); 1647 _log.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); 1648 _log.flush(); 1649 return null; 1650 } 1651 } 1652 1653 /** 1654 * Handle a @Combine-style annotation. 1655 * @param ann annotation 1656 * @param co associated @Combine 1657 * @param thisO value of this, or null 1658 * @param methodArgs array of method arguments, or null if none passed 1659 * @return false if there was a violation, or null if there was an error 1660 */ 1661 public static Boolean checkCombineAnnotation(Annotation ann, Combine co, Object thisO, Object[] methodArgs) { 1662 Combine.Mode mode = co.value(); 1663 // System.out.println("checkCombineAnnotation: "+ann); 1664 // set up starting value: AND/NOT = true, OR/XOR = false, 0 for XOR 1665 boolean result = true; 1666 int count = 0; 1667 if ((mode==Combine.Mode.OR) || 1668 (mode==Combine.Mode.XOR)) { result = false; } 1669 1670 Class<? extends Annotation> annClass = ann.annotationType(); 1671 // System.out.println("Processing annotation "+ann); 1672 for (Method m: annClass.getDeclaredMethods()) { 1673 Class<?> retClass = m.getReturnType(); 1674 // System.out.println("\tMethod "+m); 1675 // System.out.println("\t\tretClass = "+retClass+", annotation? "+retClass.isAnnotation()); 1676 boolean isAnnotation = retClass.isAnnotation(); 1677 if (retClass.isArray()) { 1678 // it's an array, make sure it's an array of annotations 1679 do { 1680 retClass = retClass.getComponentType(); 1681 if (retClass!=null) { isAnnotation = retClass.isAnnotation(); } 1682 } while((retClass!=null) && (retClass.isArray())); 1683 } 1684 if (!isAnnotation) { 1685 // not an annotation, illegal in a @Combine-style annotation 1686 initLog(); 1687 _log.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); 1688 _log.println("Thread Checker found a @Combine-style annotation "+ann+" that had a non-annotation member."); 1689 _log.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); 1690 _log.flush(); 1691 return null; 1692 } 1693 PredicateLink memberPL = retClass.getAnnotation(PredicateLink.class); 1694 Combine memberCO = retClass.getAnnotation(Combine.class); 1695 if ((memberPL!=null) && (memberCO!=null)) { 1696 initLog(); 1697 _log.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); 1698 _log.println("Thread Checker found a @Combine-style annotation "+ann+" that had a member that had " 1699 + "both a @PredicateLink and a @Combine meta-annotation."); 1700 _log.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); 1701 _log.flush(); 1702 } 1703 if ((memberPL==null) && (memberCO==null)) { 1704 initLog(); 1705 _log.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); 1706 _log.println("Thread Checker found a @Combine-style annotation "+ann+" that had a member that had " 1707 + "neither a @PredicateLink nor a @Combine meta-annotation."); 1708 _log.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); 1709 _log.flush(); 1710 } 1711 1712 // invoke method to get member annotation 1713 Object retObj; 1714 try { 1715 m.setAccessible(true); 1716 retObj = m.invoke(ann); 1717 } 1718 catch(IllegalAccessException e) { 1719 initLog(); 1720 _log.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); 1721 _log.println("Thread Checker could not invoke annotation method "+m+": "+ 1722 e.toString()); 1723 _log.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); 1724 _log.flush(); 1725 return null; 1726 } 1727 catch(InvocationTargetException e) { 1728 initLog(); 1729 _log.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); 1730 _log.println("Thread Checker could not invoke annotation method "+m+": "+ 1731 e.toString()); 1732 _log.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); 1733 _log.flush(); 1734 return null; 1735 } 1736 1737 if (!retObj.getClass().isArray()) { 1738 Annotation member = (Annotation)retObj; 1739 Boolean retval = processCombineMember(member, memberPL, memberCO, thisO, methodArgs); 1740 if (retval==null) { 1741 // some error occurred 1742 initLog(); 1743 _log.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); 1744 _log.println("Thread Checker encountered an error while processing member annotation "+member+ 1745 " of annotation "+ann); 1746 _log.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); 1747 _log.flush(); 1748 return null; 1749 } 1750 1751 ++count; 1752 switch(mode) { 1753 case OR: { 1754 result |= retval; 1755 break; 1756 } 1757 case AND: { 1758 result &= retval; 1759 break; 1760 } 1761 case NOT: { 1762 if (count>1) { 1763 // not is a unary operation 1764 initLog(); 1765 _log.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); 1766 _log.println("Thread Checker found a @Combine-style annotation "+ann+" that used NOT with not exactly one operand."); 1767 _log.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); 1768 _log.flush(); 1769 return null; 1770 } 1771 result = !retval; 1772 break; 1773 } 1774 case XOR: { 1775 result = (count==1); 1776 break; 1777 } 1778 case IMPLIES: { 1779 // implies must be a single array with two elements 1780 initLog(); 1781 _log.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); 1782 _log.println("Thread Checker found a @Combine-style annotation "+ann+" that used IMPLIES with not exactly one array with two elements."); 1783 _log.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); 1784 _log.flush(); 1785 break; 1786 } 1787 } 1788 } 1789 else { 1790 Annotation[] members = (Annotation[])retObj; 1791 for(Annotation member: members) { 1792 Boolean retval = processCombineMember(member, memberPL, memberCO, thisO, methodArgs); 1793 if (retval==null) { 1794 // some error occurred 1795 initLog(); 1796 _log.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); 1797 _log.println("Thread Checker encountered an error while processing member annotation "+member+ 1798 " of annotation "+ann); 1799 _log.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); 1800 _log.flush(); 1801 return null; 1802 } 1803 1804 ++count; 1805 boolean impliesOp1 = false; 1806 switch(mode) { 1807 case OR: { 1808 result |= retval; 1809 break; 1810 } 1811 case AND: { 1812 result &= retval; 1813 break; 1814 } 1815 case NOT: { 1816 if (count>1) { 1817 // not is a unary operation 1818 initLog(); 1819 _log.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); 1820 _log.println("Thread Checker found a @Combine-style annotation "+ann+" that used NOT with not exactly one operand."); 1821 _log.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); 1822 _log.flush(); 1823 return null; 1824 } 1825 result = !retval; 1826 break; 1827 } 1828 case XOR: { 1829 result = (count==1); 1830 break; 1831 } 1832 case IMPLIES: { 1833 if (count>2) { 1834 // implies must be a single array with two elements 1835 initLog(); 1836 _log.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); 1837 _log.println("Thread Checker found a @Combine-style annotation "+ann+" that used IMPLIES with not exactly one array with two elements."); 1838 _log.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); 1839 _log.flush(); 1840 break; 1841 } 1842 else if (count<2) { 1843 // store the first time 1844 impliesOp1 = retval; 1845 } 1846 if (count==2) { 1847 // perform the second time 1848 // P Q (P->Q) 1849 // T T T->T = T 1850 // T F T=>F = F 1851 // F T F->T = T 1852 // F F F->F = T 1853 // (NOT A) OR B 1854 result = !impliesOp1 || retval; 1855 } 1856 break; 1857 } 1858 } 1859 } 1860 } 1861 } 1862 // System.out.println("\tResult = "+result); 1863 // System.out.flush(); 1864 1865 return result; 1866 } 1867 1868 /** 1869 * Process the value of a member annotation in a @Combine-style annotation. 1870 * @param member member annotation 1871 * @param memberPL @PredicateLink meta-annotation associated with this member 1872 * @param memberCO @Combine meta-annotation associated with this member 1873 * @param thisO value of this 1874 * @param methodArgs array of method arguments, or null if none passed 1875 * @return true if permitted, false if violation, or null if error occurred. 1876 */ 1877 private static Boolean processCombineMember(Annotation member, PredicateLink memberPL, Combine memberCO, 1878 Object thisO, Object[] methodArgs) { 1879 Boolean retval; 1880 if (memberPL!=null) { 1881 // member is a @PredicateLink-style annotation 1882 // System.out.println("\tRecursing into @PredicateLink annotation "+member+" with @PredicateLink = "+memberPL); 1883 retval = checkPredicateLinkAnnotation(member, memberPL, thisO, methodArgs); 1884 } 1885 else { 1886 // member is a @Combine-style annotation 1887 // System.out.println("\tRecursing into @Combine annotation "+member+" with @Combine = "+memberCO); 1888 retval = checkCombineAnnotation(member, memberCO, thisO, methodArgs); 1889 } 1890 // System.out.println("\tResult = "+retval); 1891 return retval; 1892 } 1893 1894 /** 1895 * Return the method with the specified name and descriptor. 1896 * @param methodClass class 1897 * @param methodName name 1898 * @param methodDesc descriptor 1899 * @return method, or null 1900 */ 1901 public static Method getMethodWithNameAndDescriptor(Class methodClass, String methodName, String methodDesc) { 1902 Method callerMethod = null; 1903 for(Method m: methodClass.getDeclaredMethods()) { 1904 if (m.getName().equals(methodName)) { 1905 String desc = getMethodDescriptor(m); 1906 if (desc.toString().equals(methodDesc)) { 1907 callerMethod = m; 1908 break; 1909 } 1910 } 1911 } 1912 return callerMethod; 1913 } 1914 1915 /** 1916 * Return the method descriptor for the method. 1917 * @param m method 1918 * @return descriptor 1919 */ 1920 public static String getMethodDescriptor(Method m) { 1921 StringBuilder sb = new StringBuilder(); 1922 sb.append('('); 1923 for(Class pt: m.getParameterTypes()) { 1924 String n = pt.getName(); 1925 if (!pt.isPrimitive()) { 1926 // reference 1927 sb.append(n.replace('.','/')); 1928 } 1929 else { 1930 // primitive value 1931 char c = ClassFileTools.getPrimitiveTypeChar(n); 1932 if (c==0) { 1933 System.err.println("Unknown primitive type in Thread Checker: "+n); 1934 System.exit(1); 1935 } 1936 else { sb.append(c); } 1937 } 1938 } 1939 sb.append(')'); 1940 String n = m.getReturnType().getName(); 1941 if (!m.getReturnType().isPrimitive()) { 1942 // reference return type 1943 sb.append(n.replace('.','/')); 1944 } 1945 else { 1946 // primitive return type 1947 char c = ClassFileTools.getPrimitiveTypeChar(n); 1948 if (c==0) { 1949 System.err.println("Unknown primitive type in Thread Checker: "+n); 1950 System.exit(1); 1951 } 1952 else { sb.append(c); } 1953 } 1954 return sb.toString(); 1955 } 1956 1957 /** 1958 * Return the correct form, singular or plural, of the word, depending on the count. 1959 * @param word word 1960 * @param count count 1961 * @return word+"s" if count!=1, or word if count==1 1962 */ 1963 public static String form(String word, long count) { return (count==1)?word:(word+'s'); } 1964 1965 /** 1966 * Storage class for predicate annotations. 1967 */ 1968 public static class ThreadCheckAnnotationRecord { 1969 public HashSet<Pair<Annotation,PredicateLink>> predicateLinkAnnotations; 1970 public HashSet<Pair<Annotation,Combine>> combineAnnotations; 1971 public boolean suppressSubtypingWarning = false; 1972 1973 public ThreadCheckAnnotationRecord() { 1974 predicateLinkAnnotations = new HashSet<Pair<Annotation,PredicateLink>>(); 1975 combineAnnotations = new HashSet<Pair<Annotation,Combine>>(); 1976 } 1977 1978 public void add(ThreadCheckAnnotationRecord otherAR) { 1979 predicateLinkAnnotations.addAll(otherAR.predicateLinkAnnotations); 1980 combineAnnotations.addAll(otherAR.combineAnnotations); 1981 } 1982 } 1983 1984 /** 1985 * Get the annotations for the specified method in the specified class file. 1986 * @param cf class file 1987 * @param mi method information 1988 * @return annotations 1989 */ 1990 public static ThreadCheckAnnotationRecord getMethodAnnotations(Class cf, Method mi) { 1991 ThreadCheckAnnotationRecord methodAR = getPredicateSets(mi.getAnnotations()); 1992 1993 // a set of method signatures of the form "<method name><descriptor>" that describe methods that have 1994 // been defined in superclasses or implemented interfaces already and to which the class level annotations 1995 // on THIS class should therefore NOT apply 1996 HashSet<String> definedAbove = new HashSet<String>(); 1997 1998 if (cf.getSuperclass()!=null) { 1999 // find the annotations for the same method in the superclass 2000 boolean found = false; 2001 Class curcf = cf; 2002 2003 while(!found && curcf.getSuperclass()!=null) { 2004 Class scf = curcf.getSuperclass(); 2005 for(Method smi : scf.getMethods()) { 2006 definedAbove.add(smi.getName()+getMethodDescriptor(smi)); 2007 if ((smi.getName().equals(mi.getName()) && 2008 (getMethodDescriptor(smi).equals(getMethodDescriptor(mi))))) { 2009 ThreadCheckAnnotationRecord superClassMethodAR = getMethodAnnotations(scf, smi); 2010 if (!methodAR.suppressSubtypingWarning) { 2011 // also check for subtyping warnings with predicate annotations? 2012 checkForSubtypingClassWarnings(cf, mi, scf, methodAR, superClassMethodAR); 2013 } 2014 methodAR.add(superClassMethodAR); 2015 found = true; 2016 } 2017 } 2018 if (!found) { 2019 curcf = scf; 2020 } 2021 } 2022 2023 // find the annotations for all the interfaces 2024 for(Class icf: cf.getInterfaces()) { 2025 found = false; 2026 Class scf = icf; 2027 while(!found && scf!=null) { 2028 for(Method smi : scf.getMethods()) { 2029 definedAbove.add(smi.getName()+getMethodDescriptor(smi)); 2030 if ((smi.getName().equals(mi.getName()) && 2031 (getMethodDescriptor(smi).equals(getMethodDescriptor(mi))))) { 2032 ThreadCheckAnnotationRecord interfClassMethodAR = getMethodAnnotations(scf, smi); 2033 if (!methodAR.suppressSubtypingWarning) { 2034 // also check for subtyping warnings with predicate annotations? 2035 checkForSubtypingClassWarnings(cf, mi, scf, methodAR, interfClassMethodAR); 2036 } 2037 methodAR.add(interfClassMethodAR); 2038 found = true; 2039 } 2040 } 2041 if (!found) { 2042 scf = scf.getSuperclass(); 2043 } 2044 } 2045 } 2046 } 2047 2048 // add class level annotations, but only for those methods not mentioned in definedAbove 2049 if (!definedAbove.contains(mi.getName()+getMethodDescriptor(mi))) { 2050 methodAR.add(getClassAnnotations(cf)); 2051 } 2052 2053 return methodAR; 2054 } 2055 2056 /** 2057 * Get the annotations for the specified class file. 2058 * @param cf class file 2059 * @return annotations 2060 */ 2061 public static ThreadCheckAnnotationRecord getClassAnnotations(Class cf) { 2062 ThreadCheckAnnotationRecord classAR = getPredicateSets(cf.getAnnotations()); 2063 2064 if (cf.getSuperclass()!=null) { 2065 ThreadCheckAnnotationRecord superClassAR = getClassAnnotations(cf.getSuperclass()); 2066 classAR.add(superClassAR); 2067 2068 // find the annotations for all the interfaces 2069 for(Class icf: cf.getInterfaces()) { 2070 ThreadCheckAnnotationRecord interfClassAR = getClassAnnotations(icf); 2071 classAR.add(interfClassAR); 2072 } 2073 } 2074 2075 return classAR; 2076 } 2077 2078 2079 /** 2080 * Get the predicate sets from the array of annotations. 2081 * @param annotations array of annotations 2082 * @return annotation record. 2083 */ 2084 public static ThreadCheckAnnotationRecord getPredicateSets(Annotation[] annotations) { 2085 ThreadCheckAnnotationRecord ar = new ThreadCheckAnnotationRecord(); 2086 for (Annotation ann: annotations) { 2087 PredicateLink pl = ann.annotationType().getAnnotation(PredicateLink.class); 2088 if (pl!=null) { 2089 ar.predicateLinkAnnotations.add(new Pair<Annotation,PredicateLink>(ann, pl)); 2090 } 2091 Combine co = ann.annotationType().getAnnotation(Combine.class); 2092 if (co!=null) { 2093 ar.combineAnnotations.add(new Pair<Annotation,Combine>(ann, co)); 2094 } 2095 if (ann.annotationType().getAnnotation(SuppressSubtypingWarning.class)!=null) { 2096 ar.suppressSubtypingWarning = true; 2097 } 2098 } 2099 return ar; 2100 } 2101 2102 /** 2103 * Check for subtyping warnings. 2104 * @param cf class file 2105 * @param mi method information, or null if on class level 2106 * @param scf superclass/interface class file 2107 * @param classAR subclass annotations 2108 * @param superClassAR superclass annotations 2109 */ 2110 public static void checkForSubtypingClassWarnings(Class cf, 2111 Method mi, 2112 Class scf, 2113 ThreadCheckAnnotationRecord classAR, 2114 ThreadCheckAnnotationRecord superClassAR) { 2115 String methodName = null, methodDesc = null; 2116 if (mi!=null) { 2117 methodName = mi.getName(); 2118 methodDesc = getMethodDescriptor(mi); 2119 } 2120 2121 // check for subtyping warnings with predicate annotations 2122 for(Pair<Annotation,PredicateLink> pair: classAR.predicateLinkAnnotations) { 2123 if (!superClassAR.predicateLinkAnnotations.contains(pair)) { 2124 initLog(); 2125 _log.print("ThreadChecker Reflection Subtyping Warnings: "); 2126 if (mi==null) { 2127 // class level warning 2128 _log.print(cf.getName()); 2129 _log.print(" has predicate annotation "); 2130 _log.print(pair.first()); 2131 _log.print(" but "); 2132 _log.print(scf.getName()); 2133 } 2134 else { 2135 // method level warning 2136 _log.print(cf.getName()); 2137 _log.print('.'); 2138 _log.print(mi.getName()); 2139 _log.print(getMethodDescriptor(mi)); 2140 _log.print(" has predicate annotation "); 2141 _log.print(pair.first()); 2142 _log.print(" but "); 2143 _log.print(scf.getName()); 2144 _log.print('.'); 2145 _log.print(mi.getName()); 2146 _log.print(getMethodDescriptor(mi)); 2147 } 2148 _log.println(" does not"); 2149 _log.flush(); 2150 } 2151 } 2152 } 2153 }