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