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 "&lt;fully-qualified class name&gt;::&lt;method name&gt;&lt;descriptor&gt;"
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 "&lt;method name&gt;&lt;descriptor&gt;" 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    }