001 package edu.rice.cs.cunit.instrumentors.threadCheck;
002
003 import edu.rice.cs.cunit.FileInstrumentor;
004 import edu.rice.cs.cunit.classFile.ClassFile;
005 import edu.rice.cs.cunit.classFile.ClassFileTools;
006 import edu.rice.cs.cunit.classFile.MethodInfo;
007 import edu.rice.cs.cunit.classFile.attributes.*;
008 import edu.rice.cs.cunit.classFile.attributes.visitors.ADefaultAttributeVisitor;
009 import edu.rice.cs.cunit.classFile.constantPool.*;
010 import edu.rice.cs.cunit.classFile.constantPool.visitors.ADefaultPoolInfoVisitor;
011 import edu.rice.cs.cunit.classFile.constantPool.visitors.CheckUTFVisitor;
012 import edu.rice.cs.cunit.classFile.constantPool.visitors.CheckIntegerVisitor;
013 import edu.rice.cs.cunit.instrumentors.IInstrumentationStrategy;
014 import edu.rice.cs.cunit.instrumentors.util.IScannerStrategy;
015 import edu.rice.cs.cunit.util.Ref;
016 import edu.rice.cs.cunit.threadCheck.*;
017 import edu.rice.cs.cunit.util.Debug;
018
019 import java.io.ByteArrayOutputStream;
020 import java.io.FileOutputStream;
021 import java.io.IOException;
022 import java.util.*;
023
024 /**
025 * Abstract instrumentor with core routines.
026 *
027 * @author Mathias Ricken
028 */
029 public abstract class AThreadCheckStrategy implements IInstrumentationStrategy {
030
031 /**
032 * Data shared among all AAddThreadCheckStrategy instances.
033 * This is an IScannerStrategy so that the warnings can be reported from a central place.
034 */
035 public static class SharedData implements IScannerStrategy {
036 /**
037 * Warnings about badly formatted predicate annotations.
038 */
039 private HashSet<BadPredicateAnnotationWarning> _badPredicateAnnotWarnings =
040 new HashSet<BadPredicateAnnotationWarning>();
041
042 /**
043 * Warnings about classes that could not be found.
044 */
045 private HashSet<ClassNotFoundWarning> _classNotFoundWarnings =
046 new HashSet<ClassNotFoundWarning>();
047
048 /**
049 * File name for the XML annotations file.
050 */
051 private String _xmlFileName = null;
052
053 /**
054 * Use concurrency definitions format for output, i.e. <threadcheck:def> nodes.
055 */
056 private boolean _xmlConcDefFormat = false ;
057
058 /**
059 * List of paths to consider when searching for classes.
060 */
061 private List<String> _classPath = new ArrayList<String>();
062
063 /** Name of the class currently being instrumented. */
064 private String _currentClassName = "<unknown>";
065
066 /** Update the method attribute of a @PredicateLink annotation to include the parameter order. */
067 private boolean _includePredicateMethodParameterOrder = true;
068
069 /**
070 * Return the name of the XML annotations file.
071 * @return XML annotations file name
072 */
073 public String getXmlFileName() {
074 return _xmlFileName;
075 }
076
077 /**
078 * Set the name of the XML annotations file.
079 * @param xmlFileName new file name
080 */
081 public void setXmlFileName(String xmlFileName) {
082 _xmlFileName = xmlFileName;
083 }
084
085 /**
086 * Return true if the XML file should use concurrency definitions format for output,
087 * i.e. <threadcheck:def> nodes.
088 * @return true for <threadcheck:def>
089 */
090 public boolean isXmlConcDefFormat() {
091 return _xmlConcDefFormat;
092 }
093
094 /**
095 * Return the paths to consider when searching classes.
096 * This creates a copy, so the actual list cannot be modified.
097 * @return class path
098 */
099 public List<String> getClassPath() {
100 return new ArrayList<String>(_classPath);
101 }
102
103 /**
104 * Add a class not found warning.
105 * @param classNotFoundWarning warning to add
106 */
107 public void addClassNotFoundWarning(ClassNotFoundWarning classNotFoundWarning) {
108 _classNotFoundWarnings.add(classNotFoundWarning);
109 }
110
111 /**
112 * Add a bad predicate annotation warning.
113 * @param badPredicateAnnotWarning warning to add
114 */
115 public void addBadPredicateAnnotWarning(BadPredicateAnnotationWarning badPredicateAnnotWarning) {
116 _badPredicateAnnotWarnings.add(badPredicateAnnotWarning);
117 }
118
119 /**
120 * Returns true if the method attribute of a @PredicateLink annotation should be updated to
121 * include the parameter order.
122 * @return true if the method attribute should be updated
123 */
124 public boolean getIncludePredicateMethodParameterOrder() {
125 return _includePredicateMethodParameterOrder;
126 }
127
128 /**
129 * Create a new instance of shared data.
130 * @param parameters parameters for the instrumentation strategy
131 */
132 public SharedData(List<String> parameters) {
133 for(String p: parameters) {
134 if (p.toLowerCase().startsWith(CLASS_PATH_PARAM_PREFIX)) {
135 String cp = p.substring(CLASS_PATH_PARAM_PREFIX.length());
136 String[] paths = cp.split(System.getProperty("path.separator"));
137 for(String path: paths) { _classPath.add(path); }
138 }
139 else if (p.toLowerCase().startsWith(XML_ANNOT_PARAM_PREFIX)) {
140 _xmlFileName = p.substring(XML_ANNOT_PARAM_PREFIX.length());
141 }
142 else if (p.equalsIgnoreCase(XML_ANNOT_FORMAT_PARAM)) {
143 _xmlConcDefFormat = true;
144 }
145 else if (p.toLowerCase().startsWith(UPDATE_PARAM_ORDER_PREFIX)) {
146 _includePredicateMethodParameterOrder = Boolean.valueOf(p.substring(UPDATE_PARAM_ORDER_PREFIX.length()));
147 }
148 }
149 if (_classPath.size()==0) {
150 String cp = System.getProperty("java.class.path");
151 String[] paths = cp.split(System.getProperty("path.separator"));
152 for(String path: paths) { _classPath.add(path); }
153 cp = FileInstrumentor.getDefaultSourceRtJarName();
154 paths = cp.split(System.getProperty("path.separator"));
155 for(String path: paths) { _classPath.add(path); }
156 }
157
158 Debug.out.println("Using as class path:");
159 for(String path: _classPath) {
160 Debug.out.println("\t"+path);
161 }
162 Debug.out.println("Include predicate method parameter order: "+_includePredicateMethodParameterOrder);
163 }
164
165 /**
166 * Return a list of scan results.
167 *
168 * @return list of scan results.
169 */
170 public List<? extends IScanResult> getScanResults() {
171 ArrayList<ClassNotFoundWarning> cnflist = new ArrayList<ClassNotFoundWarning>(_classNotFoundWarnings);
172 Collections.sort(cnflist);
173
174 ArrayList<BadPredicateAnnotationWarning> baflist = new ArrayList<BadPredicateAnnotationWarning>(
175 _badPredicateAnnotWarnings);
176 Collections.sort(baflist);
177
178 ArrayList<IScanResult> list = new ArrayList<IScanResult>(cnflist);
179 list.addAll(baflist);
180 return list;
181 }
182
183 /**
184 * Instrument the class.
185 * @param cf class file info
186 */
187 public void instrument(ClassFile cf) {
188 // don't do anything
189 }
190
191 /**
192 * Return the name of the class currently being instrumented.
193 * @return current class name
194 */
195 public String getCurrentClassName() {
196 return _currentClassName;
197 }
198
199 /**
200 * Set the name of the class currently being instrumented.
201 * @param currentClassName new current class name
202 */
203 public void setCurrentClassName(String currentClassName) {
204 this._currentClassName = currentClassName;
205 }
206
207 /**
208 * Instrumentation of all classes is done.
209 */
210 public void done() {
211 // nothing to do
212 }
213 }
214
215 /**
216 * Prefix for the parameter that determines the paths to consider when searching.
217 * This is a prefix to a list of paths separated by the "path.separator" property.
218 * If this parameter isn't specified, then the "java.class.path" and the default rt.jar file (as determined by
219 * TCFileInstrumentor.getDefaultSourceRtJarName) will be used as class path.
220 */
221 public static final String CLASS_PATH_PARAM_PREFIX = "class-path=";
222
223 /**
224 * XML configuration used for additional annotations not based in the code.
225 * $ for inner classes are replaced by -.
226 * Example format:
227 *
228 * <?xml version="1.0" encoding="UTF-8"?>
229 * <concutest>
230 * <threadcheck>
231 * <sample>
232 * <threadCheck>
233 * <ThreadCheckSample4>
234 * <class>
235 * <name type="only" value="childclass-xml1"/>
236 * <name type="not" value="childclass-xml2"/>
237 * <group type="only" value="childclass-xml1"/>
238 * <group type="not" value="childclass-xml2"/>
239 * <id type="only" value="1001"/>
240 * <id type="not" value="1002"/>
241 * <eventThread type="only"/>
242 * </class>
243 * <method sig="run()V">
244 * <name type="only" value="childclass-method-xml1"/>
245 * <name type="not" value="childclass-method-xml2"/>
246 * </method>
247 * </ThreadCheckSample4>
248 * </threadCheck>
249 * </sample>
250 * </threadcheck>
251 * </concutest>
252 */
253
254 /**
255 * Default XML path prefix before the class name.
256 */
257 public static final String DEFAULT_XML_PATH_PREFIX = "concutest/threadcheck/";
258
259 /**
260 * Default XML path prefix before for concurrency definitions.
261 */
262 public static final String DEFAULT_XML_CONC_DEF_PATH_PREFIX = "concutest/threadcheck:def/";
263
264 /**
265 * Prefix for the parameter that determines the XML file that contains additional annotations.
266 * If this parameter is not specified, then no additional file will be used.
267 */
268 public static final String XML_ANNOT_PARAM_PREFIX = "xml-annot=";
269
270 /**
271 * The parameter that determines that the XML file will be written out using concurrency definitions,
272 * i.e. using <threadcheck:def> nodes. If this parameter is not specified, then <threadcheck> will be used.
273 */
274 public static final String XML_ANNOT_FORMAT_PARAM = "xml-concdef-annot-format";
275
276 /**
277 * Prefix for the parameter that specifies whether the method attribute of a @PredicateLink
278 * annotation gets updated to include the order of the parameters. The default is true.
279 * This process changes the value of the method attribute from the form "methodName" to the
280 * form "methodName(param1,param2,param3)".
281 */
282 public static final String UPDATE_PARAM_ORDER_PREFIX = "update-param-order=";
283
284 /**
285 * The separator string between class name and method signature.
286 */
287 public static final String CLASS_SIG_SEPARATOR_STRING = "::";
288
289 /**
290 * Data shared among all AThreadCheckStrategy instances.
291 */
292 SharedData _sharedData;
293
294 /**
295 * Constructor for this strategy.
296 * @param parameters parameters for the instrumentors
297 * @param shared data shared among all AThreadCheckStrategy instances
298 */
299 public AThreadCheckStrategy(List<String> parameters, SharedData shared) {
300 _sharedData = shared;
301 }
302
303 /**
304 * Extract the lists of thread names, ids and groups from a list of attributes.
305 * @param typeName name of the annotation type
306 * @param attributesList list of attributes
307 * @param threadNames the set of thread names to fill
308 * @param threadIds the set of thread ids to fill
309 * @param threadGroups the set of thread groups to fill
310 * @param eventThread mutable flag for checking the event thread
311 */
312 protected void extractLists(String typeName,
313 ArrayList<AAttributeInfo> attributesList,
314 HashSet<String> threadNames,
315 HashSet<Long> threadIds,
316 HashSet<String> threadGroups,
317 Ref<OnlyRunBy.EVENT_THREAD> eventThread) {
318 for(AAttributeInfo ai: attributesList) {
319 if ((ai.getName().toString().equals(RuntimeInvisibleAnnotationsAttributeInfo.getAttributeName())) ||
320 (ai.getName().toString().equals(RuntimeVisibleAnnotationsAttributeInfo.getAttributeName()))) {
321 ASingleAnnotationsAttributeInfo aa = (ASingleAnnotationsAttributeInfo)ai;
322 for(AAnnotationsAttributeInfo.Annotation annot: aa.getAnnotations()) {
323 if (annot.getType().equals(typeName.replace('.','/')+";")) {
324 for (AAnnotationsAttributeInfo.Annotation.NameValuePair nvp: annot.getPairs()) {
325 if (nvp.getName().toString().equals("value")) {
326 Ref<OnlyRunBy.EVENT_THREAD> eThr = new Ref<OnlyRunBy.EVENT_THREAD>(null);
327 extractAnnotationArray(nvp.getValue(), threadNames, threadIds, threadGroups, eventThread);
328 if ((eThr.get()!=null) && (eventThread.get()!=null)) {
329 throw new ThreadCheckException("eventThread may only be specified once");
330 }
331 }
332 else if (nvp.getName().toString().equals("threadNames")) {
333 extractStringArray(nvp.getValue(), threadNames);
334 }
335 else if (nvp.getName().toString().equals("threadIds")) {
336 extractLongArray(nvp.getValue(), threadIds);
337 }
338 else if (nvp.getName().toString().equals("threadGroups")) {
339 extractStringArray(nvp.getValue(), threadGroups);
340 }
341 else if ((eventThread!=null) && (nvp.getName().toString().equals("eventThread"))) {
342 if (eventThread.get()!=null) {
343 throw new ThreadCheckException("eventThread may only be specified once");
344 }
345 AAnnotationsAttributeInfo.Annotation.EnumMemberValue ev = nvp.getValue().execute(
346 new AAnnotationsAttributeInfo.Annotation.ADefaultMemberValueVisitor<AAnnotationsAttributeInfo.Annotation.EnumMemberValue, Object>() {
347 public AAnnotationsAttributeInfo.Annotation.EnumMemberValue defaultCase(
348 AAnnotationsAttributeInfo.Annotation.AMemberValue host,
349 Object o) {
350 throw new ThreadCheckException(
351 "eventThread should be a OnlyRunBy.EVENT_THREAD");
352
353 }
354 public AAnnotationsAttributeInfo.Annotation.EnumMemberValue enumMemberCase(
355 AAnnotationsAttributeInfo.Annotation.EnumMemberValue host,
356 Object o) {
357 return host;
358 }
359 }, null);
360 AUTFPoolInfo str = ev.getConstValue().execute(new ADefaultPoolInfoVisitor<AUTFPoolInfo,Object>() {
361 public AUTFPoolInfo defaultCase(APoolInfo host, Object o) {
362 throw new ThreadCheckException(
363 "eventThread should be a OnlyRunBy.EVENT_THREAD");
364 }
365 public AUTFPoolInfo unicodeCase(UnicodePoolInfo host, Object o) {
366 return host;
367 }
368 public AUTFPoolInfo asciizCase(ASCIIPoolInfo host, Object o) {
369 return host;
370 }
371 }, null);
372 if (str.toString().equals("NO")) {
373 eventThread.set(OnlyRunBy.EVENT_THREAD.NO);
374 }
375 else if (str.toString().equals("ONLY")) {
376 eventThread.set(OnlyRunBy.EVENT_THREAD.ONLY);
377 }
378 else if (str.toString().equals("ONLY_AFTER_REALIZED")) {
379 eventThread.set(OnlyRunBy.EVENT_THREAD.ONLY_AFTER_REALIZED);
380 }
381 else {
382 throw new ThreadCheckException("eventThread should be a OnlyRunBy.EVENT_THREAD");
383 }
384 }
385 else {
386 throw new ThreadCheckException("Unknown field in "+typeName+" annotation: "+nvp.getName());
387 }
388 }
389 }
390 }
391 }
392 }
393 }
394
395 /**
396 * Extract the data in the annotation's string array and put it in the set of strings.
397 * @param mv member value, i.e. the string array
398 * @param threadNames the set of thread names to fill
399 * @param threadIds the set of thread ids to fill
400 * @param threadGroups the set of thread groups to fill
401 * @param eventThread mutable flag for checking the event thread
402 */
403 protected void extractAnnotationArray(AAnnotationsAttributeInfo.Annotation.AMemberValue mv,
404 HashSet<String> threadNames,
405 HashSet<Long> threadIds,
406 HashSet<String> threadGroups,
407 Ref<OnlyRunBy.EVENT_THREAD> eventThread) {
408 AAnnotationsAttributeInfo.Annotation.ArrayMemberValue arr = mv.execute(new AAnnotationsAttributeInfo.Annotation.ADefaultMemberValueVisitor<AAnnotationsAttributeInfo.Annotation.ArrayMemberValue,Object>() {
409 public AAnnotationsAttributeInfo.Annotation.ArrayMemberValue defaultCase(
410 AAnnotationsAttributeInfo.Annotation.AMemberValue host,
411 Object o) {
412 throw new ThreadCheckException("value should be an array of @ThreadDesc annotations");
413 }
414 public AAnnotationsAttributeInfo.Annotation.ArrayMemberValue arrayMemberCase(
415 AAnnotationsAttributeInfo.Annotation.ArrayMemberValue host,
416 Object o) {
417 return host;
418 }
419 }, null);
420 for(AAnnotationsAttributeInfo.Annotation.AMemberValue arrEntry: arr.getEntries()) {
421 AAnnotationsAttributeInfo.Annotation.AnnotationMemberValue av = arrEntry.execute(
422 new AAnnotationsAttributeInfo.Annotation.ADefaultMemberValueVisitor<AAnnotationsAttributeInfo.Annotation.AnnotationMemberValue, Object>() {
423 public AAnnotationsAttributeInfo.Annotation.AnnotationMemberValue defaultCase(
424 AAnnotationsAttributeInfo.Annotation.AMemberValue host,
425 Object o) {
426 throw new ThreadCheckException(
427 "value should be an array of @ThreadDesc annotations");
428 }
429
430 public AAnnotationsAttributeInfo.Annotation.AnnotationMemberValue annotationMemberCase(
431 AAnnotationsAttributeInfo.Annotation.AnnotationMemberValue host,
432 Object o) {
433 return host;
434 }
435 }, null);
436 String name = null;
437 Long id = null;
438 String group = null;
439 OnlyRunBy.EVENT_THREAD eThrd = null;
440 if (av.getAnnotation().getType().equals("L"+ThreadDesc.class.getName().replace('.','/')+";")) {
441 for (AAnnotationsAttributeInfo.Annotation.NameValuePair nvp: av.getAnnotation().getPairs()) {
442 if (nvp.getName().toString().equals("name")) {
443 AAnnotationsAttributeInfo.Annotation.ConstantMemberValue cvp = nvp.getValue().execute(new AAnnotationsAttributeInfo.Annotation.ADefaultMemberValueVisitor<AAnnotationsAttributeInfo.Annotation.ConstantMemberValue,Object>() {
444 public AAnnotationsAttributeInfo.Annotation.ConstantMemberValue defaultCase(
445 AAnnotationsAttributeInfo.Annotation.AMemberValue host,
446 Object o) {
447 throw new ThreadCheckException("@ThreadDesc name should be a string");
448 }
449 public AAnnotationsAttributeInfo.Annotation.ConstantMemberValue constantMemberCase(
450 AAnnotationsAttributeInfo.Annotation.ConstantMemberValue host,
451 Object o) {
452 return host;
453 }
454 }, null);
455 AUTFPoolInfo str = cvp.getConstValue().execute(
456 new ADefaultPoolInfoVisitor<AUTFPoolInfo, Object>() {
457 public AUTFPoolInfo defaultCase(APoolInfo host, Object o) {
458 throw new ThreadCheckException("@ThreadDesc name should be a string");
459 }
460
461 public AUTFPoolInfo asciizCase(ASCIIPoolInfo host, Object o) {
462 return host;
463 }
464
465 public AUTFPoolInfo unicodeCase(UnicodePoolInfo host, Object o) {
466 return host;
467 }
468 }, null);
469 name = str.toString();
470 }
471 else if (nvp.getName().toString().equals("id")) {
472 AAnnotationsAttributeInfo.Annotation.ConstantMemberValue cvp = nvp.getValue().execute(new AAnnotationsAttributeInfo.Annotation.ADefaultMemberValueVisitor<AAnnotationsAttributeInfo.Annotation.ConstantMemberValue,Object>() {
473 public AAnnotationsAttributeInfo.Annotation.ConstantMemberValue defaultCase(
474 AAnnotationsAttributeInfo.Annotation.AMemberValue host,
475 Object o) {
476 throw new ThreadCheckException("@ThreadDesc id should be a long");
477 }
478 public AAnnotationsAttributeInfo.Annotation.ConstantMemberValue constantMemberCase(
479 AAnnotationsAttributeInfo.Annotation.ConstantMemberValue host,
480 Object o) {
481 return host;
482 }
483 }, null);
484 LongPoolInfo l = cvp.getConstValue().execute(new ADefaultPoolInfoVisitor<LongPoolInfo, Object>() {
485 public LongPoolInfo defaultCase(APoolInfo host, Object o) {
486 throw new ThreadCheckException("@ThreadDesc id should be a long");
487 }
488 public LongPoolInfo longCase(LongPoolInfo host, Object o) {
489 return host;
490 }
491 }, null);
492 id = l.getLongValue();
493 }
494 else if (nvp.getName().toString().equals("group")) {
495 AAnnotationsAttributeInfo.Annotation.ConstantMemberValue cvp = nvp.getValue().execute(new AAnnotationsAttributeInfo.Annotation.ADefaultMemberValueVisitor<AAnnotationsAttributeInfo.Annotation.ConstantMemberValue,Object>() {
496 public AAnnotationsAttributeInfo.Annotation.ConstantMemberValue defaultCase(
497 AAnnotationsAttributeInfo.Annotation.AMemberValue host,
498 Object o) {
499 throw new ThreadCheckException("@ThreadDesc group should be a string");
500 }
501 public AAnnotationsAttributeInfo.Annotation.ConstantMemberValue constantMemberCase(
502 AAnnotationsAttributeInfo.Annotation.ConstantMemberValue host,
503 Object o) {
504 return host;
505 }
506 }, null);
507 AUTFPoolInfo str = cvp.getConstValue().execute(
508 new ADefaultPoolInfoVisitor<AUTFPoolInfo, Object>() {
509 public AUTFPoolInfo defaultCase(APoolInfo host, Object o) {
510 throw new ThreadCheckException("@ThreadDesc group should be a string");
511 }
512
513 public AUTFPoolInfo asciizCase(ASCIIPoolInfo host, Object o) {
514 return host;
515 }
516
517 public AUTFPoolInfo unicodeCase(UnicodePoolInfo host, Object o) {
518 return host;
519 }
520 }, null);
521 group = str.toString();
522 }
523 else if ((eventThread != null) && (nvp.getName().toString().equals("eventThread"))) {
524 AAnnotationsAttributeInfo.Annotation.EnumMemberValue ev = nvp.getValue().execute(
525 new AAnnotationsAttributeInfo.Annotation.ADefaultMemberValueVisitor<AAnnotationsAttributeInfo.Annotation.EnumMemberValue, Object>() {
526 public AAnnotationsAttributeInfo.Annotation.EnumMemberValue defaultCase(
527 AAnnotationsAttributeInfo.Annotation.AMemberValue host,
528 Object o) {
529 throw new ThreadCheckException("eventThread should be a OnlyRunBy.EVENT_THREAD");
530 }
531
532 public AAnnotationsAttributeInfo.Annotation.EnumMemberValue enumMemberCase(
533 AAnnotationsAttributeInfo.Annotation.EnumMemberValue host,
534 Object o) {
535 return host;
536 }
537 }, null);
538 AUTFPoolInfo str = ev.getConstValue().execute(
539 new ADefaultPoolInfoVisitor<AUTFPoolInfo, Object>() {
540 public AUTFPoolInfo defaultCase(APoolInfo host, Object o) {
541 throw new ThreadCheckException("eventThread should be a OnlyRunBy.EVENT_THREAD");
542 }
543 public AUTFPoolInfo unicodeCase(UnicodePoolInfo host, Object o) {
544 return host;
545 }
546 public AUTFPoolInfo asciizCase(ASCIIPoolInfo host, Object o) {
547 return host;
548 }
549 }, null);
550 if (str.toString().equals("NO")) {
551 eThrd = OnlyRunBy.EVENT_THREAD.NO;
552 }
553 else if (str.toString().equals("ONLY")) {
554 eThrd = OnlyRunBy.EVENT_THREAD.ONLY;
555 }
556 else if (str.toString().equals("ONLY_AFTER_REALIZED")) {
557 eThrd = OnlyRunBy.EVENT_THREAD.ONLY_AFTER_REALIZED;
558 }
559 else {
560 throw new ThreadCheckException("eventThread should be a OnlyRunBy.EVENT_THREAD");
561 }
562 }
563 else {
564 throw new ThreadCheckException(
565 "Unknown field in @ThreadDesk: " + nvp.getName());
566 }
567 }
568 if (name!=null) {
569 if ((id!=null) || (group!=null) || (eThrd!=null)) {
570 throw new ThreadCheckException("Only exactly one attribute (name,id,group,eventThread) may be used.");
571 }
572 threadNames.add(name);
573 }
574 else if (id!=null) {
575 if ((name!=null) || (group!=null) || (eThrd!=null)) {
576 throw new ThreadCheckException("Only exactly one attribute (name,id,group,eventThread) may be used.");
577 }
578 threadIds.add(id);
579 }
580 else if (group!=null) {
581 if ((name!=null) || (id!=null) || (eThrd!=null)) {
582 throw new ThreadCheckException("Only exactly one attribute (name,id,group,eventThread) may be used.");
583 }
584 threadGroups.add(group);
585 }
586 else if (eThrd!=null) {
587 if ((name!=null) || (id!=null) || (group!=null)) {
588 throw new ThreadCheckException("Only exactly one attribute (name,id,group,eventThread) may be used.");
589 }
590 eventThread.set(eThrd);
591 }
592 }
593 else {
594 throw new ThreadCheckException("value should be an array of @ThreadDesc annotations");
595
596 }
597 }
598 }
599
600 /**
601 * Extract the data in the annotation's string array and put it in the set of strings.
602 * @param mv member value, i.e. the string array
603 * @param set set to contain the strings
604 */
605 protected void extractStringArray(AAnnotationsAttributeInfo.Annotation.AMemberValue mv, HashSet<String> set) {
606 AAnnotationsAttributeInfo.Annotation.ArrayMemberValue arr = mv.execute(new AAnnotationsAttributeInfo.Annotation.ADefaultMemberValueVisitor<AAnnotationsAttributeInfo.Annotation.ArrayMemberValue,Object>() {
607 public AAnnotationsAttributeInfo.Annotation.ArrayMemberValue defaultCase(
608 AAnnotationsAttributeInfo.Annotation.AMemberValue host,
609 Object o) {
610 throw new ThreadCheckException("threadNames/threadGroups should be an array of strings");
611 }
612 public AAnnotationsAttributeInfo.Annotation.ArrayMemberValue arrayMemberCase(
613 AAnnotationsAttributeInfo.Annotation.ArrayMemberValue host,
614 Object o) {
615 return host;
616 }
617 }, null);
618 for(AAnnotationsAttributeInfo.Annotation.AMemberValue arrEntry: arr.getEntries()) {
619 AAnnotationsAttributeInfo.Annotation.ConstantMemberValue cv = arrEntry.execute(
620 new AAnnotationsAttributeInfo.Annotation.ADefaultMemberValueVisitor<AAnnotationsAttributeInfo.Annotation.ConstantMemberValue, Object>() {
621 public AAnnotationsAttributeInfo.Annotation.ConstantMemberValue defaultCase(
622 AAnnotationsAttributeInfo.Annotation.AMemberValue host,
623 Object o) {
624 throw new ThreadCheckException(
625 "threadNames/threadGroups should be an array of strings");
626 }
627
628 public AAnnotationsAttributeInfo.Annotation.ConstantMemberValue constantMemberCase(
629 AAnnotationsAttributeInfo.Annotation.ConstantMemberValue host,
630 Object o) {
631 return host;
632 }
633 }, null);
634 AUTFPoolInfo str = cv.getConstValue().execute(new ADefaultPoolInfoVisitor<AUTFPoolInfo,Object>() {
635 public AUTFPoolInfo defaultCase(APoolInfo host, Object o) {
636 throw new ThreadCheckException(
637 "threadNames/threadGroups should be an array of strings");
638 }
639 public AUTFPoolInfo asciizCase(ASCIIPoolInfo host, Object o) {
640 return host;
641 }
642 public AUTFPoolInfo unicodeCase(UnicodePoolInfo host, Object o) {
643 return host;
644 }
645 }, null);
646 set.add(str.toString());
647 }
648 }
649
650 /**
651 * Extract the data in the annotation's long array and put it in the set of longs.
652 * @param mv member value, i.e. the long array
653 * @param set set to contain the longs
654 */
655 protected void extractLongArray(AAnnotationsAttributeInfo.Annotation.AMemberValue mv, HashSet<Long> set) {
656 AAnnotationsAttributeInfo.Annotation.ArrayMemberValue arr = mv.execute(new AAnnotationsAttributeInfo.Annotation.ADefaultMemberValueVisitor<AAnnotationsAttributeInfo.Annotation.ArrayMemberValue,Object>() {
657 public AAnnotationsAttributeInfo.Annotation.ArrayMemberValue defaultCase(
658 AAnnotationsAttributeInfo.Annotation.AMemberValue host,
659 Object o) {
660 throw new ThreadCheckException("threadIds should be an array of longs");
661 }
662 public AAnnotationsAttributeInfo.Annotation.ArrayMemberValue arrayMemberCase(
663 AAnnotationsAttributeInfo.Annotation.ArrayMemberValue host,
664 Object o) {
665 return host;
666 }
667 }, null);
668 for(AAnnotationsAttributeInfo.Annotation.AMemberValue arrEntry: arr.getEntries()) {
669 AAnnotationsAttributeInfo.Annotation.ConstantMemberValue cv = arrEntry.execute(
670 new AAnnotationsAttributeInfo.Annotation.ADefaultMemberValueVisitor<AAnnotationsAttributeInfo.Annotation.ConstantMemberValue, Object>() {
671 public AAnnotationsAttributeInfo.Annotation.ConstantMemberValue defaultCase(
672 AAnnotationsAttributeInfo.Annotation.AMemberValue host,
673 Object o) {
674 throw new ThreadCheckException(
675 "threadIds should be an array of longs");
676 }
677
678 public AAnnotationsAttributeInfo.Annotation.ConstantMemberValue constantMemberCase(
679 AAnnotationsAttributeInfo.Annotation.ConstantMemberValue host,
680 Object o) {
681 return host;
682 }
683 }, null);
684 LongPoolInfo l = cv.getConstValue().execute(new ADefaultPoolInfoVisitor<LongPoolInfo,Object>() {
685 public LongPoolInfo defaultCase(APoolInfo host, Object o) {
686 throw new ThreadCheckException(
687 "threadIds should be an array of longs");
688 }
689 public LongPoolInfo longCase(LongPoolInfo host, Object o) {
690 return host;
691 }
692 }, null);
693 set.add(l.getLongValue());
694 }
695 }
696
697 /**
698 * Extract the list of predicate annotation records.
699 * @param attributesList list of attributes
700 * @param predicateSet list of predicate annotation records to create
701 * @param methodSig method signature
702 */
703 protected void extractPredicateSet(ArrayList<AAttributeInfo> attributesList,
704 ArrayList<PredicateAnnotationRecord> predicateSet,
705 String methodSig) {
706 for(AAttributeInfo ai: attributesList) {
707 if ((ai.getName().toString().equals(RuntimeInvisibleAnnotationsAttributeInfo.getAttributeName())) ||
708 (ai.getName().toString().equals(RuntimeVisibleAnnotationsAttributeInfo.getAttributeName()))) {
709 ASingleAnnotationsAttributeInfo aa = (ASingleAnnotationsAttributeInfo)ai;
710 for(AAnnotationsAttributeInfo.Annotation annot: aa.getAnnotations()) {
711 PredicateAnnotationRecord par = processAnnotation(annot);
712 if (par!=null) {
713 predicateSet.add(par);
714 }
715 }
716 }
717 }
718 }
719
720 /**
721 * Return the predicate link, or null if not found
722 * @param attributesList list of attributes
723 * @return predicate link annotation
724 */
725 protected AAnnotationsAttributeInfo.Annotation getPredicateLink(ArrayList<AAttributeInfo> attributesList) {
726 for(AAttributeInfo ai: attributesList) {
727 if ((ai.getName().toString().equals(RuntimeInvisibleAnnotationsAttributeInfo.getAttributeName())) ||
728 (ai.getName().toString().equals(RuntimeVisibleAnnotationsAttributeInfo.getAttributeName()))) {
729 ASingleAnnotationsAttributeInfo aa = (ASingleAnnotationsAttributeInfo)ai;
730 for(AAnnotationsAttributeInfo.Annotation annot: aa.getAnnotations()) {
731 if (annot.getType().equals("L"+ PredicateLink.class.getName().replace('.','/')+";")) {
732 return annot;
733 }
734 }
735 }
736 }
737 return null;
738 }
739
740 /**
741 * Return the combine attribute, or null if not found
742 * @param attributesList list of attributes
743 * @return combine annotation
744 */
745 protected AAnnotationsAttributeInfo.Annotation getCombineMode(ArrayList<AAttributeInfo> attributesList) {
746 for(AAttributeInfo ai: attributesList) {
747 if ((ai.getName().toString().equals(RuntimeInvisibleAnnotationsAttributeInfo.getAttributeName())) ||
748 (ai.getName().toString().equals(RuntimeVisibleAnnotationsAttributeInfo.getAttributeName()))) {
749 ASingleAnnotationsAttributeInfo aa = (ASingleAnnotationsAttributeInfo)ai;
750 for(AAnnotationsAttributeInfo.Annotation annot: aa.getAnnotations()) {
751 if (annot.getType().equals("L"+ Combine.class.getName().replace('.','/')+";")) {
752 return annot;
753 }
754 }
755 }
756 }
757 return null;
758 }
759
760 /**
761 * Process the specified annotation and return a PredicateAnnotationRecord if the annotation is
762 * a Thread Checker predicate annotation, or null otherwise.
763 * @param annot annotation to process
764 * @return PredicateAnnotationRecord, or null if the annotation is not a Thread Checker predicate annotation
765 */
766 protected PredicateAnnotationRecord processAnnotation(AAnnotationsAttributeInfo.Annotation annot) {
767 Ref<ClassFileTools.ClassLocation> refCL = new Ref<ClassFileTools.ClassLocation>(null);
768 Ref<AAnnotationsAttributeInfo.Annotation> refPredicateLink = new Ref<AAnnotationsAttributeInfo.Annotation>(null);
769 Ref<AAnnotationsAttributeInfo.Annotation> refCombineAnnot = new Ref<AAnnotationsAttributeInfo.Annotation>(null);
770 if ((!getAnnotationClassFile(annot.getType(), refCL, refPredicateLink, refCombineAnnot)) || (refCL.get()==null)) {
771 return null;
772 }
773 final ClassFile cf = refCL.get().getClassFile();
774
775 String predicateClass = null;
776 String predicateMethod = "check";
777 boolean passArguments = false;
778 Combine.Mode combineMode = Combine.Mode.OR;
779 ClassFile predicateCF = null;
780 if (refPredicateLink.get()!=null) {
781 try {
782 for(AAnnotationsAttributeInfo.Annotation.NameValuePair nvp: refPredicateLink.get().getPairs()) {
783 if (nvp.getName().toString().equals("value")) {
784 // extract class
785 AAnnotationsAttributeInfo.Annotation.ClassMemberValue cmv = nvp.getValue().execute(
786 new AAnnotationsAttributeInfo.Annotation.ADefaultMemberValueVisitor<AAnnotationsAttributeInfo.Annotation.ClassMemberValue, Object>() {
787 public AAnnotationsAttributeInfo.Annotation.ClassMemberValue defaultCase(
788 AAnnotationsAttributeInfo.Annotation.AMemberValue host, Object param) {
789 throw new BadPredicateAnnotationWarning("@PredicateLink's value member should be a class constant in "+cf.getThisClassName()+
790 " (processing " + _sharedData.getCurrentClassName() + ")");
791 }
792 public AAnnotationsAttributeInfo.Annotation.ClassMemberValue classMemberCase(
793 AAnnotationsAttributeInfo.Annotation.ClassMemberValue host, Object param) {
794 return host;
795 }
796 }, null);
797 String cn = cmv.getClassName().toString();
798 predicateClass = cn.substring(1,cn.length()-1).replace('/','.');
799 }
800 else if (nvp.getName().toString().equals("method")) {
801 // extract method
802 AAnnotationsAttributeInfo.Annotation.ConstantMemberValue cmv = nvp.getValue().execute(
803 AAnnotationsAttributeInfo.Annotation.CheckConstantMemberVisitor.singleton(), null);
804 AUTFPoolInfo mpi = cmv.getConstValue().execute(new ADefaultPoolInfoVisitor<AUTFPoolInfo,Object>() {
805 public AUTFPoolInfo defaultCase(APoolInfo host, Object param) {
806 throw new BadPredicateAnnotationWarning("@PredicateLink's value member should be a string constant in "+cf.getThisClassName()+
807 " (processing " + _sharedData.getCurrentClassName() + ")");
808 }
809 public AUTFPoolInfo asciizCase(ASCIIPoolInfo host, Object param) {
810 return host;
811 }
812 public AUTFPoolInfo unicodeCase(UnicodePoolInfo host, Object param) {
813 return host;
814 }
815 }, null);
816 predicateMethod = mpi.toString();
817 }
818 else if (nvp.getName().toString().equals("arguments")) {
819 // extract arguments
820 AAnnotationsAttributeInfo.Annotation.ConstantMemberValue cmv = nvp.getValue().execute(
821 AAnnotationsAttributeInfo.Annotation.CheckConstantMemberVisitor.singleton(), null);
822 IntegerPoolInfo mpi = cmv.getConstValue().execute(new ADefaultPoolInfoVisitor<IntegerPoolInfo,Object>() {
823 public IntegerPoolInfo defaultCase(APoolInfo host, Object param) {
824 throw new BadPredicateAnnotationWarning("@PredicateLink's value arguments should be a boolean constant in "+cf.getThisClassName()+
825 " (processing " + _sharedData.getCurrentClassName() + ")");
826 }
827 public IntegerPoolInfo intCase(IntegerPoolInfo host, Object param) {
828 return host;
829 }
830 }, null);
831 if (mpi.getIntValue()!=0) {
832 passArguments = true;
833 }
834 }
835 }
836 }
837 catch(BadPredicateAnnotationWarning bpaw) {
838 _sharedData.addBadPredicateAnnotWarning(bpaw);
839 return null;
840 }
841 if (predicateClass==null) {
842 _sharedData.addBadPredicateAnnotWarning(new BadPredicateAnnotationWarning("Predicate class not specified in "+cf.getThisClassName()+
843 " (processing " + _sharedData.getCurrentClassName() + ")"));
844 }
845 ClassFileTools.ClassLocation cl = ClassFileTools.findClassFile(predicateClass, _sharedData._classPath);
846 if (cl==null) {
847 _sharedData.addClassNotFoundWarning(new ClassNotFoundWarning(predicateClass,_sharedData.getCurrentClassName()));
848 return null;
849 }
850 predicateCF = cl.getClassFile();
851 try {
852 cl.close();
853 }
854 catch(IOException e) { /* ignore */ }
855 cl = null;
856 }
857 if (refCombineAnnot.get()!=null) {
858 try {
859 for(AAnnotationsAttributeInfo.Annotation.NameValuePair nvp: refCombineAnnot.get().getPairs()) {
860 if (nvp.getName().toString().equals("value")) {
861 // extract class
862 AAnnotationsAttributeInfo.Annotation.EnumMemberValue emv = nvp.getValue().execute(
863 new AAnnotationsAttributeInfo.Annotation.ADefaultMemberValueVisitor<AAnnotationsAttributeInfo.Annotation.EnumMemberValue, Object>() {
864 public AAnnotationsAttributeInfo.Annotation.EnumMemberValue defaultCase(
865 AAnnotationsAttributeInfo.Annotation.AMemberValue host, Object param) {
866 throw new BadPredicateAnnotationWarning("@Combine's value member should be an enum constant in "+cf.getThisClassName()+
867 " (processing " + _sharedData.getCurrentClassName() + ")");
868 }
869 public AAnnotationsAttributeInfo.Annotation.EnumMemberValue enumMemberCase(
870 AAnnotationsAttributeInfo.Annotation.EnumMemberValue host, Object param) {
871 return host;
872 }
873 }, null);
874 if (!emv.getTypeName().toString().equals("L"+Combine.Mode.class.getName().replace('.','/')+";")) {
875 throw new BadPredicateAnnotationWarning("@Combine's value member should be an enum constant of type "+
876 Combine.Mode.class.getName()+" in "+cf.getThisClassName()+
877 " (processing " + _sharedData.getCurrentClassName() + ")");
878 }
879 String cn = emv.getConstValue().toString();
880 if (cn.equals(Combine.Mode.AND.toString())) {
881 combineMode = Combine.Mode.AND;
882 }
883 else if (cn.equals(Combine.Mode.OR.toString())) {
884 combineMode = Combine.Mode.OR;
885 }
886 else if (cn.equals(Combine.Mode.NOT.toString())) {
887 combineMode = Combine.Mode.NOT;
888 }
889 else if (cn.equals(Combine.Mode.XOR.toString())) {
890 combineMode = Combine.Mode.XOR;
891 }
892 else if (cn.equals(Combine.Mode.IMPLIES.toString())) {
893 combineMode = Combine.Mode.IMPLIES;
894 }
895 else {
896 throw new BadPredicateAnnotationWarning("@Combine's value member ("+cn+") was not recognized; "+
897 "it should be an enum constant of type "+
898 Combine.Mode.class.getName()+" in "+cf.getThisClassName()+
899 " (processing " + _sharedData.getCurrentClassName() + ")");
900 }
901 }
902 else if (nvp.getName().toString().equals("arguments")) {
903 // extract arguments
904 AAnnotationsAttributeInfo.Annotation.ConstantMemberValue cmv = nvp.getValue().execute(
905 AAnnotationsAttributeInfo.Annotation.CheckConstantMemberVisitor.singleton(), null);
906 IntegerPoolInfo mpi = cmv.getConstValue().execute(new ADefaultPoolInfoVisitor<IntegerPoolInfo,Object>() {
907 public IntegerPoolInfo defaultCase(APoolInfo host, Object param) {
908 throw new BadPredicateAnnotationWarning("@Combine's value arguments should be a boolean constant in "+cf.getThisClassName()+
909 " (processing " + _sharedData.getCurrentClassName() + ")");
910 }
911 public IntegerPoolInfo intCase(IntegerPoolInfo host, Object param) {
912 return host;
913 }
914 }, null);
915 if (mpi.getIntValue()!=0) {
916 passArguments = true;
917 }
918 }
919 }
920 }
921 catch(BadPredicateAnnotationWarning bpaw) {
922 _sharedData.addBadPredicateAnnotWarning(bpaw);
923 return null;
924 }
925 }
926
927 PredicateAnnotationRecord par;
928 try {
929 par = getPredicateAnnotationRecord(annot, refCL.get(), predicateCF, predicateMethod, combineMode, passArguments);
930 }
931 catch(BadPredicateAnnotationWarning bpaw) {
932 _sharedData.addBadPredicateAnnotWarning(bpaw);
933 return null;
934 }
935 if (par==null) {
936 _sharedData.addBadPredicateAnnotWarning(new BadPredicateAnnotationWarning("Predicate method "+predicateMethod+" in class "+
937 predicateClass+" not found in "+cf.getThisClassName()+
938 " (processing " + _sharedData.getCurrentClassName() + ")"));
939 return null;
940 }
941 return par;
942 }
943
944 /**
945 * Return the annotations class file, or null if it is not a Thread Checker annotation.
946 * @param annotType class name of the annotation, in the form "Ljava/lang/Object;"
947 * @param refCL reference to the class file; will get filled in by this method
948 * @param predicateLink reference to the predicate link; will get filled in by this method
949 * @param combineAnnot reference to the combine annotation; will get filled in by this method
950 * @return true if this is an annotation for the Thread Checker
951 */
952 protected boolean getAnnotationClassFile(String annotType,
953 Ref<ClassFileTools.ClassLocation> refCL,
954 Ref<AAnnotationsAttributeInfo.Annotation> predicateLink,
955 Ref<AAnnotationsAttributeInfo.Annotation> combineAnnot) {
956 final String className = annotType.substring(1, annotType.length()-1).replace('/','.');
957 ClassFileTools.ClassLocation cl = ClassFileTools.findClassFile(className, _sharedData._classPath);
958 if (cl==null) {
959 _sharedData.addClassNotFoundWarning(new ClassNotFoundWarning(className,_sharedData.getCurrentClassName()));
960 return false;
961 }
962 refCL.set(cl);
963 try {
964 cl.close();
965 }
966 catch(IOException e) {
967 // ignore
968 }
969 if ((cl.getClassFile().getClassAccessFlags() & ClassFile.ACC_ANNOTATION)==0) {
970 _sharedData.addBadPredicateAnnotWarning(new BadPredicateAnnotationWarning(className+" is not an annotation "+
971 " (processing " + _sharedData.getCurrentClassName() + ")"));
972 return false;
973 }
974
975 // scan for PredicateLink and Combine annotation
976 predicateLink.set(getPredicateLink(cl.getClassFile().getAttributes()));
977 combineAnnot.set(getCombineMode(cl.getClassFile().getAttributes()));
978 if ((predicateLink.get()==null) && (combineAnnot.get()==null)) {
979 // not meant for the thread checker
980 return false;
981 }
982 if ((predicateLink.get()!=null) && (combineAnnot.get()!=null)) {
983 // @PredicateLink and @Combine are mutually exclusive
984 _sharedData.addBadPredicateAnnotWarning(new BadPredicateAnnotationWarning("@PredicateLink and @Combine are mutually exclusive, "+
985 "but both were provided in "+className+
986 " (processing " + _sharedData.getCurrentClassName() + ")"));
987 return false;
988 }
989 return true;
990 }
991
992 /**
993 * Return the method info for the predicate method, or null if not found.
994 * @param annot the annotation linked to the predicate
995 * @param annotCL class file location for the annotation linked to the predicate
996 * @param predicateCF class file for the class containing the predicate method, or null if not specified
997 * @param predicateMethod name of the predicate method
998 * @param combineMode mode to combine results from several predicates
999 * @param passArguments whether method arguments should be passed as well
1000 * @return method info for the predicate method
1001 */
1002 protected PredicateAnnotationRecord getPredicateAnnotationRecord(final AAnnotationsAttributeInfo.Annotation annot,
1003 ClassFileTools.ClassLocation annotCL,
1004 ClassFile predicateCF,
1005 String predicateMethod,
1006 Combine.Mode combineMode,
1007 final boolean passArguments) {
1008 ClassFile annotCF = annotCL.getClassFile();
1009 if (predicateCF!=null) {
1010 // @PredicateLink specified
1011
1012 // check if the predicate method is accessible from everywhere:
1013 // 1) the class needs to be:
1014 // a) a public top-level class
1015 // b) a public static nested class inside a class that fulfills criterion 1
1016 // 2) the method must be public and static
1017 // this checks 1)
1018
1019 // check that it is not an anonymous inner class
1020 // by checking if the EnclosingMethod attribute is present
1021 if (predicateCF.getAttribute("EnclosingMethod")!=null) {
1022 _sharedData.addBadPredicateAnnotWarning(new BadPredicateAnnotationWarning("Predicate method class "+predicateCF.getThisClassName()+
1023 " is not accessible from everywhere (anonymous inner class)"+
1024 " (processing " + _sharedData.getCurrentClassName() + ")"));
1025 }
1026 else {
1027 // check that if it is a nested class, that it is public static, and all of its enclosing classes are, too
1028 if (checkPublicStaticIfNestedClass(predicateCF, predicateCF)) {
1029 // check that the class is public
1030 if ((predicateCF.getClassAccessFlags() & ClassFile.ACC_PUBLIC)==0) {
1031 _sharedData.addBadPredicateAnnotWarning(new BadPredicateAnnotationWarning("Predicate method class "+predicateCF.getThisClassName()+
1032 " is not accessible from everywhere (not public)"+
1033 " (processing " + _sharedData.getCurrentClassName() + ")"));
1034 }
1035 }
1036 }
1037
1038 // check that there are no annotations or arrays of annotations
1039 checkPredicateMembers(annotCF, passArguments);
1040
1041 // check that there are no annotations or arrays of annotations as members
1042 for (final AAnnotationsAttributeInfo.Annotation.NameValuePair nvp: annot.getPairs()) {
1043 nvp.getValue().execute(new AAnnotationsAttributeInfo.Annotation.ADefaultMemberValueVisitor<Object,Object>() {
1044 public Object defaultCase(AAnnotationsAttributeInfo.Annotation.AMemberValue host, Object param) {
1045 return null;
1046 }
1047 public Object annotationMemberCase(AAnnotationsAttributeInfo.Annotation.AnnotationMemberValue host, Object param) {
1048 String t = annot.getType();
1049 _sharedData.addBadPredicateAnnotWarning(new BadPredicateAnnotationWarning("Annotation "+t.substring(1,t.length()-1)+" has meta-annotation @Predicate, "+
1050 "but contains a member that is an annotation: "+nvp.getName()+
1051 " (processing " + _sharedData.getCurrentClassName() + ")"));
1052 return null;
1053 }
1054 public Object arrayMemberCase(AAnnotationsAttributeInfo.Annotation.ArrayMemberValue host,
1055 Object param) {
1056 // check that this is an array of annotations
1057 for(AAnnotationsAttributeInfo.Annotation.AMemberValue mv: host.getEntries()) {
1058 mv.execute(new AAnnotationsAttributeInfo.Annotation.ADefaultMemberValueVisitor<Object, Object>() {
1059 public Object defaultCase(AAnnotationsAttributeInfo.Annotation.AMemberValue host, Object param) {
1060 return null;
1061 }
1062 public Object annotationMemberCase(AAnnotationsAttributeInfo.Annotation.AnnotationMemberValue host, Object param) {
1063 String t = annot.getType();
1064 _sharedData.addBadPredicateAnnotWarning(new BadPredicateAnnotationWarning("Annotation "+t.substring(1,t.length()-1)+" has meta-annotation @Predicate, "+
1065 "but contains a member that is an array of annotations: "+nvp.getName()+
1066 " (processing " + _sharedData.getCurrentClassName() + ")"));
1067 return null;
1068 }
1069 }, null);
1070 }
1071 return null;
1072 }
1073 }, null);
1074 }
1075
1076 // order of the parameters in the method, ignoring the first Object, and if passing method arguments, the
1077 // Object[] array as second parameter as well
1078 ArrayList<String> paramNames = null;
1079
1080 int parenIndex = predicateMethod.indexOf('(');
1081 if (parenIndex>=0) {
1082 if (!predicateMethod.endsWith(")")) {
1083 _sharedData.addBadPredicateAnnotWarning(new BadPredicateAnnotationWarning("Annotation "+annot.getType().substring(1,annot.getType().length()-1)+" has meta-annotation @Predicate, "+
1084 "with a bad method value whose parameter list does not end with a ')' "+
1085 " (processing " + _sharedData.getCurrentClassName() + ")"));
1086 return null;
1087 }
1088 // strip parameter name list in parentheses from name: "methodName(param1,param2)"
1089 paramNames = new ArrayList<String>();
1090 String s = predicateMethod.substring(parenIndex + 1, predicateMethod.length() - 1);
1091 if (s.length()>0) {
1092 String[] specifiedOrder = s.split(",");
1093 for(String name: specifiedOrder) {
1094 if (name.length()>0) {
1095 paramNames.add(name);
1096 }
1097 }
1098 }
1099 predicateMethod = predicateMethod.substring(0,parenIndex);
1100 }
1101
1102 for (MethodInfo mi: predicateCF.getMethods()) {
1103 if (!mi.getName().toString().equals(predicateMethod)) { continue; } // wrong name
1104 String desc = mi.getDescriptor().toString();
1105 if ((mi.getAccessFlags() & ClassFile.ACC_PUBLIC)==0) { continue; } // not public
1106 if ((mi.getAccessFlags() & ClassFile.ACC_STATIC)==0) { continue; } // not static
1107 if ((mi.getAccessFlags() & ClassFile.ACC_ABSTRACT)!=0) { continue; } // abstract, not concrete
1108 final String prefix = "(Ljava/lang/Object;"+((passArguments)?"[Ljava/lang/Object;":"");
1109 if (!desc.startsWith(prefix)) { continue; } // no Object as first parameter
1110 final String suffix = ")Z";
1111 if (!desc.endsWith(suffix)) { continue; } // not returning boolean
1112
1113 // paramTypeList includes the types of the method arguments in argTypeList
1114 String paramString = desc.substring(prefix.length(), desc.length()-suffix.length());
1115 List<String> paramTypeList = ClassFileTools.getSignatures(paramString);
1116 if (annotCF.getMethods().size()!=paramTypeList.size()) {
1117 continue; // wrong number of parameters
1118 }
1119
1120 CodeAttributeInfo ca = mi.getCodeAttributeInfo();
1121 LocalVariableTableAttributeInfo lvarTable = null;
1122 for(AAttributeInfo caAttr: ca.getAttributes()) {
1123 lvarTable = caAttr.execute(new ADefaultAttributeVisitor<LocalVariableTableAttributeInfo,Object>() {
1124 public LocalVariableTableAttributeInfo defaultCase(AAttributeInfo host, Object param) {
1125 return null;
1126 }
1127 public LocalVariableTableAttributeInfo localVariableTableCase(LocalVariableTableAttributeInfo host,
1128 Object param) {
1129 return host;
1130 }
1131 }, null);
1132 if (lvarTable!=null) { break; }
1133 }
1134
1135 if (paramNames==null) {
1136 // no order specified
1137 paramNames = new ArrayList<String>(annotCF.getMethods().size());
1138 if (lvarTable==null) {
1139 // match values in annotation with parameters in predicate method by order and type, no names
1140 _sharedData.addBadPredicateAnnotWarning(new BadPredicateAnnotationWarning("No LocalVariableTable for method "+predicateMethod+
1141 desc+" in class "+predicateCF.getThisClassName()+"; matching by order, not names"+
1142 " (processing " + _sharedData.getCurrentClassName() + ")"));
1143 int paramIndex = 0;
1144 boolean mismatch = false;
1145 for(MethodInfo annotMI: annotCF.getMethods()) {
1146 final String annotDesc = annotMI.getDescriptor().toString();
1147 if ((annotMI.getAccessFlags() & ClassFile.ACC_PUBLIC) == 0) {
1148 throw new BadPredicateAnnotationWarning("Annotation "+annotCF.getThisClassName()+" has non-public method "+
1149 annotMI.getName().toString()+ annotDesc+
1150 " (processing " + _sharedData.getCurrentClassName() + ")");
1151 }
1152 if ((annotMI.getAccessFlags() & ClassFile.ACC_ABSTRACT) == 0) {
1153 throw new BadPredicateAnnotationWarning("Annotation "+annotCF.getThisClassName()+" has non-abstract method "+
1154 annotMI.getName().toString()+ annotDesc+
1155 " (processing " + _sharedData.getCurrentClassName() + ")");
1156 }
1157 final String noParamPrefix = "()";
1158 if (!annotDesc.startsWith(noParamPrefix)) {
1159 throw new BadPredicateAnnotationWarning("Annotation "+annotCF.getThisClassName()+" has method taking arguments "+
1160 annotMI.getName().toString()+ annotDesc+
1161 " (processing " + _sharedData.getCurrentClassName() + ")");
1162 }
1163 String retType = annotDesc.substring(noParamPrefix.length());
1164 if (!(retType.equals(paramTypeList.get(paramIndex++)))) {
1165 mismatch = true;
1166 break;
1167 }
1168 paramNames.add(annotMI.getName().toString());
1169 }
1170 if (mismatch) { continue; } // parameters don't match
1171 }
1172 else {
1173 // match values in annotation with parameters in predicate method by name and type
1174 ArrayList<LocalVariableTableAttributeInfo.LocalVariableRecord> predicateParams =
1175 new ArrayList<LocalVariableTableAttributeInfo.LocalVariableRecord>();
1176 for (LocalVariableTableAttributeInfo.LocalVariableRecord lvr: lvarTable.getLocalVariables()) {
1177 if ((lvr.startPC==0) && (lvr.length==ca.getCode().length)) { predicateParams.add(lvr); }
1178 }
1179 Collections.sort(predicateParams, new Comparator<LocalVariableTableAttributeInfo.LocalVariableRecord>() {
1180 public int compare(LocalVariableTableAttributeInfo.LocalVariableRecord o1,
1181 LocalVariableTableAttributeInfo.LocalVariableRecord o2) {
1182 return o1.index-o2.index;
1183 }
1184 });
1185 if (predicateParams.get(0).index!=0) {
1186 throw new BadPredicateAnnotationWarning("Predicate method "+mi.getName().toString()+mi.getDescriptor().toString()+
1187 " in class "+predicateCF.getThisClassName()+" has parameters not starting at index 0"+
1188 " (processing " + _sharedData.getCurrentClassName() + ")");
1189 }
1190 if (!predicateParams.get(0).descriptor.toString().equals("L"+Object.class.getName().replace('.','/')+";")) {
1191 throw new BadPredicateAnnotationWarning("Predicate method "+mi.getName().toString()+mi.getDescriptor().toString()+
1192 " in class "+predicateCF.getThisClassName()+" does not have Object as parameter at index 0: "+
1193 predicateParams.get(0).descriptor+
1194 " (processing " + _sharedData.getCurrentClassName() + ")");
1195 }
1196
1197 // remove first parameter, it's not in the annotation
1198 // this is the first parameter Object this0
1199 predicateParams.remove(0);
1200
1201 if (passArguments) {
1202 // remove second parameter, it's not in the annotation either
1203 // this is the second parameter Object[] args for the method arguments
1204 predicateParams.remove(0);
1205 }
1206
1207 if (annotCF.getMethods().size()!=predicateParams.size()) {
1208 throw new BadPredicateAnnotationWarning("Predicate method "+mi.getName().toString()+mi.getDescriptor().toString()+
1209 " in class "+predicateCF.getThisClassName()+" has wrong number of parameters"+
1210 " (processing " + _sharedData.getCurrentClassName() + ")");
1211 }
1212
1213 boolean mismatch = false;
1214 for(MethodInfo annotMI: annotCF.getMethods()) {
1215 final String annotDesc = annotMI.getDescriptor().toString();
1216 if ((annotMI.getAccessFlags() & ClassFile.ACC_PUBLIC) == 0) {
1217 throw new BadPredicateAnnotationWarning("Annotation "+annotCF.getThisClassName()+" has non-public method "+
1218 annotMI.getName().toString()+ annotDesc+
1219 " (processing " + _sharedData.getCurrentClassName() + ")");
1220 }
1221 if ((annotMI.getAccessFlags() & ClassFile.ACC_ABSTRACT) == 0) {
1222 throw new BadPredicateAnnotationWarning("Annotation "+annotCF.getThisClassName()+" has non-abstract method "+
1223 annotMI.getName().toString()+ annotDesc+
1224 " (processing " + _sharedData.getCurrentClassName() + ")");
1225 }
1226 final String noParamPrefix = "()";
1227 if (!annotDesc.startsWith(noParamPrefix)) {
1228 throw new BadPredicateAnnotationWarning("Annotation "+annotCF.getThisClassName()+" has method taking arguments "+
1229 annotMI.getName().toString()+ annotDesc+
1230 " (processing " + _sharedData.getCurrentClassName() + ")");
1231 }
1232 String retType = annotDesc.substring(noParamPrefix.length());
1233
1234 // scan through predicateParams
1235 boolean found = false;
1236 for(LocalVariableTableAttributeInfo.LocalVariableRecord lvr: predicateParams) {
1237 if ((retType.equals(lvr.descriptor.toString()) &&
1238 (annotMI.getName().toString().equals(lvr.name.toString())))) {
1239 found = true;
1240 break;
1241 }
1242 }
1243 if (!found) {
1244 mismatch = true;
1245 break;
1246 }
1247 }
1248 if (mismatch) { continue; } // parameters don't match
1249 for(LocalVariableTableAttributeInfo.LocalVariableRecord lvr: predicateParams) {
1250 paramNames.add(lvr.name.toString());
1251 }
1252 }
1253
1254 // if desired, add the parameter order to the predicate link method attribute
1255 // this is necessary if the reflection-based Thread Checker is used
1256 if (_sharedData.getIncludePredicateMethodParameterOrder() && (annotCL.getJarFile()==null)) {
1257 includePredicateMethodParameterOrder(predicateMethod, paramNames, annotCL);
1258 }
1259 }
1260
1261 // create list of name-value pairs, including default values
1262 ArrayList<AAnnotationsAttributeInfo.Annotation.AMemberValue> valueList =
1263 new ArrayList<AAnnotationsAttributeInfo.Annotation.AMemberValue>();
1264 for(int paramNameIndex=0; paramNameIndex<paramNames.size(); ++paramNameIndex) {
1265 String name = paramNames.get(paramNameIndex);
1266
1267 // scan for name in annotation at usage site
1268 AAnnotationsAttributeInfo.Annotation.AMemberValue mv = null;
1269 for (AAnnotationsAttributeInfo.Annotation.NameValuePair nvp: annot.getPairs()) {
1270 if (nvp.getName().toString().equals(name)) {
1271 mv = nvp.getValue();
1272 break;
1273 }
1274 }
1275 if (mv==null) {
1276 // not found in annotation, try to find default value
1277 AnnotationDefaultAttributeInfo adAttr = null;
1278 for(MethodInfo annotMI: annotCF.getMethods()) {
1279 if (annotMI.getName().toString().equals(name)) {
1280 for(AAttributeInfo annotAttr: annotMI.getAttributes()) {
1281 adAttr = annotAttr.execute(new ADefaultAttributeVisitor<AnnotationDefaultAttributeInfo,Object>() {
1282 public AnnotationDefaultAttributeInfo defaultCase(AAttributeInfo host, Object param) {
1283 return null;
1284 }
1285 public AnnotationDefaultAttributeInfo annotationDefaultCase(
1286 AnnotationDefaultAttributeInfo host, Object param) {
1287 return host;
1288 }
1289 }, null);
1290 if (adAttr!=null) { break; }
1291 }
1292 }
1293 if (adAttr!=null) { break; }
1294 }
1295 if (adAttr==null) {
1296 throw new BadPredicateAnnotationWarning("Annotation "+annotCF.getThisClassName()+" has no value for member "+name+
1297 " (processing " + _sharedData.getCurrentClassName() + ")");
1298 }
1299 mv = adAttr.getDefaultValue();
1300 }
1301 valueList.add(mv);
1302 }
1303
1304 // create hashmap of types
1305 HashMap<String, String> paramTypes = new HashMap<String, String>();
1306 for(int i=0; i<paramNames.size();++i) {
1307 paramTypes.put(paramNames.get(i), paramTypeList.get(i-0));
1308 }
1309
1310 // this checks 2)
1311 if (((mi.getAccessFlags() & ClassFile.ACC_PUBLIC)==0) ||
1312 ((mi.getAccessFlags() & ClassFile.ACC_STATIC)==0)) {
1313 throw new BadPredicateAnnotationWarning("Predicate method "+mi.getName()+mi.getDescriptor()+
1314 " in class "+predicateCF.getThisClassName()+" is not public static."+
1315 " (processing " + _sharedData.getCurrentClassName() + ")");
1316 }
1317
1318 return new PredicateAnnotationRecord(annot,
1319 predicateCF.getThisClassName(),
1320 mi,
1321 paramNames,
1322 paramTypes,
1323 valueList,
1324 passArguments,
1325 null,
1326 new HashMap<String,ArrayList<PredicateAnnotationRecord>>());
1327 }
1328 // TODO: allow for subclassing
1329 throw new BadPredicateAnnotationWarning("Could not find predicate method "+predicateMethod+" in "+predicateCF.getThisClassName()+
1330 " that is suitable for annotation "+annot.getType()+
1331 " (processing " + _sharedData.getCurrentClassName() + ")");
1332 }
1333 else {
1334 // @Combine specified
1335
1336 // check that there are only annotations and arrays of annotations
1337 checkCombineMembers(annotCF, passArguments);
1338
1339 // collect the predicates combined in this annotation
1340 final HashMap<String,ArrayList<PredicateAnnotationRecord>> combinedPredicates =
1341 new HashMap<String,ArrayList<PredicateAnnotationRecord>>();
1342 final HashSet<PredicateAnnotationRecord> allSet = new HashSet<PredicateAnnotationRecord>();
1343 final HashMap<String,String> paramTypes = new HashMap<String,String>();
1344
1345 for(final MethodInfo annotMI : annotCF.getMethods()) {
1346 final String annotDesc = annotMI.getDescriptor().toString();
1347 final String noParamPrefix = "()";
1348 if (!annotDesc.startsWith(noParamPrefix)) {
1349 throw new BadPredicateAnnotationWarning("Annotation "+annotCF.getThisClassName()+" has method taking arguments "+
1350 annotMI.getName().toString()+ annotDesc+
1351 " (processing " + _sharedData.getCurrentClassName() + ")");
1352 }
1353 String retType = annotDesc.substring(noParamPrefix.length());
1354 paramTypes.put(annotMI.getName().toString(), retType);
1355
1356 // scan for name in annotation at usage site
1357 AAnnotationsAttributeInfo.Annotation.AMemberValue mv = null;
1358 for (AAnnotationsAttributeInfo.Annotation.NameValuePair nvp: annot.getPairs()) {
1359 if (nvp.getName().toString().equals(annotMI.getName().toString())) {
1360 mv = nvp.getValue();
1361 break;
1362 }
1363 }
1364 if (mv==null) {
1365 // not found in annotation, try to find default value
1366 AnnotationDefaultAttributeInfo adAttr = null;
1367 for(AAttributeInfo annotAttr: annotMI.getAttributes()) {
1368 adAttr = annotAttr.execute(new ADefaultAttributeVisitor<AnnotationDefaultAttributeInfo,Object>() {
1369 public AnnotationDefaultAttributeInfo defaultCase(AAttributeInfo host, Object param) {
1370 return null;
1371 }
1372 public AnnotationDefaultAttributeInfo annotationDefaultCase(
1373 AnnotationDefaultAttributeInfo host, Object param) {
1374 return host;
1375 }
1376 }, null);
1377 if (adAttr!=null) { break; }
1378 }
1379 if (adAttr==null) {
1380 throw new BadPredicateAnnotationWarning("Annotation "+annotCF.getThisClassName()+
1381 " has no value for member "+annotMI.getName().toString()+
1382 " (processing " + _sharedData.getCurrentClassName() + ")");
1383 }
1384 mv = adAttr.getDefaultValue();
1385 }
1386
1387 mv.execute(new AAnnotationsAttributeInfo.Annotation.ADefaultMemberValueVisitor<Object,Object>() {
1388 public Object defaultCase(AAnnotationsAttributeInfo.Annotation.AMemberValue host, Object param) {
1389 String t = annot.getType();
1390 throw new BadPredicateAnnotationWarning("Annotation "+t.substring(1,t.length()-1)+" has meta-annotation @Combine, "+
1391 "but contains a member that is not a Thread Checker annotation or an array thereof: "+
1392 annotMI.getName().toString()+
1393 " (processing " + _sharedData.getCurrentClassName() + ")");
1394 }
1395 public Object annotationMemberCase(AAnnotationsAttributeInfo.Annotation.AnnotationMemberValue host, Object param) {
1396 PredicateAnnotationRecord par = processAnnotation(host.getAnnotation());
1397 if ((par!=null) /* && (!allSet.contains(par)) */) {
1398 // single element
1399 allSet.add(par);
1400 ArrayList<PredicateAnnotationRecord> elementList = new ArrayList<PredicateAnnotationRecord>();
1401 elementList.add(par);
1402 combinedPredicates.put(annotMI.getName().toString(), elementList);
1403 }
1404 else {
1405 String t = annot.getType();
1406 throw new BadPredicateAnnotationWarning("Annotation "+t.substring(1,t.length()-1)+" has meta-annotation @Combine, "+
1407 "but contains an annotation as member that is not a Thread Checker annotation: "+
1408 annotMI.getName().toString()+
1409 " (processing " + _sharedData.getCurrentClassName() + ")");
1410 }
1411 return null;
1412 }
1413 public Object arrayMemberCase(AAnnotationsAttributeInfo.Annotation.ArrayMemberValue host,
1414 Object param) {
1415 // check that this is an array of annotations
1416 // array of elements
1417 final ArrayList<PredicateAnnotationRecord> elementList = new ArrayList<PredicateAnnotationRecord>();
1418 for(AAnnotationsAttributeInfo.Annotation.AMemberValue mv: host.getEntries()) {
1419 mv.execute(new AAnnotationsAttributeInfo.Annotation.ADefaultMemberValueVisitor<Object, Object>() {
1420 public Object defaultCase(AAnnotationsAttributeInfo.Annotation.AMemberValue host, Object param) {
1421 throw new BadPredicateAnnotationWarning("Annotation " + annot.getType()
1422 + " has meta-annotation @Combine, "
1423 + "but contains a member that is not a Thread Checker annotation or an array thereof: "
1424 + annotMI.getName().toString()
1425 + " (processing " + _sharedData.getCurrentClassName() + ")");
1426 }
1427 public Object annotationMemberCase(AAnnotationsAttributeInfo.Annotation.AnnotationMemberValue host, Object param) {
1428 PredicateAnnotationRecord par = processAnnotation(host.getAnnotation());
1429 if (par!=null) {
1430 allSet.add(par);
1431 elementList.add(par);
1432 }
1433 else {
1434 String t = annot.getType();
1435 throw new BadPredicateAnnotationWarning("Annotation "+t.substring(1,t.length()-1)+" has meta-annotation @Combine, "+
1436 "but contains an array of annotations as member whose elements are not a Thread Checker annotations: "+
1437 annotMI.getName().toString()+
1438 " (processing " + _sharedData.getCurrentClassName() + ")");
1439 }
1440 return null;
1441 }
1442 }, null);
1443 }
1444 combinedPredicates.put(annotMI.getName().toString(), elementList);
1445 return null;
1446 }
1447 }, null);
1448 }
1449
1450
1451 // make sure that NOT us unary and IMPLIES is binary as an array
1452 if ((combineMode==Combine.Mode.NOT)&&((paramTypes.size()!=1)||(combinedPredicates.size()!=1))) {
1453 String t = annot.getType();
1454 throw new BadPredicateAnnotationWarning("Annotation "+t.substring(1,t.length()-1)+" has meta-annotation @Combine "+
1455 "with mode NOT, but has more than one member annotations ("+paramTypes.size()+")"+
1456 " (processing " + _sharedData.getCurrentClassName() + ")");
1457 }
1458 if ((combineMode==Combine.Mode.IMPLIES)&&((paramTypes.size()!=1)||(combinedPredicates.size()!=1))) {
1459 String t = annot.getType();
1460 throw new BadPredicateAnnotationWarning("Annotation "+t.substring(1,t.length()-1)+" has meta-annotation @Combine "+
1461 "with mode IMPLIES, but has more than one member annotations ("+paramTypes.size()+")"+
1462 " (processing " + _sharedData.getCurrentClassName() + ")");
1463 }
1464 String key = combinedPredicates.keySet().toArray(new String[0])[0];
1465 if ((combineMode==Combine.Mode.IMPLIES)&&(combinedPredicates.get(key).size()!=2)) {
1466 String t = annot.getType();
1467 throw new BadPredicateAnnotationWarning("Annotation "+t.substring(1,t.length()-1)+" has meta-annotation @Combine "+
1468 "with mode IMPLIES, but does not have an array with exactly two member annotations ("+allSet.size()+")"+
1469 " (processing " + _sharedData.getCurrentClassName() + ")");
1470 }
1471
1472 return new PredicateAnnotationRecord(annot,
1473 null,
1474 null,
1475 new ArrayList<String>(),
1476 paramTypes,
1477 new ArrayList<AAnnotationsAttributeInfo.Annotation.AMemberValue>(),
1478 passArguments,
1479 combineMode,
1480 combinedPredicates);
1481 }
1482 }
1483
1484 /**
1485 * Check that the class file, should it be a nested class and not a top-level class, is public static,
1486 * and the same applies to all its enclosing classes. If this is not the case, a BadPredicateAnnotationWarning
1487 * is added and false is returned.
1488 * This check is performed by examining the InnerClasses attribute.
1489 * @param predicateCF original predicate class file for which this check is performed; does not change in recursion
1490 * @param cf class file to check
1491 * @return false if the class is a nested class and not accessible from everywhere
1492 * @throws BadPredicateAnnotationWarning
1493 */
1494 protected boolean checkPublicStaticIfNestedClass(final ClassFile predicateCF, final ClassFile cf) {
1495 AAttributeInfo attr = cf.getAttribute("InnerClasses");
1496 if (attr==null) { return true; }
1497 InnerClassesAttributeInfo ic = attr.execute(new ADefaultAttributeVisitor<InnerClassesAttributeInfo,Object>() {
1498 public InnerClassesAttributeInfo defaultCase(AAttributeInfo host, Object param) {
1499 _sharedData.addBadPredicateAnnotWarning(new BadPredicateAnnotationWarning("Could not check if predicate method class "+
1500 predicateCF.getThisClassName()+
1501 " is not accessible from everywhere (InnerClasses attribute of class "+
1502 cf.getThisClassName()+" not well-formed)" +
1503 " (processing " + _sharedData.getCurrentClassName() + ")"));
1504 return null;
1505 }
1506 public InnerClassesAttributeInfo innerClassesCase(InnerClassesAttributeInfo host, Object param) {
1507 return host;
1508 }
1509 }, null);
1510
1511 if (ic==null) { return false; }
1512
1513 for(InnerClassesAttributeInfo.InnerClassesRecord icr: ic.getInnerClasses()) {
1514 if (icr.innerClass.toString().equals(cf.getThisClassName().replace('.','/'))) {
1515 // this record is about the current class, i.e. the current class is an inner class
1516 // check that the class is public static
1517 if (((icr.innerFlags & ClassFile.ACC_PUBLIC)==0) ||
1518 ((icr.innerFlags & ClassFile.ACC_STATIC)==0)) {
1519 // not public or not static, throw exception
1520 _sharedData.addBadPredicateAnnotWarning(new BadPredicateAnnotationWarning("Predicate method class "+
1521 predicateCF.getThisClassName()+
1522 " is not accessible from everywhere (inner class "+
1523 cf.getThisClassName()+" not public static)"+
1524 " (processing " + _sharedData.getCurrentClassName() + ")"));
1525 return false;
1526 }
1527
1528 // now recur into the enclosing class and make sure that the same holds for it
1529 ClassFileTools.ClassLocation cl = ClassFileTools.findClassFile(icr.outerClass.toString().replace('/','.'), _sharedData._classPath);
1530 if (cl==null) {
1531 _sharedData.addBadPredicateAnnotWarning(new BadPredicateAnnotationWarning("Could not check if predicate method class "+
1532 predicateCF.getThisClassName()+
1533 " is not accessible from everywhere (class "+cf.getThisClassName()+" not found)" +
1534 " (processing " + _sharedData.getCurrentClassName() + ")"));
1535 return false;
1536 }
1537 ClassFile outerCF = cl.getClassFile();
1538 try {
1539 cl.close();
1540 }
1541 catch(IOException e) { /* ignore */ }
1542 cl = null;
1543 if (!checkPublicStaticIfNestedClass(predicateCF, outerCF)) {
1544 return false;
1545 }
1546 }
1547 }
1548 return true;
1549 }
1550
1551 /**
1552 * Check that the members of the annotation class file are all Thread Checker annotations or arrays thereof.
1553 * @param annotCF annotation class file
1554 */
1555 protected void checkCombineMembers(ClassFile annotCF, boolean passMethods) {
1556 for(MethodInfo annotMI : annotCF.getMethods()) {
1557 final String annotDesc = annotMI.getDescriptor().toString();
1558 if ((annotMI.getAccessFlags() & ClassFile.ACC_PUBLIC) == 0) {
1559 throw new BadPredicateAnnotationWarning("Annotation " + annotCF.getThisClassName()
1560 + " has non-public method " + annotMI.getName().toString()
1561 + annotDesc + " (processing " + _sharedData.getCurrentClassName() + ")");
1562 }
1563 if ((annotMI.getAccessFlags() & ClassFile.ACC_ABSTRACT) == 0) {
1564 throw new BadPredicateAnnotationWarning("Annotation " + annotCF.getThisClassName()
1565 + " has non-abstract method " + annotMI.getName().toString()
1566 + annotDesc + " (processing " + _sharedData.getCurrentClassName() + ")");
1567 }
1568 final String noParamPrefix = "()";
1569 if (!annotDesc.startsWith(noParamPrefix)) {
1570 throw new BadPredicateAnnotationWarning("Annotation " + annotCF.getThisClassName()
1571 + " has method taking arguments "
1572 + annotMI.getName().toString() + annotDesc
1573 + " (processing " + _sharedData.getCurrentClassName() + ")");
1574 }
1575
1576 String retType = annotDesc.substring(noParamPrefix.length());
1577 if ((retType.charAt(0) != 'L') && (retType.charAt(0) != '[')) {
1578 throw new BadPredicateAnnotationWarning("Annotation " + annotCF.getThisClassName()
1579 + " has meta-annotation @Combine, "
1580 + "but contains a member that is not a Thread Checker annotation or an array thereof: "
1581 + annotMI.getName()
1582 + " (processing " + _sharedData.getCurrentClassName() + ")");
1583 }
1584 String memberType;
1585 if (retType.charAt(0)=='L') {
1586 memberType = retType;
1587 }
1588 else {
1589 memberType = retType.substring(1);
1590 if (memberType.charAt(0) != 'L') {
1591 throw new BadPredicateAnnotationWarning("Annotation " + annotCF.getThisClassName()
1592 + " has meta-annotation @Combine, "
1593 + "but contains a member that is not a Thread Checker annotation or an array thereof: "
1594 + annotMI.getName() + " (processing " + _sharedData.getCurrentClassName() + ")");
1595 }
1596 }
1597 Ref<ClassFileTools.ClassLocation> refCL = new Ref<ClassFileTools.ClassLocation>(null);
1598 Ref<AAnnotationsAttributeInfo.Annotation> refPredicateLink = new Ref<AAnnotationsAttributeInfo.Annotation>(null);
1599 Ref<AAnnotationsAttributeInfo.Annotation> refCombineAnnot = new Ref<AAnnotationsAttributeInfo.Annotation>(null);
1600 if (!(getAnnotationClassFile(memberType, refCL, refPredicateLink, refCombineAnnot)) || (refCL.get()==null)) {
1601 throw new BadPredicateAnnotationWarning("Annotation " + annotCF.getThisClassName()
1602 + " has meta-annotation @Combine, "
1603 + "but contains a member that is not a Thread Checker annotation or an array thereof: "
1604 + annotMI.getName() + " (processing " + _sharedData.getCurrentClassName() + ")");
1605 }
1606 if (!passMethods) {
1607 List<AAnnotationsAttributeInfo.Annotation.NameValuePair> nvpList = null;
1608 if (refPredicateLink.get()!=null) { nvpList = refPredicateLink.get().getPairs(); }
1609 else if (refCombineAnnot.get()!=null) { nvpList = refCombineAnnot.get().getPairs(); }
1610 if (nvpList!=null) {
1611 for (AAnnotationsAttributeInfo.Annotation.NameValuePair nvp: nvpList) {
1612 if (nvp.getName().toString().equals("arguments")) {
1613 AAnnotationsAttributeInfo.Annotation.ConstantMemberValue cvp = nvp.getValue().execute(new AAnnotationsAttributeInfo.Annotation.ADefaultMemberValueVisitor<AAnnotationsAttributeInfo.Annotation.ConstantMemberValue,Object>() {
1614 public AAnnotationsAttributeInfo.Annotation.ConstantMemberValue defaultCase(
1615 AAnnotationsAttributeInfo.Annotation.AMemberValue host,
1616 Object o) {
1617 throw new ThreadCheckException("The arguments value should be a boolean");
1618 }
1619 public AAnnotationsAttributeInfo.Annotation.ConstantMemberValue constantMemberCase(
1620 AAnnotationsAttributeInfo.Annotation.ConstantMemberValue host,
1621 Object o) {
1622 return host;
1623 }
1624 }, null);
1625 if (cvp.getTag()!='Z') {
1626 throw new ThreadCheckException("The arguments value should be a boolean");
1627 }
1628 else {
1629 if (cvp.getConstValue().execute(CheckIntegerVisitor.singleton(),null).getIntValue()!=0) {
1630 // the containing annotation has arguments=false, but the contained annotation
1631 // has arguments=true
1632 throw new BadPredicateAnnotationWarning("A member annotation "+ClassFileTools.getTypeString(memberType,"")+
1633 "of "+annotCF.getThisClassName()+
1634 " has arguments=true, but the containing annotation does not.");
1635 }
1636 }
1637 break;
1638 }
1639 }
1640 }
1641 }
1642 final ClassFile cf = refCL.get().getClassFile();
1643 if (refPredicateLink.get()!=null) {
1644 checkPredicateMembers(cf, passMethods);
1645 }
1646 else {
1647 checkCombineMembers(cf, passMethods);
1648 }
1649 }
1650 }
1651
1652 /**
1653 * Check that the members of the annotation class do not contain annotations or arrays of annotations
1654 * @param annotCF annotation class file
1655 * @param passMethods whether method arguments should be passed ass well
1656 */
1657 protected void checkPredicateMembers(ClassFile annotCF, boolean passMethods) {
1658 for(MethodInfo annotMI : annotCF.getMethods()) {
1659 final String annotDesc = annotMI.getDescriptor().toString();
1660 if ((annotMI.getAccessFlags() & ClassFile.ACC_PUBLIC) == 0) {
1661 throw new BadPredicateAnnotationWarning("Annotation " + annotCF.getThisClassName()
1662 + " has non-public method " + annotMI.getName().toString()
1663 + annotDesc + " (processing " + _sharedData.getCurrentClassName() + ")");
1664 }
1665 if ((annotMI.getAccessFlags() & ClassFile.ACC_ABSTRACT) == 0) {
1666 throw new BadPredicateAnnotationWarning("Annotation " + annotCF.getThisClassName()
1667 + " has non-abstract method " + annotMI.getName().toString()
1668 + annotDesc + " (processing " + _sharedData.getCurrentClassName() + ")");
1669 }
1670 final String noParamPrefix = "()";
1671 if (!annotDesc.startsWith(noParamPrefix)) {
1672 throw new BadPredicateAnnotationWarning("Annotation " + annotCF.getThisClassName()
1673 + " has method taking arguments "
1674 + annotMI.getName().toString() + annotDesc
1675 + " (processing " + _sharedData.getCurrentClassName() + ")");
1676 }
1677
1678 String retType = annotDesc.substring(noParamPrefix.length());
1679 if ((retType.charAt(0) != 'L') && (retType.charAt(0) != '[')) {
1680 continue;
1681 }
1682 String memberType;
1683 if (retType.charAt(0)=='L') {
1684 memberType = retType;
1685 }
1686 else {
1687 memberType = retType.substring(1);
1688 if (memberType.charAt(0) != 'L') {
1689 continue;
1690 }
1691 }
1692 final String className = memberType.substring(1, memberType.length()-1).replace('/','.');
1693 ClassFileTools.ClassLocation cl = ClassFileTools.findClassFile(className, _sharedData._classPath);
1694 if (cl==null) {
1695 _sharedData.addClassNotFoundWarning(new ClassNotFoundWarning(className,_sharedData.getCurrentClassName()));
1696 }
1697 else {
1698 final ClassFile cf = cl.getClassFile();
1699 try {
1700 cl.close();
1701 }
1702 catch(IOException e) { /* ignore */ }
1703 if (cf!=null) {
1704 if ((cf.getClassAccessFlags() & ClassFile.ACC_ANNOTATION)!=0) {
1705 throw new BadPredicateAnnotationWarning("Annotation " + annotCF.getThisClassName()
1706 + " has meta-annotation @PredicateLink, "
1707 + "but contains a member that is an annotation or an array of annotations: "
1708 + annotMI.getName()
1709 + " (processing " + _sharedData.getCurrentClassName() + ")");
1710 }
1711 }
1712 }
1713 }
1714 }
1715
1716 /**
1717 * Update the method attribute in the @PredicateLink to include the parameter order.
1718 * @param predicateMethod name of the predicate method
1719 * @param paramNames order of the parameters, ignoring the first "Object thisO" parameter
1720 * @param annotCL class file location of the annotation
1721 */
1722 protected void includePredicateMethodParameterOrder(String predicateMethod,
1723 ArrayList<String> paramNames,
1724 ClassFileTools.ClassLocation annotCL) {
1725 ClassFile annotCF = annotCL.getClassFile();
1726
1727 StringBuilder sb = new StringBuilder();
1728 sb.append(predicateMethod);
1729 sb.append('(');
1730 boolean notFirst = false;
1731 for(int pi=0; pi<paramNames.size(); ++pi) {
1732 if (notFirst) { sb.append(','); } else { notFirst = true; }
1733 sb.append(paramNames.get(pi));
1734 }
1735 sb.append(')');
1736
1737 // find the annotation type in the constant pool
1738 AUTFPoolInfo newPoolItem = new ASCIIPoolInfo(sb.toString(), annotCF.getConstantPool());
1739 int[] l = annotCF.addConstantPoolItems(new APoolInfo[] {newPoolItem});
1740 newPoolItem = annotCF.getConstantPoolItem(l[0]).execute(CheckUTFVisitor.singleton(), null);
1741
1742 // find the @PredicateLink annotation
1743 ASingleAnnotationsAttributeInfo origAnnotAttrib = null;
1744 int index = 0;
1745 AAnnotationsAttributeInfo.Annotation pl = null;
1746 for(AAttributeInfo ai: annotCF.getAttributes()) {
1747 if ((ai.getName().toString().equals(RuntimeInvisibleAnnotationsAttributeInfo.getAttributeName())) ||
1748 (ai.getName().toString().equals(RuntimeVisibleAnnotationsAttributeInfo.getAttributeName()))) {
1749 ASingleAnnotationsAttributeInfo aa = (ASingleAnnotationsAttributeInfo)ai;
1750 for(AAnnotationsAttributeInfo.Annotation annot: aa.getAnnotations()) {
1751 if (annot.getType().equals("L"+ PredicateLink.class.getName().replace('.','/')+";")) {
1752 origAnnotAttrib = aa;
1753 pl = annot;
1754 break;
1755 }
1756 ++index;
1757 }
1758 if (pl!=null) {
1759 break;
1760 }
1761 }
1762 }
1763 if (pl==null) {
1764 throw new BadPredicateAnnotationWarning("@PredicateLink of annotation "+annotCF.getThisClassName()+
1765 " not found when updating predicate method parameter order.");
1766 }
1767
1768 // create an updated list of name-value pairs
1769 ArrayList<AAnnotationsAttributeInfo.Annotation.NameValuePair> pairs =
1770 new ArrayList<AAnnotationsAttributeInfo.Annotation.NameValuePair>();
1771 for(AAnnotationsAttributeInfo.Annotation.NameValuePair nvp: pl.getPairs()) {
1772 if (nvp.getName().toString().equals("method")) {
1773 nvp = new AAnnotationsAttributeInfo.Annotation.NameValuePair(
1774 nvp.getName(),
1775 new AAnnotationsAttributeInfo.Annotation.ConstantMemberValue('s', newPoolItem));
1776 }
1777 pairs.add(nvp);
1778 }
1779
1780 // find the annotation type in the constant pool
1781 AUTFPoolInfo annotType = new ASCIIPoolInfo(pl.getType(), annotCF.getConstantPool());
1782 l = annotCF.addConstantPoolItems(new APoolInfo[] {annotType});
1783 annotType = annotCF.getConstantPoolItem(l[0]).execute(CheckUTFVisitor.singleton(), null);
1784
1785 AAnnotationsAttributeInfo.Annotation newpl = new AAnnotationsAttributeInfo.Annotation(
1786 annotType,
1787 (short)pairs.size(),
1788 pairs.toArray(new AAnnotationsAttributeInfo.Annotation.NameValuePair[0]));
1789
1790 AAnnotationsAttributeInfo.Annotation[] annotations = origAnnotAttrib.getAnnotations();
1791 annotations[index] = newpl;
1792
1793 ByteArrayOutputStream baos = new ByteArrayOutputStream();
1794 try {
1795 origAnnotAttrib.setAnnotations(annotations);
1796
1797 baos.reset();
1798 annotCF.write(baos);
1799
1800 FileOutputStream fos = new FileOutputStream(annotCL.getFile());
1801 fos.write(baos.toByteArray());
1802 fos.close();
1803 }
1804 catch(IOException e) {
1805 // TODO: warning
1806 }
1807 }
1808
1809 /**
1810 * Instrumentation of all classes is done.
1811 */
1812 public void done() {
1813 // nothing to do
1814 }
1815 }