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 }