001 package edu.rice.cs.cunit.instrumentors.threadCheck; 002 003 import edu.rice.cs.cunit.classFile.ClassFile; 004 import edu.rice.cs.cunit.classFile.ClassFileTools; 005 import edu.rice.cs.cunit.classFile.MethodInfo; 006 import edu.rice.cs.cunit.classFile.code.InstructionList; 007 import edu.rice.cs.cunit.classFile.code.Opcode; 008 import edu.rice.cs.cunit.classFile.code.instructions.AInstruction; 009 import edu.rice.cs.cunit.classFile.code.instructions.ReferenceInstruction; 010 import edu.rice.cs.cunit.classFile.code.instructions.GenericInstruction; 011 import edu.rice.cs.cunit.classFile.attributes.*; 012 import edu.rice.cs.cunit.classFile.constantPool.*; 013 import edu.rice.cs.cunit.classFile.constantPool.visitors.CheckUTFVisitor; 014 import edu.rice.cs.cunit.classFile.constantPool.visitors.CheckClassVisitor; 015 import edu.rice.cs.cunit.classFile.constantPool.visitors.CheckNameAndTypeVisitor; 016 import edu.rice.cs.cunit.classFile.constantPool.visitors.CheckMethodVisitor; 017 import edu.rice.cs.cunit.instrumentors.util.IScannerStrategy; 018 import edu.rice.cs.cunit.threadCheck.*; 019 import edu.rice.cs.cunit.util.*; 020 import org.w3c.dom.Node; 021 022 import java.io.IOException; 023 import java.util.*; 024 025 /** 026 * Abstract class for adding thread checks. 027 * @author Mathias Ricken 028 */ 029 public abstract class AAddThreadCheckStrategy extends AThreadCheckStrategy { 030 /** 031 * Prefix for the parameter that determines the file name as which the merged XML configuration will be saved. 032 * If this parameter is not specified, then the XML configuration is not saved. 033 */ 034 public static final String XML_ANNOT_SAVE_MERGED_PREFIX = "xml-save-merged="; 035 036 /** 037 * Data shared among all AAddThreadCheckStrategy instances. 038 */ 039 public static class SharedAddData implements IScannerStrategy { 040 /** 041 * Hash table from a fully-qualified class name to the annotations that class has. 042 */ 043 protected SoftHashMap<String, ThreadCheckAnnotationRecord> classAnnotations = 044 new SoftHashMap<String, ThreadCheckAnnotationRecord>(); 045 046 /** 047 * Hash table from a string of the form "<fully-qualified class name>::<method name><descriptor>" 048 * to the annotations that method has. 049 */ 050 protected SoftHashMap<String, ThreadCheckAnnotationRecord> methodAnnotations = 051 new SoftHashMap<String, ThreadCheckAnnotationRecord>(); 052 053 /** 054 * Class info. 055 */ 056 protected CacheInfo cacheInfo = new CacheInfo(); 057 058 /** 059 * Warnings. 060 */ 061 protected HashSet<IScannerStrategy.IScanResult> otherWarnings = 062 new HashSet<IScannerStrategy.IScanResult>(); 063 064 /** 065 * Subtyping Warnings. 066 */ 067 protected HashSet<SubtypingWarning> subtypingWarnings = 068 new HashSet<SubtypingWarning>(); 069 070 /** 071 * XML configurations used for additional annotations not based in the code. 072 */ 073 protected XMLConfig xmlAnnot = new XMLConfig(); 074 075 public SoftHashMap<String, ThreadCheckAnnotationRecord> getClassAnnotations() { 076 return classAnnotations; 077 } 078 079 public SoftHashMap<String, ThreadCheckAnnotationRecord> getMethodAnnotations() { 080 return methodAnnotations; 081 } 082 083 public CacheInfo getCacheInfo() { 084 return cacheInfo; 085 } 086 087 public HashSet<IScanResult> getOtherWarnings() { 088 return otherWarnings; 089 } 090 091 public HashSet<SubtypingWarning> getSubtypingWarnings() { 092 return subtypingWarnings; 093 } 094 095 public XMLConfig getXmlAnnot() { 096 return xmlAnnot; 097 } 098 099 /** 100 * Create a new instance of shared data. 101 * @param parameters parameters for the instrumentation strategy 102 * @param sharedData data shared among all AThreadCheckStrategy instances 103 */ 104 public SharedAddData(List<String> parameters, SharedData sharedData) { 105 XMLAnnotUtils.ConcurrentyDefinitions concDefs = new XMLAnnotUtils.ConcurrentyDefinitions(); 106 if (sharedData.getXmlFileName()!=null) { 107 String[] fns = sharedData.getXmlFileName().split(System.getProperty("path.separator")); 108 if (fns.length>0) { 109 Debug.out.println("Using XML annotations/concurrency definitions:"); 110 for(int i=0; i<fns.length; ++i) { 111 XMLConfig xc = new XMLConfig(fns[i]); 112 XMLAnnotUtils.joinXMLConcDefs(xc, concDefs); 113 Debug.out.println("\t"+fns[i]); 114 } 115 } 116 } 117 xmlAnnot = XMLAnnotUtils.convertConcDefsToClassBasedXML(concDefs); 118 for(String p: parameters) { 119 if (p.toLowerCase().startsWith(XML_ANNOT_SAVE_MERGED_PREFIX)) { 120 Debug.out.println("Saving merged XML file:"); 121 String xmlMergeFileName = p.substring(XML_ANNOT_SAVE_MERGED_PREFIX.length()); 122 xmlAnnot.save(xmlMergeFileName); 123 Debug.out.println("\t"+xmlMergeFileName); 124 } 125 } 126 } 127 128 /** 129 * Instrument the class. 130 * @param cf class file info 131 */ 132 public void instrument(ClassFile cf) { 133 // don't do anything 134 } 135 136 /** 137 * Return a list of scan results. 138 * 139 * @return list of scan results. 140 */ 141 public List<? extends IScanResult> getScanResults() { 142 ArrayList<SubtypingWarning> stlist = new ArrayList<SubtypingWarning>(subtypingWarnings); 143 Collections.sort(stlist); 144 145 ArrayList<IScanResult> list = new ArrayList<IScanResult>(stlist); 146 list.addAll(otherWarnings); 147 list.add(cacheInfo); 148 return list; 149 } 150 151 /** 152 * Instrumentation of all classes is done. 153 */ 154 public void done() { 155 // nothing to do 156 } 157 } 158 159 /** 160 * Data shared among all AAddThreadCheckStrategy instances. 161 */ 162 SharedAddData _sharedAddData; 163 164 /** 165 * Cache hit/miss message. 166 */ 167 public static class CacheInfo implements IScannerStrategy.IScanResult { 168 /** 169 * Class cache hit count. 170 */ 171 long _classCacheHit = 0; 172 173 /** 174 * Class cache miss count. 175 */ 176 long _classCacheMiss = 0; 177 178 /** 179 * Method cache hit count. 180 */ 181 long _methodCacheHit = 0; 182 183 /** 184 * Method cache miss count. 185 */ 186 long _methodCacheMiss = 0; 187 188 /** 189 * Combine predicate cache hit count. 190 */ 191 long _combinePredicateCacheHit = 0; 192 193 /** 194 * Combine predicate miss count. 195 */ 196 long _combinePredicateCacheMiss = 0; 197 198 /** 199 * Time spent looking up annotation info. 200 */ 201 long _timeSpent = 0; 202 203 public void incClassCacheHit() { 204 ++_classCacheHit; 205 } 206 207 public void incClassCacheMiss() { 208 ++_classCacheMiss; 209 } 210 211 public void incMethodCacheHit() { 212 ++_methodCacheHit; 213 } 214 215 public void incMethodCacheMiss() { 216 ++_methodCacheMiss; 217 } 218 219 public void incCombinePredicateCacheHit() { 220 ++_combinePredicateCacheHit; 221 } 222 223 public void incCombinePredicateCacheMiss() { 224 ++_combinePredicateCacheMiss; 225 } 226 227 public void addTimeSpent(long t) { 228 _timeSpent += t; 229 } 230 231 /** 232 * Return the name of the property that was scanned for. 233 * 234 * @return property name 235 */ 236 public String getPropertyName() { 237 return "ThreadChecker Cache Info"; 238 } 239 240 /** 241 * Returns a string representation of the object. 242 * @return a string representation of the object. 243 */ 244 public String toString() { 245 return "Class cache "+_classCacheHit+"/"+_classCacheMiss+", method cache "+ 246 _methodCacheHit+"/"+_methodCacheMiss+", combined predicate cache "+ 247 _combinePredicateCacheHit+"/"+_combinePredicateCacheMiss+" (hits/misses), time spent = "+ 248 StringOps.toStringMillis(_timeSpent); 249 } 250 } 251 252 /** 253 * ONLY_AFTER_REALIZED warning 254 */ 255 public static class OnlyAfterRealizedWarning implements IScannerStrategy.IScanResult { 256 /** 257 * Warning message. 258 */ 259 String msg; 260 261 /** 262 * Creates a new warning. 263 * @param msg the warning message 264 */ 265 public OnlyAfterRealizedWarning(String msg) { 266 this.msg = msg; 267 } 268 269 /** 270 * Return the name of the property that was scanned for. 271 * 272 * @return property name 273 */ 274 public String getPropertyName() { 275 return "ThreadChecker ONLY_AFTER_REALIZED Warning"; 276 } 277 278 /** 279 * Returns a string representation of the object. 280 * @return a string representation of the object. 281 */ 282 public String toString() { 283 return msg; 284 } 285 } 286 287 /** 288 * Constructor for this strategy. 289 * @param shared data shared among all AThreadCheckStrategy instances 290 * @param sharedAdd data for all AAddThreadCheckStrategy instances 291 */ 292 public AAddThreadCheckStrategy(SharedData shared, SharedAddData sharedAdd) { 293 this(new ArrayList<String>(), shared, sharedAdd); 294 } 295 296 /** 297 * Constructor for a strategy that adds thread checks. 298 * @param parameters parameters for the instrumentors 299 * @param shared data shared among all AThreadCheckStrategy instances 300 * @param sharedAdd data for all AAddThreadCheckStrategy instances 301 */ 302 public AAddThreadCheckStrategy(List<String> parameters, SharedData shared, SharedAddData sharedAdd) { 303 super(parameters, shared); 304 305 _sharedAddData = sharedAdd; 306 } 307 308 /** 309 * Get the annotations for the specified class file. 310 * @param cf class file 311 * @return annotations 312 */ 313 protected ThreadCheckAnnotationRecord getClassAnnotations(ClassFile cf) { 314 ThreadCheckAnnotationRecord ca = _sharedAddData.classAnnotations.get(cf.getThisClassName()); 315 if (ca!=null) { 316 _sharedAddData.cacheInfo.incClassCacheHit(); 317 return ca; 318 } 319 else { 320 _sharedAddData.cacheInfo.incClassCacheMiss(); 321 } 322 ThreadCheckAnnotationRecord classAR = new ThreadCheckAnnotationRecord(); 323 324 // find the names, ids and groups applied to the entire class by NotRunBy 325 extractLists("L"+ NotRunBy.class.getName(), cf.getAttributes(), classAR.denyThreadNames, classAR.denyThreadIds, classAR.denyThreadGroups, null); 326 327 // find the names, ids and groups applied to the entire class by OnlyRunBy 328 Ref<OnlyRunBy.EVENT_THREAD> allowEventThread = new Ref<OnlyRunBy.EVENT_THREAD>(null); 329 extractLists("L"+ OnlyRunBy.class.getName(), cf.getAttributes(), classAR.allowThreadNames, classAR.allowThreadIds, classAR.allowThreadGroups, 330 allowEventThread); 331 if (allowEventThread.get()!=null) { 332 classAR.allowEventThread = allowEventThread.get(); 333 } 334 else { 335 classAR.allowEventThread = OnlyRunBy.EVENT_THREAD.NO; 336 } 337 338 // find the predicate annotations 339 extractPredicateSet(cf.getAttributes(), classAR.predicateAnnotations, null); 340 341 if (!cf.getSuperClassName().equals("")) { 342 // find the annotations for the superclass 343 ClassFileTools.ClassLocation cl = null; 344 try { 345 cl = ClassFileTools.findClassFile(cf.getSuperClassName(), _sharedData.getClassPath()); 346 if (cl!=null) { 347 ClassFile scf = cl.getClassFile(); 348 ThreadCheckAnnotationRecord superClassAR = getClassAnnotations(scf); 349 classAR.add(superClassAR); 350 } 351 else { 352 _sharedData.addClassNotFoundWarning(new ClassNotFoundWarning(cf.getSuperClassName(),_sharedData.getCurrentClassName())); 353 //Debug.out.println("Warning: Could not find "+cf.getSuperClassName()); 354 } 355 356 // find the annotations for all the interfaces 357 for(ClassPoolInfo cpi: cf.getInterfaces()) { 358 String interfaceName = cpi.getName().toString().replace('/','.'); 359 cl = ClassFileTools.findClassFile(interfaceName, _sharedData.getClassPath()); 360 if (cl!=null) { 361 ClassFile icf = cl.getClassFile(); 362 ThreadCheckAnnotationRecord interfClassAR = getClassAnnotations(icf); 363 classAR.add(interfClassAR); 364 } 365 else { 366 _sharedData.addClassNotFoundWarning(new ClassNotFoundWarning(interfaceName,_sharedData.getCurrentClassName())); 367 //Debug.out.println("Warning: Could not find "+interfaceName); 368 } 369 } 370 } 371 finally { 372 try { if (cl!=null) cl.close(); } 373 catch(IOException e) { /* ignore; shouldn't cause any problems except on Windows with read locks */ } 374 } 375 } 376 377 try { 378 String path = DEFAULT_XML_PATH_PREFIX + cf.getThisClassName().replace('.', '/').replace('$', '-') + "/class"; 379 List<Node> classNodes = _sharedAddData.xmlAnnot.getNodes(path); 380 if (classNodes.size()>1) { 381 throw new ThreadCheckException("There may only be one <class> tag per class (path: "+path+")."); 382 } 383 else if (classNodes.size()==1) { 384 try { 385 ThreadCheckAnnotationRecord xmlClassAR = extractXMLAnnotations(classNodes.get(0)); 386 classAR.add(xmlClassAR); 387 } 388 catch(ThreadCheckException e) { 389 throw new ThreadCheckException("Exception extracting XML annotations for path "+path+".", e); 390 } 391 } 392 } 393 catch(XMLConfig.XMLConfigException e) { /* ignore */ } 394 395 396 _sharedAddData.classAnnotations.put(cf.getThisClassName(), classAR); 397 398 return classAR; 399 } 400 401 /** 402 * Extract the concurrency definitions from the XML file. 403 * This works both for <threadcheck> and <threadcheck:def>. 404 * @param xc XML concurrency definitions configuration 405 * @return list of ThreadCheckDefinitionRecord 406 */ 407 public List<ThreadCheckDefinitionRecord> extractXMLConcDef(XMLConfig xc) { 408 // not already cached 409 final List<ThreadCheckDefinitionRecord> concDefs = new ArrayList<ThreadCheckDefinitionRecord>(); 410 411 // read <threadcheck:def> nodes 412 List<Node> defs = xc.getNodes(DEFAULT_XML_CONC_DEF_PATH_PREFIX); 413 for(Node def: defs) { 414 try { 415 ThreadCheckDefinitionRecord cd = extractXMLConcDef(xc, def); 416 if (cd!=null) { concDefs.add(cd); } 417 } 418 catch(ThreadCheckException e) { /* ignore */ } 419 } 420 421 // read <threadcheck> node 422 IPredicate<String> processClassPred = new IPredicate<String>() { 423 public Boolean apply(String className) { 424 return true; 425 } 426 }; 427 428 ILambda.Ternary< 429 Object, 430 String, ThreadCheckAnnotationRecord, 431 HashMap<String,ThreadCheckAnnotationRecord>> classLambda = 432 new ILambda.Ternary< 433 Object, 434 String, ThreadCheckAnnotationRecord, 435 HashMap<String,ThreadCheckAnnotationRecord>>() { 436 public Object apply(String className, 437 ThreadCheckAnnotationRecord classAnnots, 438 HashMap<String, ThreadCheckAnnotationRecord> methodAnnots) { 439 // add concurrency definition for the class 440 if (!classAnnots.empty()) { 441 ThreadCheckDefinitionRecord cd = new ThreadCheckDefinitionRecord(classAnnots); 442 cd.addClass(className); 443 concDefs.add(cd); 444 } 445 446 // add concurrency defintions for the methods 447 for(String sig: methodAnnots.keySet()) { 448 ThreadCheckAnnotationRecord annots = methodAnnots.get(sig); 449 if (!annots.empty()) { 450 ThreadCheckDefinitionRecord cd = new ThreadCheckDefinitionRecord(annots); 451 cd.addMethod(className, sig, methodAnnots.get(sig).suppressSubtypingWarning); 452 concDefs.add(cd); 453 } 454 } 455 return null; 456 } 457 }; 458 459 List<ThreadCheckException> exList = new ArrayList<ThreadCheckException>(); 460 XMLAnnotUtils.ClassBased.processNode(xc, classLambda, processClassPred, exList); 461 462 if (exList.size()>0) { 463 throw new ThreadCheckException("Errors reading <threadcheck> nodes: "+Arrays.toString(exList.toArray())); 464 } 465 466 return concDefs; 467 } 468 469 /** 470 * Extract annotations for the specified path from the XML file, if there is one. 471 * @param elt element that contains the name/group/id/eventThread elements 472 * @return annotations record 473 */ 474 public ThreadCheckAnnotationRecord extractXMLAnnotations(Node elt) { 475 ThreadCheckAnnotationRecord xmlAR = new ThreadCheckAnnotationRecord(); 476 for(int i = 0; i < elt.getChildNodes().getLength(); ++i) { 477 Node element = elt.getChildNodes().item(i); 478 if (element.getNodeName().equalsIgnoreCase("name")) { 479 if (element.getAttributes().getLength() != 2) { 480 throw new ThreadCheckException("The <name> tag should have type and value attributes."); 481 } 482 Node type = element.getAttributes().getNamedItem("type"); 483 Node value = element.getAttributes().getNamedItem("value"); 484 if ((type == null) || (value == null)) { 485 throw new ThreadCheckException("The <name> tag should have type and value attributes."); 486 } 487 if (type.getNodeValue().equalsIgnoreCase("only")) { 488 xmlAR.allowThreadNames.add(value.getNodeValue()); 489 //System.out.println("\tonly name - " + value.getNodeValue()); 490 } 491 else if (type.getNodeValue().equalsIgnoreCase("not")) { 492 xmlAR.denyThreadNames.add(value.getNodeValue()); 493 //System.out.println("\tnot name - " + value.getNodeValue()); 494 } 495 else { 496 throw new ThreadCheckException("The type attribute of the <name> tag should either be \"only\" or \"not\""); 497 } 498 } 499 else if (element.getNodeName().equalsIgnoreCase("group")) { 500 if (element.getAttributes().getLength() != 2) { 501 throw new ThreadCheckException("The <group> tag should have type and value attributes."); 502 } 503 Node type = element.getAttributes().getNamedItem("type"); 504 Node value = element.getAttributes().getNamedItem("value"); 505 if ((type == null) || (value == null)) { 506 throw new ThreadCheckException("The <group> tag should have type and value attributes."); 507 } 508 if (type.getNodeValue().equalsIgnoreCase("only")) { 509 xmlAR.allowThreadGroups.add(value.getNodeValue()); 510 //System.out.println("\tonly group - " + value.getNodeValue()); 511 } 512 else if (type.getNodeValue().equalsIgnoreCase("not")) { 513 xmlAR.denyThreadGroups.add(value.getNodeValue()); 514 //System.out.println("\tnot group - " + value.getNodeValue()); 515 } 516 else { 517 throw new ThreadCheckException("The type attribute of the <group> tag should either be \"only\" or \"not\""); 518 } 519 } 520 else if (element.getNodeName().equalsIgnoreCase("id")) { 521 if (element.getAttributes().getLength() != 2) { 522 throw new ThreadCheckException("The <id> tag should have type and value attributes."); 523 } 524 Node type = element.getAttributes().getNamedItem("type"); 525 Node value = element.getAttributes().getNamedItem("value"); 526 if ((type == null) || (value == null)) { 527 throw new ThreadCheckException("The <id> tag should have type and value attributes."); 528 } 529 if (type.getNodeValue().equalsIgnoreCase("only")) { 530 xmlAR.allowThreadIds.add(Long.valueOf(value.getNodeValue())); 531 //System.out.println("\tonly id - " + value.getNodeValue()); 532 } 533 else if (type.getNodeValue().equalsIgnoreCase("not")) { 534 xmlAR.denyThreadIds.add(Long.valueOf(value.getNodeValue())); 535 //System.out.println("\tnot id - " + value.getNodeValue()); 536 } 537 else { 538 throw new ThreadCheckException("The type attribute of the <id> tag should either be \"only\" or \"not\""); 539 } 540 } 541 else if (element.getNodeName().equalsIgnoreCase("eventThread")) { 542 if (element.getAttributes().getLength() != 1) { 543 throw new ThreadCheckException("The <eventThread> tag should only have a type attribute."); 544 } 545 Node type = element.getAttributes().getNamedItem("type"); 546 if (type == null) { 547 throw new ThreadCheckException("The <eventThread> tag should have a type attribute."); 548 } 549 String str = type.getNodeValue(); 550 if (str.equalsIgnoreCase("NO")) { 551 xmlAR.allowEventThread = OnlyRunBy.EVENT_THREAD.NO; 552 } 553 else if (str.equalsIgnoreCase("ONLY")) { 554 xmlAR.allowEventThread = OnlyRunBy.EVENT_THREAD.ONLY; 555 } 556 else if (str.equalsIgnoreCase("ONLY_AFTER_REALIZED")) { 557 xmlAR.allowEventThread = OnlyRunBy.EVENT_THREAD.ONLY_AFTER_REALIZED; 558 } 559 else { 560 throw new ThreadCheckException("The type attribute of the <eventThread> tag should be an OnlyRunBy.EVENT_THREAD (was '"+str+"')"); 561 } 562 //System.out.println("\tonly eventThread - " + value.getNodeValue()); 563 } 564 else if (element.getNodeName().equalsIgnoreCase("predicate")) { 565 // extract predicate annotations from XML 566 AAnnotationsAttributeInfo.Annotation annot = extractAnnotation(element, element.getAttributes().getNamedItem("type"), 567 true); 568 PredicateAnnotationRecord par = processAnnotation(annot); 569 if (par!=null) { 570 xmlAR.predicateAnnotations.add(par); 571 // integrate passing arguments into XML 572 Node n = element.getAttributes().getNamedItem("arguments"); 573 if ((n!=null) && (n.getNodeValue().equalsIgnoreCase("true"))) { 574 par.passArguments = true; 575 } 576 } 577 } 578 else if (element.getNodeName().equalsIgnoreCase("combine")) { 579 // extract combine annotations from XML 580 AAnnotationsAttributeInfo.Annotation annot = extractAnnotation(element, element.getAttributes().getNamedItem("type"), 581 true); 582 PredicateAnnotationRecord par = processAnnotation(annot); 583 if (par!=null) { 584 xmlAR.predicateAnnotations.add(par); 585 // integrate passing arguments into XML 586 Node n = element.getAttributes().getNamedItem("arguments"); 587 if ((n!=null) && (n.getNodeValue().equalsIgnoreCase("true"))) { 588 par.passArguments = true; 589 } 590 } 591 } 592 } 593 return xmlAR; 594 } 595 596 /** 597 * Extract an annotation from the XML node specified. 598 * @param element XML node 599 * @param annotType node for the annotation 600 * @param requireArrayDesc 601 * @return annotation extracted from XML 602 */ 603 private AAnnotationsAttributeInfo.Annotation extractAnnotation(Node element, Node annotType, 604 boolean requireArrayDesc) { 605 if (annotType == null) { 606 throw new ThreadCheckException("The XML tag should have a type attribute."); 607 } 608 AUTFPoolInfo annotTypeName = new ASCIIPoolInfo(annotType.getNodeValue(), null); 609 ArrayList<AAnnotationsAttributeInfo.Annotation.NameValuePair> pairs = new ArrayList<AAnnotationsAttributeInfo.Annotation.NameValuePair>(); 610 for(int childIdx=0; childIdx<element.getChildNodes().getLength(); ++childIdx) { 611 Node childNode = element.getChildNodes().item(childIdx); 612 if (childNode.getNodeName().equals("arg")) { 613 Node name = childNode.getAttributes().getNamedItem("name"); 614 if (name == null) { 615 throw new ThreadCheckException("The <arg> tag should have a name attribute."); 616 } 617 AUTFPoolInfo pairName = new ASCIIPoolInfo(name.getNodeValue(), null); 618 AAnnotationsAttributeInfo.Annotation.AMemberValue mv = extractMemberValue(childNode, requireArrayDesc); 619 pairs.add(new AAnnotationsAttributeInfo.Annotation.NameValuePair(pairName, mv)); 620 } 621 } 622 return new AAnnotationsAttributeInfo.Annotation(annotTypeName, 623 (short)pairs.size(), 624 pairs.toArray(new AAnnotationsAttributeInfo.Annotation.NameValuePair[0])); 625 } 626 627 /** 628 * Extract an annotation member value from the XML node specified. 629 * @param childNode XML node 630 * @param requireArrayDesc 631 * @return extracted member value 632 */ 633 protected AAnnotationsAttributeInfo.Annotation.AMemberValue extractMemberValue(Node childNode, 634 boolean requireArrayDesc) { 635 AAnnotationsAttributeInfo.Annotation.AMemberValue mv; 636 Node type = childNode.getAttributes().getNamedItem("type"); 637 if ((type == null) || (type.getNodeValue().length()!=1)) { 638 throw new ThreadCheckException("The <arg> tag should have a single-character type attribute."); 639 } 640 Node value = childNode.getAttributes().getNamedItem("value"); 641 if (value == null) { 642 throw new ThreadCheckException("The <arg> tag should have a value attribute."); 643 } 644 String nodeValue = value.getNodeValue(); 645 char tag = type.getNodeValue().charAt(0); 646 try { 647 if ("BCDFIJSZs".indexOf(tag) != -1) { 648 APoolInfo poolItem = null; 649 switch(tag) { 650 case 'B': 651 case 'C': 652 case 'I': 653 case 'S': 654 case 'Z': 655 poolItem = new IntegerPoolInfo(Integer.valueOf(nodeValue),null); 656 break; 657 case 'J': 658 poolItem = new LongPoolInfo(Long.valueOf(nodeValue),null); 659 break; 660 case 'F': 661 poolItem = new FloatPoolInfo(Float.valueOf(nodeValue),null); 662 break; 663 case 'D': 664 poolItem = new DoublePoolInfo(Double.valueOf(nodeValue),null); 665 break; 666 case 's': 667 poolItem = new ASCIIPoolInfo(nodeValue,null); 668 break; 669 } 670 mv = new AAnnotationsAttributeInfo.Annotation.ConstantMemberValue(tag, poolItem); 671 } 672 else if (tag == 'e') { 673 ASCIIPoolInfo typeName = new ASCIIPoolInfo(nodeValue.substring(0, nodeValue.indexOf('.')), null); 674 ASCIIPoolInfo constValue = new ASCIIPoolInfo(nodeValue.substring(nodeValue.indexOf('.')+1), null); 675 mv = new AAnnotationsAttributeInfo.Annotation.EnumMemberValue(tag, typeName, constValue); 676 } 677 else if (tag == 'c') { 678 ASCIIPoolInfo className = new ASCIIPoolInfo(nodeValue, null); 679 mv = new AAnnotationsAttributeInfo.Annotation.ClassMemberValue(tag, className); 680 } 681 else if (tag == '@') { 682 final AAnnotationsAttributeInfo.Annotation annotation = extractAnnotation(childNode, childNode.getAttributes().getNamedItem("value"), 683 false); 684 mv = new AAnnotationsAttributeInfo.Annotation.AnnotationMemberValue(tag, annotation); 685 } 686 else if (tag == '[') { 687 if (requireArrayDesc) { 688 Node desc = childNode.getAttributes().getNamedItem("desc"); 689 if (desc == null) { 690 throw new ThreadCheckException("The <arg> tag should have a desc attribute \"[x\" " + 691 "if it is an array, where \"x\" stands for the type of the element " + 692 "(path="+XMLConfig.getNodePath(childNode)+")."); 693 } 694 if ((!desc.getNodeValue().startsWith("[")) || 695 (desc.getNodeValue().length()<2)) { 696 throw new ThreadCheckException("The <arg> tag should have a desc attribute \"[x\" " + 697 "if it is an array, where \"x\" stands for the type of the element, " + 698 "was '"+desc.getNodeValue()+"' (path="+XMLConfig.getNodePath(childNode)+")."); 699 } 700 } 701 AAnnotationsAttributeInfo.Annotation.AMemberValue[] array = 702 new AAnnotationsAttributeInfo.Annotation.AMemberValue[Integer.valueOf(value.getNodeValue())]; 703 int nextIdx = 0; 704 for(int eltIdx=0; eltIdx<childNode.getChildNodes().getLength(); ++eltIdx) { 705 Node eltNode = childNode.getChildNodes().item(eltIdx); 706 if (eltNode.getNodeName().equals("element")) { 707 array[nextIdx] = extractMemberValue(eltNode, false); 708 nextIdx++; 709 } 710 } 711 if (nextIdx!=array.length) { 712 throw new ThreadCheckException("The <arg> tag had an incorrect number of <element> children for the array."); 713 } 714 mv = new AAnnotationsAttributeInfo.Annotation.ArrayMemberValue(tag, array.length, array); 715 } 716 else { 717 throw new ThreadCheckException("The <arg> tag had an unrecognized type attribute."); 718 } 719 } 720 catch(RuntimeException e) { 721 throw new ThreadCheckException("The <arg> tag's value attribute was ill-formatted.", e); 722 } 723 return mv; 724 } 725 726 /** 727 * Get the annotations for the specified method in the specified class file. 728 * @param cf class file 729 * @param mi method information 730 * @return annotations 731 */ 732 protected ThreadCheckAnnotationRecord getMethodAnnotations(ClassFile cf, MethodInfo mi) { 733 String key = cf.getThisClassName()+CLASS_SIG_SEPARATOR_STRING+mi.getName().toString()+mi.getDescriptor().toString(); 734 ThreadCheckAnnotationRecord ca = _sharedAddData.methodAnnotations.get(key); 735 if (ca!=null) { 736 _sharedAddData.cacheInfo.incMethodCacheHit(); 737 return ca; 738 } 739 else { 740 _sharedAddData.cacheInfo.incMethodCacheMiss(); 741 } 742 743 ThreadCheckAnnotationRecord methodAR = new ThreadCheckAnnotationRecord(); 744 745 try { 746 String path = DEFAULT_XML_PATH_PREFIX + cf.getThisClassName().replace('.', '/').replace('$', '-') + "/class"; 747 List<Node> classNodes = _sharedAddData.xmlAnnot.getNodes(path); 748 if (classNodes.size()>1) { 749 throw new ThreadCheckException("There may only be one <class> tag per class."); 750 } 751 else if (classNodes.size()==1) { 752 List<Node> methodNodes = _sharedAddData.xmlAnnot.getNodes("method", classNodes.get(0)); 753 for(Node mn: methodNodes) { 754 Node sig = mn.getAttributes().getNamedItem("sig"); 755 if (sig==null) { 756 throw new ThreadCheckException("The <method> tag should have a sig attribute."); 757 } 758 if (sig.getNodeValue().equals(mi.getName().toString()+mi.getDescriptor().toString())) { 759 try { 760 ThreadCheckAnnotationRecord xmlMethodAR = extractXMLAnnotations(mn); 761 Node suppress = mn.getAttributes().getNamedItem("suppress"); 762 if ((suppress!=null) && (suppress.getNodeValue().equalsIgnoreCase("true"))) { 763 xmlMethodAR.suppressSubtypingWarning = true; 764 } 765 methodAR.add(xmlMethodAR); 766 } 767 catch(ThreadCheckException e) { 768 throw new ThreadCheckException("Exception extracting XML annotations for path "+path+".", e); 769 } 770 } 771 } 772 } 773 } 774 catch(XMLConfig.XMLConfigException e) { /* ignore */ } 775 776 // find @SuppressSubtyingWarning annotation if it's there 777 for(AAttributeInfo ai: mi.getAttributes()) { 778 if ((ai.getName().toString().equals(RuntimeInvisibleAnnotationsAttributeInfo.getAttributeName())) || 779 (ai.getName().toString().equals(RuntimeVisibleAnnotationsAttributeInfo.getAttributeName()))) { 780 ASingleAnnotationsAttributeInfo aa = (ASingleAnnotationsAttributeInfo)ai; 781 for(AAnnotationsAttributeInfo.Annotation annot: aa.getAnnotations()) { 782 if (annot.getType().equals("L"+SuppressSubtypingWarning.class.getName().replace('.','/')+";")) { 783 methodAR.suppressSubtypingWarning = true; 784 break; 785 } 786 } 787 } 788 } 789 790 // find the names, ids and groups applied to this method by NotRunBy 791 extractLists("L"+ NotRunBy.class.getName(), mi.getAttributes(), methodAR.denyThreadNames, methodAR.denyThreadIds, methodAR.denyThreadGroups, null); 792 793 // find the names, ids and groups applied to this method by OnlyRunBy 794 Ref<OnlyRunBy.EVENT_THREAD> allowEventThread = new Ref<OnlyRunBy.EVENT_THREAD>(null); 795 extractLists("L"+ OnlyRunBy.class.getName(), mi.getAttributes(), methodAR.allowThreadNames, methodAR.allowThreadIds, methodAR.allowThreadGroups, allowEventThread); 796 if (allowEventThread.get()!=null) { 797 methodAR.allowEventThread = allowEventThread.get(); 798 } 799 else { 800 methodAR.allowEventThread = OnlyRunBy.EVENT_THREAD.NO; 801 } 802 803 // find the predicate annotations 804 extractPredicateSet(mi.getAttributes(), methodAR.predicateAnnotations, mi.getDescriptor().toString()); 805 806 // a set of method signatures of the form "<method name><descriptor>" that describe methods that have 807 // been defined in superclasses or implemented interfaces already and to which the class level annotations 808 // on THIS class should therefore NOT apply 809 HashSet<String> definedAbove = new HashSet<String>(); 810 811 if (!cf.getSuperClassName().equals("")) { 812 // find the annotations for the same method in the superclass 813 boolean found = false; 814 ClassFile curcf = cf; 815 816 while(!found && !curcf.getSuperClassName().equals("")) { 817 ClassFileTools.ClassLocation cl = null; 818 try { 819 cl = ClassFileTools.findClassFile(curcf.getSuperClassName(), _sharedData.getClassPath()); 820 if (cl != null) { 821 ClassFile scf = cl.getClassFile(); 822 for(MethodInfo smi : scf.getMethods()) { 823 definedAbove.add(smi.getName().toString()+smi.getDescriptor().toString()); 824 if ((smi.getName().toString().equals(mi.getName().toString()) && 825 (smi.getDescriptor().toString().equals(mi.getDescriptor().toString())))) { 826 ThreadCheckAnnotationRecord superClassMethodAR = getMethodAnnotations(scf, smi); 827 if (!methodAR.suppressSubtypingWarning) { 828 checkForSubtypingClassWarnings(cf, mi, scf, methodAR, superClassMethodAR); 829 } 830 methodAR.add(superClassMethodAR); 831 found = true; 832 } 833 } 834 if (!found) { 835 curcf = scf; 836 } 837 } 838 else { 839 _sharedData.addClassNotFoundWarning(new ClassNotFoundWarning(curcf.getSuperClassName(),_sharedData.getCurrentClassName())); 840 //Debug.out.println("Warning: Could not find " + curcf.getSuperClassName()); 841 break; 842 } 843 } 844 finally { 845 try { if (cl!=null) cl.close(); } 846 catch(IOException e) { /* ignore; shouldn't cause any problems except on Windows with read locks */ } 847 } 848 } 849 850 // find the annotations for all the interfaces 851 for(ClassPoolInfo cpi: cf.getInterfaces()) { 852 found = false; 853 curcf = cf; 854 String superName = cpi.getName().toString().replace('/','.'); 855 while(!found && !superName.equals("")) { 856 ClassFileTools.ClassLocation cl = null; 857 try { 858 cl = ClassFileTools.findClassFile(superName, _sharedData.getClassPath()); 859 if (cl != null) { 860 ClassFile scf = cl.getClassFile(); 861 for(MethodInfo smi : scf.getMethods()) { 862 definedAbove.add(smi.getName().toString()+smi.getDescriptor().toString()); 863 if ((smi.getName().toString().equals(mi.getName().toString()) && 864 (smi.getDescriptor().toString().equals(mi.getDescriptor().toString())))) { 865 ThreadCheckAnnotationRecord interfClassMethodAR = getMethodAnnotations(scf, smi); 866 if (!methodAR.suppressSubtypingWarning) { 867 checkForSubtypingClassWarnings(cf, mi, scf, methodAR, interfClassMethodAR); 868 } 869 methodAR.add(interfClassMethodAR); 870 found = true; 871 } 872 } 873 if (!found) { 874 curcf = scf; 875 superName = scf.getSuperClassName(); 876 } 877 } 878 else { 879 _sharedData.addClassNotFoundWarning(new ClassNotFoundWarning(superName,_sharedData.getCurrentClassName())); 880 //Debug.out.println("Warning: Could not find " + superName); 881 break; 882 } 883 } 884 finally { 885 try { if (cl!=null) { cl.getInputStream().close(); } } 886 catch(IOException e) { /* ignore; shouldn't cause any problems except on Windows with read locks */ } 887 } 888 } 889 } 890 } 891 892 // add class level annotations, but only for those methods not mentioned in definedAbove 893 if (!definedAbove.contains(mi.getName().toString()+mi.getDescriptor().toString())) { 894 methodAR.add(getClassAnnotations(cf)); 895 } 896 897 _sharedAddData.methodAnnotations.put(key, methodAR); 898 899 return methodAR; 900 } 901 902 /** 903 * Check for subtyping warnings. 904 * @param cf class file 905 * @param mi method information, or null if on class level 906 * @param scf superclass/interface class file 907 * @param classAR subclass annotations 908 * @param superClassAR superclass annotations 909 */ 910 protected void checkForSubtypingClassWarnings(ClassFile cf, 911 MethodInfo mi, 912 ClassFile scf, 913 ThreadCheckAnnotationRecord classAR, 914 ThreadCheckAnnotationRecord superClassAR) { 915 String methodName = null, methodDesc = null; 916 if (mi!=null) { 917 methodName = mi.getName().toString(); 918 methodDesc = mi.getDescriptor().toString(); 919 } 920 921 for(String annot: classAR.denyThreadNames) { 922 if (!superClassAR.denyThreadNames.contains(annot)) { 923 String s = "@NotRunBy thread name '" + annot + "'"; 924 if (!_sharedAddData.subtypingWarnings.contains(new SubtypingWarning(cf.getThisClassName(), 925 scf.getThisClassName(), 926 null, 927 null, s))) { 928 _sharedAddData.subtypingWarnings.add(new SubtypingWarning(cf.getThisClassName(), 929 scf.getThisClassName(), 930 methodName, 931 methodDesc, s)); 932 } 933 } 934 } 935 for(long annot: classAR.denyThreadIds) { 936 if (!superClassAR.denyThreadIds.contains(annot)) { 937 String s = "@NotRunBy thread name '" + annot + "'"; 938 if (!_sharedAddData.subtypingWarnings.contains(new SubtypingWarning(cf.getThisClassName(), 939 scf.getThisClassName(), 940 null, 941 null, s))) { 942 _sharedAddData.subtypingWarnings.add(new SubtypingWarning(cf.getThisClassName(), 943 scf.getThisClassName(), 944 methodName, 945 methodDesc, 946 s)); 947 } 948 } 949 } 950 for(String annot: classAR.allowThreadGroups) { 951 if (!superClassAR.allowThreadGroups.contains(annot)) { 952 String s = "@OnlyRunBy thread group '" + annot + "'"; 953 if (!_sharedAddData.subtypingWarnings.contains(new SubtypingWarning(cf.getThisClassName(), 954 scf.getThisClassName(), 955 null, 956 null, s))) { 957 _sharedAddData.subtypingWarnings.add(new SubtypingWarning(cf.getThisClassName(), 958 scf.getThisClassName(), 959 methodName, 960 methodDesc, 961 s)); 962 } 963 } 964 } 965 for(String annot: classAR.allowThreadNames) { 966 if (!superClassAR.allowThreadNames.contains(annot)) { 967 String s = "@OnlyRunBy thread name '" + annot + "'"; 968 if (!_sharedAddData.subtypingWarnings.contains(new SubtypingWarning(cf.getThisClassName(), 969 scf.getThisClassName(), 970 null, 971 null, s))) { 972 _sharedAddData.subtypingWarnings.add(new SubtypingWarning(cf.getThisClassName(), 973 scf.getThisClassName(), 974 methodName, 975 methodDesc, 976 s)); 977 } 978 } 979 } 980 for(long annot: classAR.allowThreadIds) { 981 if (!superClassAR.allowThreadIds.contains(annot)) { 982 String s = "@OnlyRunBy thread id '" + annot + "'"; 983 if (!_sharedAddData.subtypingWarnings.contains(new SubtypingWarning(cf.getThisClassName(), 984 scf.getThisClassName(), 985 null, 986 null, s))) { 987 _sharedAddData.subtypingWarnings.add(new SubtypingWarning(cf.getThisClassName(), 988 scf.getThisClassName(), 989 methodName, 990 methodDesc, 991 s)); 992 } 993 } 994 } 995 for(String annot: classAR.allowThreadGroups) { 996 if (!superClassAR.allowThreadGroups.contains(annot)) { 997 String s = "@OnlyRunBy thread group '" + annot + "'"; 998 if (!_sharedAddData.subtypingWarnings.contains(new SubtypingWarning(cf.getThisClassName(), 999 scf.getThisClassName(), 1000 null, 1001 null, s))) { 1002 _sharedAddData.subtypingWarnings.add(new SubtypingWarning(cf.getThisClassName(), 1003 scf.getThisClassName(), 1004 methodName, 1005 methodDesc, 1006 s)); 1007 } 1008 } 1009 } 1010 if (classAR.allowEventThread.compareTo(superClassAR.allowEventThread)>0) { 1011 String s = "@OnlyRunBy event thread "+classAR.allowEventThread.toString()+" (>"+superClassAR.allowEventThread.toString()+")"; 1012 if (!_sharedAddData.subtypingWarnings.contains(new SubtypingWarning(cf.getThisClassName(), 1013 scf.getThisClassName(), 1014 null, 1015 null, s))) { 1016 _sharedAddData.subtypingWarnings.add(new SubtypingWarning(cf.getThisClassName(), 1017 scf.getThisClassName(), 1018 methodName, 1019 methodDesc, 1020 s)); 1021 } 1022 } 1023 1024 // check for subtyping warnings with predicate annotations? 1025 for(PredicateAnnotationRecord par: classAR.predicateAnnotations) { 1026 if (!superClassAR.predicateAnnotations.contains(par)) { 1027 String s = "Predicate annotation " + par.annotation; 1028 if (!_sharedAddData.subtypingWarnings.contains(new SubtypingWarning(cf.getThisClassName(), 1029 scf.getThisClassName(), 1030 null, 1031 null, s))) { 1032 _sharedAddData.subtypingWarnings.add(new SubtypingWarning(cf.getThisClassName(), 1033 scf.getThisClassName(), 1034 methodName, 1035 methodDesc, 1036 s)); 1037 } 1038 } 1039 } 1040 } 1041 1042 /** 1043 * Extracts the concurrency definition from an XML concurrency definitions configuration at the given node. 1044 * This works only for <threadcheck:def>. 1045 * @param xc XML configuration 1046 * @param def XML concurrency definition node 1047 * @return ThreadCheckDefinitionRecord describing the invariant 1048 * @throws ThreadCheckException 1049 */ 1050 public ThreadCheckDefinitionRecord extractXMLConcDef(XMLConfig xc, Node def) { 1051 List<Node> invs = xc.getNodes("invariant", def); 1052 if (invs.size()!=1) { 1053 throw new ThreadCheckException("There must be exactly one invariant per definition, found "+invs.size()+ 1054 " (path: "+DEFAULT_XML_CONC_DEF_PATH_PREFIX+"invariant)"); 1055 } 1056 Node inv = invs.get(0); 1057 List<Node> annots = xc.getNodes("*", inv); 1058 if (annots.size()!=1) { 1059 throw new ThreadCheckException("There must be exactly one annotation per invariant, found "+annots.size()+ 1060 " (path: "+XMLConfig.getNodePath(annots.get(0))+")"); 1061 } 1062 List<Node> classes = xc.getNodes("class", def); 1063 List<Node> methods = xc.getNodes("method", def); 1064 if (classes.size()+methods.size()<=0) { 1065 throw new ThreadCheckException("There must be at least one class or method per definition, found "+ 1066 classes.size()+" <class> and "+methods.size()+" <method> "+ 1067 "(path: "+DEFAULT_XML_CONC_DEF_PATH_PREFIX+")"); 1068 } 1069 List<Node> all = xc.getNodes("*", def); 1070 if (all.size()-invs.size()-classes.size()-methods.size()!=0) { 1071 throw new ThreadCheckException("There must be exactly one invariant and at least one class or "+ 1072 "method per definition, only <invariant>, <class> and <method> "+ 1073 "are allowed , found "+classes.size()+" <class> and "+methods.size()+ 1074 " <method>, 1 <invariant>, but "+all.size()+ 1075 " children (path: "+DEFAULT_XML_CONC_DEF_PATH_PREFIX+")"); 1076 } 1077 ThreadCheckDefinitionRecord concDef = new ThreadCheckDefinitionRecord(extractXMLAnnotations(inv)); 1078 for(Node target: classes) { 1079 int cnum = xc.getNodes("*", target).size(); 1080 if (cnum!=0) { 1081 throw new ThreadCheckException("A class node should have no child nodes, found "+cnum+ 1082 " (path: "+DEFAULT_XML_CONC_DEF_PATH_PREFIX+"class)"); 1083 } 1084 List<String> attribs = xc.getMultiple(".*",target); 1085 int anum = attribs.size(); 1086 if (anum!=1) { 1087 throw new ThreadCheckException("There should be only a name attribute for a class node, found "+ 1088 anum+" (path: "+DEFAULT_XML_CONC_DEF_PATH_PREFIX+"class)"); 1089 } 1090 if ((xc.getMultiple(".name",target).size()!=1)) { 1091 throw new ThreadCheckException("The name attribute is missing for the class node (path: "+ 1092 DEFAULT_XML_CONC_DEF_PATH_PREFIX+"class)"); 1093 } 1094 concDef.addClass(xc.getMultiple(".name",target).get(0)); 1095 } 1096 for(Node target: methods) { 1097 int cnum = xc.getNodes("*", target).size(); 1098 if (cnum!=0) { 1099 throw new ThreadCheckException("A method node should have no child nodes, there were "+cnum+ 1100 " (path: "+DEFAULT_XML_CONC_DEF_PATH_PREFIX+"method)"); 1101 } 1102 List<String> attribs = xc.getMultiple(".*",target); 1103 int anum = attribs.size(); 1104 if (anum !=2) { 1105 throw new ThreadCheckException("There should be only a name and a sig attribute for a method node, "+ 1106 "found "+anum+" (path: "+DEFAULT_XML_CONC_DEF_PATH_PREFIX+"method)"); 1107 } 1108 if ((xc.getMultiple(".name",target).size()!=1)) { 1109 throw new ThreadCheckException("The name attribute is missing for the method node (path: "+ 1110 DEFAULT_XML_CONC_DEF_PATH_PREFIX+"method)"); 1111 } 1112 if ((xc.getMultiple(".sig",target).size()!=1)) { 1113 throw new ThreadCheckException("The name attribute is missing for the method node (path: "+ 1114 DEFAULT_XML_CONC_DEF_PATH_PREFIX+"method)"); 1115 } 1116 if ((xc.getNodes(".suppress",target).size()>1)) { 1117 throw new ThreadCheckException("There should be at most one suppress atribute for the method node (path: "+ 1118 DEFAULT_XML_CONC_DEF_PATH_PREFIX+"method)"); 1119 } 1120 boolean suppressSubtypingWarnings = (xc.getNodes(".suppress",target).size()==1); 1121 concDef.addMethod(xc.getMultiple(".name",target).get(0), 1122 xc.getMultiple(".sig",target).get(0), 1123 suppressSubtypingWarnings); 1124 } 1125 return concDef; 1126 } 1127 1128 /** 1129 * Checks if an XML concurrency definition is well-formed. 1130 * @param xc XML configuration 1131 * @return a set of thrown ThreadCheckException 1132 */ 1133 public static Set<ThreadCheckException> checkXMLConcDef(XMLConfig xc) { 1134 List<String> parameters = new ArrayList<String>(); 1135 SharedData sharedData = new SharedData(parameters); 1136 AAddThreadCheckStrategy dummy = new AAddThreadCheckStrategy(sharedData, new SharedAddData(parameters, sharedData)) { 1137 public void instrument(ClassFile cf) { 1138 // no, I'm not doing that here 1139 } 1140 public void done() { 1141 // nothing to do 1142 } 1143 1144 }; 1145 1146 Set<ThreadCheckException> tces = new HashSet<ThreadCheckException>(); 1147 List<Node> defs = xc.getNodes(DEFAULT_XML_CONC_DEF_PATH_PREFIX); 1148 for(Node def: defs) { 1149 try { 1150 dummy.extractXMLConcDef(xc, def); 1151 } 1152 catch(ThreadCheckException e) { tces.add(e); } 1153 } 1154 return tces; 1155 } 1156 1157 /** 1158 * Instrumentation of all classes is done. 1159 */ 1160 public void done() { 1161 // nothing to do 1162 } 1163 1164 /** 1165 * Inserts a constructor call to the class specified for the value placed on the top of the stack. 1166 * @param cf class file 1167 * @param mi method info 1168 * @param il instruction list 1169 * @param className name of the class to construct 1170 * @param ctorSig signature of the constructor 1171 * @param loadValue instruction to load the value 1172 */ 1173 protected void insertCtorCall(ClassFile cf, MethodInfo mi, InstructionList il, String className, String ctorSig, 1174 AInstruction loadValue) { 1175 AUTFPoolInfo ctorClassName; 1176 int[] l; 1177 ClassPoolInfo ctorClass; 1178 AUTFPoolInfo ctorMethodType; 1179 AUTFPoolInfo ctorMethodName; 1180 NameAndTypePoolInfo ctorNat; 1181 MethodPoolInfo ctorMethod; 1182 boolean res; 1183 1184 // add a new name for the className 1185 ctorClassName = new ASCIIPoolInfo(className, cf.getConstantPool()); 1186 l = cf.addConstantPoolItems(new APoolInfo[]{ctorClassName}); 1187 ctorClassName = cf.getConstantPoolItem(l[0]).execute(CheckUTFVisitor.singleton(), null); 1188 1189 // add a new class item for className 1190 ctorClass = new ClassPoolInfo(ctorClassName, cf.getConstantPool()); 1191 l = cf.addConstantPoolItems(new APoolInfo[]{ctorClass}); 1192 ctorClass = cf.getConstantPoolItem(l[0]).execute(CheckClassVisitor.singleton(), null); 1193 1194 // add a new type for the className 1195 ctorMethodType = new ASCIIPoolInfo(ctorSig, cf.getConstantPool()); 1196 l = cf.addConstantPoolItems(new APoolInfo[]{ctorMethodType}); 1197 ctorMethodType = cf.getConstantPoolItem(l[0]).execute(CheckUTFVisitor.singleton(), null); 1198 1199 // add a new name for className 1200 ctorMethodName = new ASCIIPoolInfo("<init>", cf.getConstantPool()); 1201 l = cf.addConstantPoolItems(new APoolInfo[]{ctorMethodName}); 1202 ctorMethodName = cf.getConstantPoolItem(l[0]).execute(CheckUTFVisitor.singleton(), null); 1203 1204 // add a new name-and-type for className 1205 ctorNat = new NameAndTypePoolInfo(ctorMethodName, ctorMethodType, cf.getConstantPool()); 1206 l = cf.addConstantPoolItems(new APoolInfo[]{ctorNat}); 1207 ctorNat = cf.getConstantPoolItem(l[0]).execute(CheckNameAndTypeVisitor.singleton(), null); 1208 1209 // add a new method item for className 1210 ctorMethod = new MethodPoolInfo(ctorClass, ctorNat, cf.getConstantPool()); 1211 l = cf.addConstantPoolItems(new APoolInfo[]{ctorMethod}); 1212 ctorMethod = cf.getConstantPoolItem(l[0]).execute(CheckMethodVisitor.singleton(), null); 1213 1214 // top of stack: array-ref array-ref index 1215 1216 ReferenceInstruction newCall = new ReferenceInstruction(Opcode.NEW, cf.getConstantPool().indexOf(ctorClass)); 1217 il.insertInstr(newCall, mi.getCodeAttributeInfo()); 1218 res = il.advanceIndex(); 1219 assert res == true; 1220 1221 // top of stack: array-ref array-ref index className-ref 1222 1223 il.insertInstr(new GenericInstruction(Opcode.DUP), mi.getCodeAttributeInfo()); 1224 res = il.advanceIndex(); 1225 assert res == true; 1226 1227 // top of stack: array-ref array-ref index className-ref className-ref 1228 1229 il.insertInstr(loadValue, mi.getCodeAttributeInfo()); 1230 res = il.advanceIndex(); 1231 assert res == true; 1232 1233 // top of stack: array-ref array-ref index className-ref className-ref value 1234 1235 ReferenceInstruction ctorCall = new ReferenceInstruction(Opcode.INVOKESPECIAL, l[0]); 1236 il.insertInstr(ctorCall, mi.getCodeAttributeInfo()); 1237 res = il.advanceIndex(); 1238 assert res == true; 1239 1240 // top of stack: array-ref array-ref index className-ref 1241 } 1242 }