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 }