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 }