001    package edu.rice.cs.cunit.instrumentors.threadCheck;
002    
003    import edu.rice.cs.cunit.classFile.ClassFile;
004    import edu.rice.cs.cunit.classFile.ClassFileTools;
005    import edu.rice.cs.cunit.classFile.MethodInfo;
006    import edu.rice.cs.cunit.classFile.attributes.AAnnotationsAttributeInfo;
007    import edu.rice.cs.cunit.classFile.attributes.AAttributeInfo;
008    import edu.rice.cs.cunit.classFile.attributes.CodeAttributeInfo;
009    import edu.rice.cs.cunit.classFile.code.InstructionList;
010    import edu.rice.cs.cunit.classFile.code.Opcode;
011    import edu.rice.cs.cunit.classFile.code.instructions.*;
012    import edu.rice.cs.cunit.classFile.constantPool.*;
013    import edu.rice.cs.cunit.classFile.constantPool.visitors.*;
014    import edu.rice.cs.cunit.instrumentors.InstrumentorException;
015    import edu.rice.cs.cunit.threadCheck.Combine;
016    import edu.rice.cs.cunit.threadCheck.OnlyRunBy;
017    import edu.rice.cs.cunit.threadCheck.ThreadCheck;
018    import edu.rice.cs.cunit.threadCheck.ThreadCheckException;
019    import edu.rice.cs.cunit.threadCheck.predicates.CombinePredicateTemplate;
020    import edu.rice.cs.cunit.util.Debug;
021    import edu.rice.cs.cunit.util.ILambda;
022    import edu.rice.cs.cunit.util.SoftHashMap;
023    import edu.rice.cs.cunit.util.Types;
024    
025    import java.io.*;
026    import java.util.ArrayList;
027    import java.util.HashMap;
028    import java.util.List;
029    
030    /**
031     * Instrumentor to add calls to ThreadCheck.checkCurrentThreadName/Id/Group to check if the current thread is not
032     * allowed to execute a class or method.
033     * <p/>
034     * This instrumentor checks for every method if there are @NotRunBy or @OnlyRunBy annotations attached to the method,
035     * the containing class, the same method in one of the superclasses or interfaces, or a superclass or interface, and
036     * then at the beginning of the method inserts calls to ThreadCheck..
037     *
038     * @author Mathias Ricken
039     */
040    public class AddPredicateThreadCheckStrategy extends AAddThreadCheckStrategy {
041        /**
042         * Hash table from a fully-qualified class name of the @Combine-type annotation to the generated
043         * predicate annotation record. Only the PredicateAnnotationRecord.valueList has not been filled in.
044         */
045        protected SoftHashMap<String, PredicateAnnotationRecord> _generatedPredicateRecords;
046    
047        /**
048         * The predicate class file which is cloned for the auto-generated predicates.
049         */
050        protected ClassFile _templatePredicateClassFile;
051    
052        /**
053         * Prefix for the parameter that determines the output directory of the generated class files.
054         */
055        public static final String PRED_OUT_DIR_PARAM_PREFIX = "pred-out-dir=";
056    
057        /**
058         * The output directory for the generated class files, corresponding to the default package.
059         */
060        protected File _classOutputDir = null;
061    
062        /**
063         * The directory where the generated class files go, corresponding to the package in _predicatePackage,
064         * or null if none set (that is ok unless we have to auto-generate class files).
065         */
066        protected File _predicatePackageDir = null;
067    
068        /**
069         * Prefix for the parameter that determines the package of the generated class files.
070         */
071        public static final String PRED_OUT_PACKAGE_PARAM_PREFIX = "pred-out-package=";
072    
073        /**
074         * The package for the generated class files, or null if none set (that is ok unless we have to auto-generate
075         * class files).
076         */
077        protected String _predicatePackage = null;
078    
079        /**
080         * Constructor for this strategy.
081         * @param shared data shared among all AThreadCheckStrategy instances
082         * @param sharedAdd data for all AAddThreadCheckStrategy instances
083         */
084        public AddPredicateThreadCheckStrategy(SharedData shared, SharedAddData sharedAdd) {
085            this(new ArrayList<String>(), shared, sharedAdd);
086        }
087    
088        /**
089         * Constructor for this strategy.
090         * @param parameters parameters for the instrumentors
091         * @param shared data shared among all AThreadCheckStrategy instances
092         * @param sharedAdd data for all AAddThreadCheckStrategy instances
093         */
094        public AddPredicateThreadCheckStrategy(List<String> parameters, SharedData shared, SharedAddData sharedAdd) {
095            super(parameters, shared, sharedAdd);
096    
097            for(String p: parameters) {
098                if (p.toLowerCase().startsWith(PRED_OUT_DIR_PARAM_PREFIX)) {
099                    _classOutputDir = new File(p.substring(PRED_OUT_DIR_PARAM_PREFIX.length()));
100                }
101                else if (p.toLowerCase().startsWith(PRED_OUT_PACKAGE_PARAM_PREFIX)) {
102                    _predicatePackage = p.substring(PRED_OUT_PACKAGE_PARAM_PREFIX.length());
103                }
104            }
105    
106            // find template class
107            ClassFileTools.ClassLocation tcl = ClassFileTools.findClassFile(CombinePredicateTemplate.class.getName(),
108                                                                            _sharedData.getClassPath());
109            if (tcl==null) {
110                throw new ClassNotFoundWarning(CombinePredicateTemplate.class.getName(),_sharedData.getCurrentClassName());
111            }
112            boolean fromJAR = (tcl.getJarFile()!=null);
113            try {
114                tcl.close();
115            }
116            catch(IOException ioe) { /* ignore */ }
117            _templatePredicateClassFile = tcl.getClassFile();
118    
119            String cptName = CombinePredicateTemplate.class.getName();
120            if (_predicatePackage==null) {
121                _predicatePackage = cptName.substring(0, cptName.lastIndexOf('.'));
122            }
123            if (_classOutputDir==null) {
124                if (!fromJAR) {
125                    // if template class was an individual file, not a jar file
126                    // then use that as place to put auto-generated class files
127                    File dir = tcl.getFile().getParentFile();
128                    int last = cptName.length();
129                    while((last=cptName.lastIndexOf('.',last))>=0) {
130                        dir = dir.getParentFile();
131                        --last;
132                    }
133                    _classOutputDir = dir;
134                }
135                else {
136                    // template class was in a jar file, we need a directory to put the
137                    // auto-generated class files
138                    for(String pathStr: _sharedData.getClassPath()) {
139                        File path = new File(pathStr);
140                        if (path.isDirectory()) {
141                            // found a directory, use it
142                            _classOutputDir = path;
143                            break;
144                        }
145                    }
146                }
147            }
148            if (_classOutputDir!=null) {
149                if (_predicatePackage.length()>0) {
150                    _predicatePackageDir = new File(_classOutputDir, _predicatePackage.replace('.', '/'));
151                }
152                else {
153                    _predicatePackageDir = _classOutputDir;
154                }
155                Debug.out.println("Output dir : "+_classOutputDir);
156                Debug.out.println("Package    : "+_predicatePackage);
157                Debug.out.println("Package dir: "+_predicatePackageDir);
158                _classOutputDir.mkdirs();
159                _predicatePackageDir.mkdirs();
160            }
161            else {
162                Debug.out.println("No directory for auto-generated classes set.");
163            }
164            _generatedPredicateRecords = new SoftHashMap<String, PredicateAnnotationRecord>();
165        }
166    
167        /**
168         * Instrument the class.
169         *
170         * @param cf class file info
171         */
172        public void instrument(final ClassFile cf) {
173            _sharedData.setCurrentClassName(cf.getThisClassName());
174            // process all methods in this class
175            for(MethodInfo mi : cf.getMethods()) {
176                // proces if not a native or abstract method, should have a body
177                if ((mi.getAccessFlags() & (ClassFile.ACC_NATIVE | ClassFile.ACC_ABSTRACT)) == 0) {
178                    long beginMillis = System.currentTimeMillis();
179                    ThreadCheckAnnotationRecord methodAR = getMethodAnnotations(cf, mi);
180                    long endMillis = System.currentTimeMillis();
181                    _sharedAddData.cacheInfo.addTimeSpent(endMillis-beginMillis);
182    
183                    if (!methodAR.empty()) {
184                        boolean changed = false;
185                        InstructionList il = new InstructionList(mi.getCodeAttributeInfo().getCode());
186    
187                        // if this is a constructor ("<init>"), then we should perhaps wait until
188                        // after the this() or super() call; try to find it
189                        if (mi.getName().toString().equals("<init>")) {
190                            // check if this ctor calls other ctors
191                            boolean ctorCalled = false;
192                            do {
193                                if (il.getOpcode() == Opcode.INVOKESPECIAL) {
194                                    ReferenceInstruction ri = (ReferenceInstruction)il.getInstr();
195                                    short method = Types.shortFromBytes(ri.getBytecode(), 1);
196                                    MethodPoolInfo mpi = cf.getConstantPoolItem(method).execute(CheckMethodVisitor.singleton(), null);
197                                    if (mpi.getNameAndType().getName().toString().equals("<init>")) {
198                                        ClassFile curcf = cf;
199                                        while((curcf.getThisClassName()!=null) && (!curcf.getThisClassName().equals(""))) {
200                                            if (curcf.getThisClass().toString().equals(mpi.getClassInfo().getName().toString())) {
201                                                // super() call
202                                                ctorCalled = true;
203                                                break;
204                                            }
205                                            ClassFileTools.ClassLocation cl = null;
206                                            try {
207                                                cl = ClassFileTools.findClassFile(curcf.getSuperClassName(), _sharedData.getClassPath());
208                                                if (cl != null) {
209                                                    curcf = cl.getClassFile();
210                                                }
211                                                else {
212                                                    _sharedData.addClassNotFoundWarning(new ClassNotFoundWarning(curcf.getSuperClassName(),_sharedData.getCurrentClassName()));
213                                                    //Debug.out.println("Warning: Could not find " + curcf.getSuperClassName());
214                                                    break;
215                                                }
216                                            }
217                                            finally {
218                                                try { if (cl!=null) cl.close(); }
219                                                catch(IOException e) { /* ignore; shouldn't cause any problems except on Windows with read locks */ }
220                                            }
221                                        }
222                                        if (ctorCalled) { break; }
223                                    }
224                                }
225                            } while(il.advanceIndex());
226                            if (ctorCalled) {
227                                // super call found
228                                // advance one past the super call
229                                boolean res = il.advanceIndex();
230                                assert res == true;
231                            }
232                            else {
233                                // no this() or super() call found, start at the beginning
234                                il.setIndex(0);
235                                _sharedAddData.otherWarnings.add(new OnlyAfterRealizedWarning("ignored, no this() or super() call found in constructor "+
236                                                                                            cf.getThisClassName()+"."+mi.getName()+mi.getDescriptor()));
237                                methodAR.allowEventThread = OnlyRunBy.EVENT_THREAD.NO;
238                            }
239                        }
240    
241                        // =====================
242                        // Predicate Annotations
243                        // =====================
244                        int maxStack = 0;
245                        if (methodAR.predicateAnnotations.size()>0) {
246                            // System.out.println(cf.getThisClassName()+CLASS_SIG_SEPARATOR_STRING+mi.getName().toString()+mi.getDescriptor()+":");
247                            for (PredicateAnnotationRecord par: methodAR.predicateAnnotations) {
248                                int stackUsage = 0;
249                                try {
250                                    if ((par.predicateClass !=null) && (par.predicateMI!=null)) {
251                                        changed = true;
252                                        stackUsage = insertPredicateCall(cf, mi, il, par);
253                                    }
254                                    else {
255                                        // System.out.println("\t@Combine: "+par.annotation.getType());
256                                        if (_predicatePackageDir==null) {
257                                            // no directory set for auto-generated classes
258                                            throw new ThreadCheckException("No directory found on class path for auto-generated classes; "+
259                                                                           "to auto-generate class files, there has to be a directory on the classpath "+
260                                                                           "where these class files can be stored");
261                                        }
262    
263                                        changed = true;
264                                        stackUsage = insertPredicateCall(cf, mi, il, getGeneratedPredicate(cf, par, mi.getDescriptor().toString()));
265                                    }
266                                }
267                                catch(ClassNotFoundWarning cnfw) {
268                                    _sharedData.addClassNotFoundWarning(cnfw);
269                                }
270                                finally {
271                                    if (stackUsage>maxStack) {
272                                        maxStack = stackUsage;
273                                    }
274                                }
275                            }
276                        }
277    
278                        if (changed) {
279                            // write code back
280                            mi.getCodeAttributeInfo().setCode(il.getCode());
281    
282                            // make sure we have at least five slots on the stack
283                            CodeAttributeInfo.CodeProperties cProps = mi.getCodeAttributeInfo().getProperties();
284                            cProps.maxStack = Math.max(Math.max(5, maxStack), cProps.maxStack);
285                            mi.getCodeAttributeInfo().setProperties(cProps.maxStack, cProps.maxLocals);
286                        }
287                    }
288                }
289            }
290        }
291    
292        /**
293         * Insert a call to the predicate sp
294         * @param cf current class file
295         * @param mi current method information
296         * @param il instruction list
297         * @param par predicate annotation record describing the check to add
298         * @return stack usage
299         */
300        protected int insertPredicateCall(ClassFile cf, MethodInfo mi, InstructionList il, PredicateAnnotationRecord par) {
301            ReferenceInstruction loadStringInstr = new ReferenceInstruction(Opcode.LDC_W, (short)0);
302            ReferenceInstruction checkCallInstr = new ReferenceInstruction(Opcode.INVOKESTATIC, (short)0);
303            ReferenceInstruction exceptionCallInstr = new ReferenceInstruction(Opcode.INVOKESTATIC, (short)0);
304            ReferenceInstruction predicateCallInstr = new ReferenceInstruction(Opcode.INVOKESTATIC, (short)0);
305            int stackUsage = 0;
306            ConstantPool cp = cf.getConstantPool();
307    
308            // @PredicateLink specified
309            // System.out.println("\t@PredicateLink "+par.predicateClass+CLASS_SIG_SEPARATOR_STRING+par.predicateMI.getName().toString()+
310            //                    par.predicateMI.getDescriptor().toString());
311    
312            // add check call for predicates
313            int checkPredicateCallIndex;
314            if (!par.passArguments) {
315                checkPredicateCallIndex = cf.addMethodToConstantPool(ThreadCheck.class.getName().replace('.','/'),
316                                                                     "checkCurrentThread_Predicate",
317                                                                     "(ZLjava/lang/String;)V");
318            }
319            else {
320                checkPredicateCallIndex = cf.addMethodToConstantPool(ThreadCheck.class.getName().replace('.','/'),
321                                                                     "checkCurrentThread_Predicate",
322                                                                     "([Ljava/lang/Object;ZLjava/lang/String;)V");
323            }
324            checkCallInstr.setReference(checkPredicateCallIndex);
325    
326    
327            int predicateCallIndex = cf.addMethodToConstantPool(par.predicateClass.replace('.','/'),
328                                                                par.predicateMI.getName().toString(),
329                                                                par.predicateMI.getDescriptor().toString());
330            predicateCallInstr.setReference(predicateCallIndex);
331            
332            // add exception handler call for predicates
333            int exceptionPredicateCallIndex;
334            if (!par.passArguments) {
335                exceptionPredicateCallIndex = cf.addMethodToConstantPool(ThreadCheck.class.getName().replace('.','/'),
336                                                                         "checkCurrentThread_PredicateException",
337                                                                         "(Ljava/lang/Throwable;Ljava/lang/String;)V");
338            }
339            else {
340                exceptionPredicateCallIndex = cf.addMethodToConstantPool(ThreadCheck.class.getName().replace('.','/'),
341                                                                         "checkCurrentThread_PredicateException",
342                                                                         "(Ljava/lang/Throwable;[Ljava/lang/Object;Ljava/lang/String;)V");
343            }
344            exceptionCallInstr.setReference(exceptionPredicateCallIndex);
345    
346            boolean res;
347    
348            final int startIndex = il.getIndex();
349            
350            // on stack: nothing
351    
352            // if passing arguments, put them on stack now
353            if (par.passArguments) {
354                loadArguments(cf, mi, il);
355    
356                il.insertInstr(new GenericInstruction(Opcode.DUP), mi.getCodeAttributeInfo());
357                res = il.advanceIndex();
358                assert res == true;
359    
360                // on stack: {array-ref array-ref}
361            }
362    
363            if ((mi.getAccessFlags()&ClassFile.ACC_STATIC)==0) {
364                // not static, load this
365                il.insertInstr(new GenericInstruction(Opcode.ALOAD_0), mi.getCodeAttributeInfo());
366            }
367            else {
368                // static, load null
369                il.insertInstr(new GenericInstruction(Opcode.ACONST_NULL), mi.getCodeAttributeInfo());
370            }
371            ++stackUsage;
372            res = il.advanceIndex();
373            assert res == true;
374    
375            // on stack: {array-ref array-ref} Ljava/lang/Object;(=this or null)
376    
377            if (par.passArguments) {
378                il.insertInstr(new GenericInstruction(Opcode.SWAP), mi.getCodeAttributeInfo());
379                res = il.advanceIndex();
380                assert res == true;
381            }
382    
383            // on stack: {array-ref} Ljava/lang/Object;(=this or null) {array-ref}
384    
385            // load arguments onto stack
386            for(int i=0; i<par.valueList.size(); ++i) {
387                AAnnotationsAttributeInfo.Annotation.AMemberValue mv = par.valueList.get(i);
388                String paramType = par.paramTypes.get(par.paramNames.get(i));
389    
390                // transfer the value into the class file's constant pool
391                // default values may not already be there
392                stackUsage += loadAndTransferMemberValue(mv, paramType, cf, mi, il);
393            }
394    
395            // on stack: {array-ref} Ljava/lang/Object;(=this or null) {array-ref} values
396    
397            // call predicate
398            il.insertInstr(predicateCallInstr, mi.getCodeAttributeInfo());
399            res = il.advanceIndex();
400            assert res == true;
401    
402            // now a boolean is on the top of the stack
403            // on stack: {array-ref} Z
404            int[] l;
405    
406            // add a new name for the predicate annotation
407            AUTFPoolInfo predicateAnnotName = new ASCIIPoolInfo(par.annotation.getType(), cp);
408            l = cf.addConstantPoolItems(new APoolInfo[]{predicateAnnotName});
409            predicateAnnotName = cf.getConstantPoolItem(l[0]).execute(CheckUTFVisitor.singleton(), null);
410    
411            // add a new string for the predicate annotation
412            StringPoolInfo predicateAnnotStr = new StringPoolInfo(predicateAnnotName, cp);
413            l = cf.addConstantPoolItems(new APoolInfo[]{predicateAnnotStr});
414            predicateAnnotStr = cf.getConstantPoolItem(l[0]).execute(new ADefaultPoolInfoVisitor<StringPoolInfo, Object>() {
415                public StringPoolInfo defaultCase(APoolInfo host, Object o) {
416                    throw new ClassFormatError("Info is of type " + host.getClass().getName()+ ", needs to be StringPoolInfo");
417                }
418                public StringPoolInfo stringCase(StringPoolInfo host, Object o) {
419                    return host;
420                }
421            }, null);
422    
423            // load the predicate annotation name onto the stack
424            loadStringInstr.setReference(l[0]);
425            il.insertInstr(loadStringInstr, mi.getCodeAttributeInfo());
426            res = il.advanceIndex();
427            assert res == true;
428            ++stackUsage;
429    
430            // on stack: {array-ref} Z Ljava/lang/String;
431    
432            // call check method
433            il.insertInstr(checkCallInstr, mi.getCodeAttributeInfo());
434            res = il.advanceIndex();
435            assert res == true;
436    
437            // on stack: nothing
438    
439            final int endIndex = il.getIndex();
440    
441            // here, we will later insert the goto_w instruction
442    
443            // exception handler begins here
444            
445            // on stack: Ljava/lang/Throwable;
446            
447            if (par.passArguments) {
448                loadArguments(cf, mi, il);
449            }
450            
451            // on stack: Ljava/lang/Throwable; {array-ref}
452            
453            // load the predicate annotation name onto the stack
454            loadStringInstr.setReference(l[0]);
455            il.insertInstr(loadStringInstr, mi.getCodeAttributeInfo());
456            res = il.advanceIndex();
457            assert res == true;
458    
459            // on stack: Ljava/lang/Throwable; {array-ref} Ljava/lang/String;
460    
461            // call exception handler method
462            il.insertInstr(exceptionCallInstr, mi.getCodeAttributeInfo());
463            res = il.advanceIndex();
464            assert res == true;
465            
466            // on stack: nothing
467            
468            // insert goto_w instruction
469            final int skipIndex = il.getIndex();
470            il.setIndex(endIndex);
471            WideBranchInstruction gotoInstr = new WideBranchInstruction(Opcode.GOTO_W, skipIndex);
472            il.insertInstr(gotoInstr, mi.getCodeAttributeInfo());
473            il.setIndex(skipIndex);
474            res = il.advanceIndex();
475            assert res == true;
476            
477            // update exceptions list
478            CodeAttributeInfo.ExceptionTableEntry[] excTable =
479                new CodeAttributeInfo.ExceptionTableEntry[mi.getCodeAttributeInfo().getExceptionTableEntries().length+1];
480            System.arraycopy(mi.getCodeAttributeInfo().getExceptionTableEntries(),
481                             0,
482                             excTable,
483                             0,
484                             mi.getCodeAttributeInfo().getExceptionTableEntries().length);
485            excTable[excTable.length-1] = new CodeAttributeInfo.ExceptionTableEntry((short)il.getPCFromIndex(startIndex),
486                                                                                    (short)il.getPCFromIndex(endIndex),
487                                                                                    (short)il.getPCFromIndex(endIndex+1),
488                                                                                    (short)0);
489            mi.getCodeAttributeInfo().setExceptionTableEntries(excTable);
490            
491            return (par.passArguments)?stackUsage+9:stackUsage;
492        }
493    
494        /*
495         * Transfer the method arguments to the stack.
496         * @param cf target class file
497         * @param mi method info
498         * @param il instruction list, or null if this is a member value to be loaded
499         */
500        private void loadArguments(final ClassFile cf,
501                                   final MethodInfo mi,
502                                   final InstructionList il) {
503            boolean res;
504            
505            // add a new name for the element type
506            AUTFPoolInfo objectClassName = new ASCIIPoolInfo("java/lang/Object", cf.getConstantPool());
507            int[] l = cf.addConstantPoolItems(new APoolInfo[]{objectClassName});
508            objectClassName = cf.getConstantPoolItem(l[0]).execute(CheckUTFVisitor.singleton(), null);
509            
510            // add a new class item for the element class
511            ClassPoolInfo objectClass = new ClassPoolInfo(objectClassName, cf.getConstantPool());
512            l = cf.addConstantPoolItems(new APoolInfo[]{objectClass});
513            objectClass = cf.getConstantPoolItem(l[0]).execute(CheckClassVisitor.singleton(), null);
514            
515            ReferenceInstruction anewarray = new ReferenceInstruction(Opcode.ANEWARRAY, cf.getConstantPool().indexOf(objectClass));
516            
517            // add number of elements to class pool
518            String sig = mi.getDescriptor().toString();
519            List<String> methodArgTypes = ClassFileTools.getSignatures(sig.substring(1,sig.lastIndexOf(')')));
520            IntegerPoolInfo count = new IntegerPoolInfo(methodArgTypes.size(), cf.getConstantPool());
521            l = cf.addConstantPoolItems(new APoolInfo[]{count});
522            count = cf.getConstantPoolItem(l[0]).execute(new ADefaultPoolInfoVisitor<IntegerPoolInfo, Object>() {
523                public IntegerPoolInfo defaultCase(APoolInfo host, Object param) {
524                    throw new ClassFormatError("Info is of type " + host.getClass().getName() + ", needs to be IntegerPoolInfo");
525                }
526                public IntegerPoolInfo intCase(IntegerPoolInfo host, Object param) {
527                    return host;
528                }
529            }, null);
530            
531            // load number of arguments onto stack
532            il.insertInstr(new ReferenceInstruction(Opcode.LDC_W, cf.getConstantPool().indexOf(count)), mi.getCodeAttributeInfo());
533            res = il.advanceIndex();
534            assert res == true;
535            
536            // create an array of objects of that size
537            il.insertInstr(anewarray, mi.getCodeAttributeInfo());
538            res = il.advanceIndex();
539            assert res == true;
540            
541            // top of stack: array-ref
542            
543            // for static methods, the first method argument has to be loaded with ALOAD_0;
544            // for non-static methods, it's ALOAD_1
545            int lvIndex = (ClassFile.ACC_STATIC == (mi.getAccessFlags() & ClassFile.ACC_STATIC))?0:1;
546            int nextLVIndex;
547            int arrayIndex = 0;
548            for(String type: methodArgTypes) {
549                IntegerPoolInfo pi;
550                ReferenceInstruction ldc_w;
551                switch(type.charAt(0)) {
552                    case '[':
553                    case 'L':
554                        nextLVIndex = lvIndex + 1;
555                        
556                        // top of stack: array-ref
557                        il.insertInstr(new GenericInstruction(Opcode.DUP), mi.getCodeAttributeInfo());
558                        res = il.advanceIndex();
559                        assert res == true;
560                        
561                        // top of stack: array-ref array-ref
562                        
563                        pi = new IntegerPoolInfo(arrayIndex, cf.getConstantPool());
564                        l = cf.addConstantPoolItems(new APoolInfo[]{pi});
565                        pi = cf.getConstantPoolItem(l[0]).execute(new ADefaultPoolInfoVisitor<IntegerPoolInfo, Object>() {
566                            public IntegerPoolInfo defaultCase(APoolInfo host, Object o) {
567                                throw new ClassFormatError("Info is of type " + host.getClass().getName() + ", needs to be IntegerPoolInfo");
568                            }
569                            public IntegerPoolInfo intCase(IntegerPoolInfo host, Object o) {
570                                return host;
571                            }
572                        }, null);
573                        ldc_w = new ReferenceInstruction(Opcode.LDC_W, cf.getConstantPool().indexOf(pi));
574                        il.insertInstr(ldc_w, mi.getCodeAttributeInfo());
575                        res = il.advanceIndex();
576                        assert res == true;
577                        
578                        // top of stack: array-ref array-ref index
579                        
580                        il.insertInstr(Opcode.getShortestLoadStoreInstruction(Opcode.ALOAD, (short)lvIndex), mi.getCodeAttributeInfo());
581                        res = il.advanceIndex();
582                        assert res == true;
583                        break;
584                        
585                        // top of stack: array-ref array-ref index ref
586                    case 'B':
587                        nextLVIndex = lvIndex + 1;
588                        
589                        // top of stack: array-ref
590                        il.insertInstr(new GenericInstruction(Opcode.DUP), mi.getCodeAttributeInfo());
591                        res = il.advanceIndex();
592                        assert res == true;
593                        
594                        // top of stack: array-ref array-ref
595                        
596                        pi = new IntegerPoolInfo(arrayIndex, cf.getConstantPool());
597                        l = cf.addConstantPoolItems(new APoolInfo[]{pi});
598                        pi = cf.getConstantPoolItem(l[0]).execute(new ADefaultPoolInfoVisitor<IntegerPoolInfo, Object>() {
599                            public IntegerPoolInfo defaultCase(APoolInfo host, Object o) {
600                                throw new ClassFormatError("Info is of type " + host.getClass().getName() + ", needs to be IntegerPoolInfo");
601                            }
602                            public IntegerPoolInfo intCase(IntegerPoolInfo host, Object o) {
603                                return host;
604                            }
605                        }, null);
606                        ldc_w = new ReferenceInstruction(Opcode.LDC_W, cf.getConstantPool().indexOf(pi));
607                        il.insertInstr(ldc_w, mi.getCodeAttributeInfo());
608                        res = il.advanceIndex();
609                        assert res == true;
610                        
611                        // top of stack: array-ref array-ref index
612                        
613                        insertCtorCall(cf, mi, il, "java/lang/Byte", "(B)V",
614                                       Opcode.getShortestLoadStoreInstruction(Opcode.ILOAD, (short)lvIndex));
615                        
616                        // top of stack: array-ref array-ref index Byte-ref
617                        break;
618                    case 'C':
619                        nextLVIndex = lvIndex + 1;
620                        
621                        // top of stack: array-ref
622                        il.insertInstr(new GenericInstruction(Opcode.DUP), mi.getCodeAttributeInfo());
623                        res = il.advanceIndex();
624                        assert res == true;
625                        
626                        // top of stack: array-ref array-ref
627                        
628                        pi = new IntegerPoolInfo(arrayIndex, cf.getConstantPool());
629                        l = cf.addConstantPoolItems(new APoolInfo[]{pi});
630                        pi = cf.getConstantPoolItem(l[0]).execute(new ADefaultPoolInfoVisitor<IntegerPoolInfo, Object>() {
631                            public IntegerPoolInfo defaultCase(APoolInfo host, Object o) {
632                                throw new ClassFormatError("Info is of type " + host.getClass().getName() + ", needs to be IntegerPoolInfo");
633                            }
634                            public IntegerPoolInfo intCase(IntegerPoolInfo host, Object o) {
635                                return host;
636                            }
637                        }, null);
638                        ldc_w = new ReferenceInstruction(Opcode.LDC_W, cf.getConstantPool().indexOf(pi));
639                        il.insertInstr(ldc_w, mi.getCodeAttributeInfo());
640                        res = il.advanceIndex();
641                        assert res == true;
642                        
643                        // top of stack: array-ref array-ref index
644                        
645                        insertCtorCall(cf, mi, il, "java/lang/Character", "(C)V",
646                                       Opcode.getShortestLoadStoreInstruction(Opcode.ILOAD, (short)lvIndex));
647                        
648                        // top of stack: array-ref array-ref index Char-ref
649                        break;
650                    case 'I':
651                        nextLVIndex = lvIndex + 1;
652                        
653                        // top of stack: array-ref
654                        il.insertInstr(new GenericInstruction(Opcode.DUP), mi.getCodeAttributeInfo());
655                        res = il.advanceIndex();
656                        assert res == true;
657                        
658                        // top of stack: array-ref array-ref
659                        
660                        pi = new IntegerPoolInfo(arrayIndex, cf.getConstantPool());
661                        l = cf.addConstantPoolItems(new APoolInfo[]{pi});
662                        pi = cf.getConstantPoolItem(l[0]).execute(new ADefaultPoolInfoVisitor<IntegerPoolInfo, Object>() {
663                            public IntegerPoolInfo defaultCase(APoolInfo host, Object o) {
664                                throw new ClassFormatError("Info is of type " + host.getClass().getName() + ", needs to be IntegerPoolInfo");
665                            }
666                            public IntegerPoolInfo intCase(IntegerPoolInfo host, Object o) {
667                                return host;
668                            }
669                        }, null);
670                        ldc_w = new ReferenceInstruction(Opcode.LDC_W, cf.getConstantPool().indexOf(pi));
671                        il.insertInstr(ldc_w, mi.getCodeAttributeInfo());
672                        res = il.advanceIndex();
673                        assert res == true;
674                        
675                        // top of stack: array-ref array-ref index
676                        
677                        insertCtorCall(cf, mi, il, "java/lang/Integer", "(I)V",
678                                       Opcode.getShortestLoadStoreInstruction(Opcode.ILOAD, (short)lvIndex));
679                        
680                        // top of stack: array-ref array-ref index Integer-ref
681                        break;
682                    case 'S':
683                        nextLVIndex = lvIndex + 1;
684                        
685                        // top of stack: array-ref
686                        il.insertInstr(new GenericInstruction(Opcode.DUP), mi.getCodeAttributeInfo());
687                        res = il.advanceIndex();
688                        assert res == true;
689                        
690                        // top of stack: array-ref array-ref
691                        
692                        pi = new IntegerPoolInfo(arrayIndex, cf.getConstantPool());
693                        l = cf.addConstantPoolItems(new APoolInfo[]{pi});
694                        pi = cf.getConstantPoolItem(l[0]).execute(new ADefaultPoolInfoVisitor<IntegerPoolInfo, Object>() {
695                            public IntegerPoolInfo defaultCase(APoolInfo host, Object o) {
696                                throw new ClassFormatError("Info is of type " + host.getClass().getName() + ", needs to be IntegerPoolInfo");
697                            }
698                            public IntegerPoolInfo intCase(IntegerPoolInfo host, Object o) {
699                                return host;
700                            }
701                        }, null);
702                        ldc_w = new ReferenceInstruction(Opcode.LDC_W, cf.getConstantPool().indexOf(pi));
703                        il.insertInstr(ldc_w, mi.getCodeAttributeInfo());
704                        res = il.advanceIndex();
705                        assert res == true;
706                        
707                        // top of stack: array-ref array-ref index
708                        
709                        insertCtorCall(cf, mi, il, "java/lang/Short", "(S)V",
710                                       Opcode.getShortestLoadStoreInstruction(Opcode.ILOAD, (short)lvIndex));
711                        
712                        // top of stack: array-ref array-ref index Short-ref
713                        break;
714                    case 'Z':
715                        nextLVIndex = lvIndex + 1;
716                        
717                        // top of stack: array-ref
718                        il.insertInstr(new GenericInstruction(Opcode.DUP), mi.getCodeAttributeInfo());
719                        res = il.advanceIndex();
720                        assert res == true;
721                        
722                        // top of stack: array-ref array-ref
723                        
724                        pi = new IntegerPoolInfo(arrayIndex, cf.getConstantPool());
725                        l = cf.addConstantPoolItems(new APoolInfo[]{pi});
726                        pi = cf.getConstantPoolItem(l[0]).execute(new ADefaultPoolInfoVisitor<IntegerPoolInfo, Object>() {
727                            public IntegerPoolInfo defaultCase(APoolInfo host, Object o) {
728                                throw new ClassFormatError("Info is of type " + host.getClass().getName() + ", needs to be IntegerPoolInfo");
729                            }
730                            public IntegerPoolInfo intCase(IntegerPoolInfo host, Object o) {
731                                return host;
732                            }
733                        }, null);
734                        ldc_w = new ReferenceInstruction(Opcode.LDC_W, cf.getConstantPool().indexOf(pi));
735                        il.insertInstr(ldc_w, mi.getCodeAttributeInfo());
736                        res = il.advanceIndex();
737                        assert res == true;
738                        
739                        // top of stack: array-ref array-ref index
740                        
741                        insertCtorCall(cf, mi, il, "java/lang/Boolean", "(Z)V",
742                                       Opcode.getShortestLoadStoreInstruction(Opcode.ILOAD, (short)lvIndex));
743                        
744                        // top of stack: array-ref array-ref index Boolean-ref
745                        break;
746                    case 'F':
747                        nextLVIndex = lvIndex + 1;
748                        
749                        // top of stack: array-ref
750                        il.insertInstr(new GenericInstruction(Opcode.DUP), mi.getCodeAttributeInfo());
751                        res = il.advanceIndex();
752                        assert res == true;
753                        
754                        // top of stack: array-ref array-ref
755                        
756                        pi = new IntegerPoolInfo(arrayIndex, cf.getConstantPool());
757                        l = cf.addConstantPoolItems(new APoolInfo[]{pi});
758                        pi = cf.getConstantPoolItem(l[0]).execute(new ADefaultPoolInfoVisitor<IntegerPoolInfo, Object>() {
759                            public IntegerPoolInfo defaultCase(APoolInfo host, Object o) {
760                                throw new ClassFormatError("Info is of type " + host.getClass().getName() + ", needs to be IntegerPoolInfo");
761                            }
762                            public IntegerPoolInfo intCase(IntegerPoolInfo host, Object o) {
763                                return host;
764                            }
765                        }, null);
766                        ldc_w = new ReferenceInstruction(Opcode.LDC_W, cf.getConstantPool().indexOf(pi));
767                        il.insertInstr(ldc_w, mi.getCodeAttributeInfo());
768                        res = il.advanceIndex();
769                        assert res == true;
770                        
771                        // top of stack: array-ref array-ref index
772                        
773                        insertCtorCall(cf, mi, il, "java/lang/Float", "(F)V",
774                                       Opcode.getShortestLoadStoreInstruction(Opcode.FLOAD, (short)lvIndex));
775                        
776                        // top of stack: array-ref array-ref index Float-ref
777                        break;
778                    case 'J':
779                        nextLVIndex = lvIndex + 2;
780                        
781                        // top of stack: array-ref
782                        il.insertInstr(new GenericInstruction(Opcode.DUP), mi.getCodeAttributeInfo());
783                        res = il.advanceIndex();
784                        assert res == true;
785                        
786                        // top of stack: array-ref array-ref
787                        
788                        pi = new IntegerPoolInfo(arrayIndex, cf.getConstantPool());
789                        l = cf.addConstantPoolItems(new APoolInfo[]{pi});
790                        pi = cf.getConstantPoolItem(l[0]).execute(new ADefaultPoolInfoVisitor<IntegerPoolInfo, Object>() {
791                            public IntegerPoolInfo defaultCase(APoolInfo host, Object o) {
792                                throw new ClassFormatError("Info is of type " + host.getClass().getName() + ", needs to be IntegerPoolInfo");
793                            }
794                            public IntegerPoolInfo intCase(IntegerPoolInfo host, Object o) {
795                                return host;
796                            }
797                        }, null);
798                        ldc_w = new ReferenceInstruction(Opcode.LDC_W, cf.getConstantPool().indexOf(pi));
799                        il.insertInstr(ldc_w, mi.getCodeAttributeInfo());
800                        res = il.advanceIndex();
801                        assert res == true;
802                        
803                        // top of stack: array-ref array-ref index
804                        
805                        insertCtorCall(cf, mi, il, "java/lang/Long", "(L)V",
806                                       Opcode.getShortestLoadStoreInstruction(Opcode.LLOAD, (short)lvIndex));
807                        
808                        // top of stack: array-ref array-ref index Long-ref
809                        break;
810                    case 'D':
811                        nextLVIndex = lvIndex + 2;
812                        
813                        // top of stack: array-ref
814                        il.insertInstr(new GenericInstruction(Opcode.DUP), mi.getCodeAttributeInfo());
815                        res = il.advanceIndex();
816                        assert res == true;
817                        
818                        // top of stack: array-ref array-ref
819                        
820                        pi = new IntegerPoolInfo(arrayIndex, cf.getConstantPool());
821                        l = cf.addConstantPoolItems(new APoolInfo[]{pi});
822                        pi = cf.getConstantPoolItem(l[0]).execute(new ADefaultPoolInfoVisitor<IntegerPoolInfo, Object>() {
823                            public IntegerPoolInfo defaultCase(APoolInfo host, Object o) {
824                                throw new ClassFormatError("Info is of type " + host.getClass().getName() + ", needs to be IntegerPoolInfo");
825                            }
826                            public IntegerPoolInfo intCase(IntegerPoolInfo host, Object o) {
827                                return host;
828                            }
829                        }, null);
830                        ldc_w = new ReferenceInstruction(Opcode.LDC_W, cf.getConstantPool().indexOf(pi));
831                        il.insertInstr(ldc_w, mi.getCodeAttributeInfo());
832                        res = il.advanceIndex();
833                        assert res == true;
834                        
835                        // top of stack: array-ref array-ref index
836                        
837                        insertCtorCall(cf, mi, il, "java/lang/Double", "(D)V",
838                                       Opcode.getShortestLoadStoreInstruction(Opcode.DLOAD, (short)lvIndex));
839                        
840                        // top of stack: array-ref array-ref index Double-ref
841                        break;
842                    case 'V':
843                        throw new ThreadCheckException("One of the method parameters had type void (processing "+
844                                                       cf.getThisClassName()+", "+mi.getName().toString()+
845                                                       mi.getDescriptor().toString()+")");
846                    default:
847                        throw new ThreadCheckException("One of the method parameters had an unknown type '"+type.charAt(0)+"' (processing "+
848                                                       cf.getThisClassName()+", "+mi.getName().toString()+
849                                                       mi.getDescriptor().toString()+")");
850                }
851                
852                // top of stack: array-ref array-ref index value
853                
854                il.insertInstr(new GenericInstruction(Opcode.AASTORE), mi.getCodeAttributeInfo());
855                res = il.advanceIndex();
856                assert res == true;
857                
858                // top of stack: array-ref
859                ++arrayIndex;
860                lvIndex = nextLVIndex;
861            }
862            
863            // on stack: {array-ref}   
864        }
865        
866        /**
867         * Transfer the annotation member value to the class file.
868         * @param value annotation pair value
869         * @param paramType parameter type, or null if that information is not available
870         * @param cf target class file
871         * @param mi method info
872         * @param il instruction list, or null if this is a member value to be loaded
873         * @return stack space required (2 for long and double, 1 otherwise)
874         */
875        private int loadAndTransferMemberValue(AAnnotationsAttributeInfo.Annotation.AMemberValue value,
876                                               final String paramType,
877                                               final ClassFile cf,
878                                               final MethodInfo mi,
879                                               final InstructionList il) {
880            return value.execute(new AAnnotationsAttributeInfo.Annotation.IMemberValueVisitor<Integer, Object>() {
881                public Integer constantMemberCase(AAnnotationsAttributeInfo.Annotation.ConstantMemberValue host, Object o) {
882                    // add the instruction to load the constant value:
883                    // ldc_w for int, float and strings
884                    // ldc2_w for long and double
885                    return host.getConstValue().execute(new ADefaultPoolInfoVisitor<Integer, Object>() {
886                        public Integer defaultCase(APoolInfo host, Object param) {
887                            // should never happen
888                            throw new BadPredicateAnnotationWarning("Unexpected annotation member constant value: "+host.getClass().getName()+
889                                                                    " (processing " + _sharedData.getCurrentClassName() + ")");
890                        }
891                        private Integer primitive1Case(APoolInfo host) {
892                            if (il!=null) {
893                                ReferenceInstruction ldc_w = new ReferenceInstruction(Opcode.LDC_W, cf.getConstantPool().indexOf(host));
894                                il.insertInstr(ldc_w, mi.getCodeAttributeInfo());
895                                boolean res = il.advanceIndex();
896                                assert res == true;
897                                return 1;
898                            }
899                            else {
900                                return 0;
901                            }
902                        }
903                        private Integer primitive2Case(APoolInfo host) {
904                            if (il!=null) {
905                                ReferenceInstruction ldc2_w = new ReferenceInstruction(Opcode.LDC2_W, cf.getConstantPool().indexOf(host));
906                                il.insertInstr(ldc2_w, mi.getCodeAttributeInfo());
907                                boolean res = il.advanceIndex();
908                                assert res == true;
909                                return 2;
910                            }
911                            else {
912                                return 0;
913                            }
914                        }
915                        private Integer utfCase(AUTFPoolInfo host) {
916                            // for string literals, we need to add a string constant first
917                            StringPoolInfo spi = new StringPoolInfo(host, cf.getConstantPool());
918                            int[] l = cf.addConstantPoolItems(new APoolInfo[]{spi});
919                            spi = cf.getConstantPoolItem(l[0]).execute(new ADefaultPoolInfoVisitor<StringPoolInfo, Object>() {
920                                public StringPoolInfo defaultCase(APoolInfo host, Object o) {
921                                    throw new ClassFormatError("Info is of type " + host.getClass().getName() + ", needs to be StringPoolInfo");
922                                }
923                                public StringPoolInfo stringCase(StringPoolInfo host, Object o) {
924                                    return host;
925                                }
926                            }, null);
927                            // then load the string constant using ldc_w
928                            return primitive1Case(spi);
929                        }
930                        public Integer intCase(IntegerPoolInfo host, Object param) {
931                            IntegerPoolInfo pi = new IntegerPoolInfo(host.getIntValue(), cf.getConstantPool());
932                            int[] l = cf.addConstantPoolItems(new APoolInfo[]{pi});
933                            pi = cf.getConstantPoolItem(l[0]).execute(new ADefaultPoolInfoVisitor<IntegerPoolInfo, Object>() {
934                                public IntegerPoolInfo defaultCase(APoolInfo host, Object o) {
935                                    throw new ClassFormatError("Info is of type " + host.getClass().getName() + ", needs to be IntegerPoolInfo");
936                                }
937                                public IntegerPoolInfo intCase(IntegerPoolInfo host, Object o) {
938                                    return host;
939                                }
940                            }, null);
941                            return primitive1Case(pi);
942                        }
943                        public Integer floatCase(FloatPoolInfo host, Object param) {
944                            FloatPoolInfo pi = new FloatPoolInfo(host.getFloatValue(), cf.getConstantPool());
945                            int[] l = cf.addConstantPoolItems(new APoolInfo[]{pi});
946                            pi = cf.getConstantPoolItem(l[0]).execute(new ADefaultPoolInfoVisitor<FloatPoolInfo, Object>() {
947                                public FloatPoolInfo defaultCase(APoolInfo host, Object o) {
948                                    throw new ClassFormatError("Info is of type " + host.getClass().getName() + ", needs to be FloatPoolInfo");
949                                }
950                                public FloatPoolInfo floatCase(FloatPoolInfo host, Object o) {
951                                    return host;
952                                }
953                            }, null);
954                            return primitive1Case(pi);
955                        }
956                        public Integer longCase(LongPoolInfo host, Object param) {
957                            LongPoolInfo pi = new LongPoolInfo(host.getLongValue(), cf.getConstantPool());
958                            int[] l = cf.addConstantPoolItems(new APoolInfo[]{pi});
959                            pi = cf.getConstantPoolItem(l[0]).execute(new ADefaultPoolInfoVisitor<LongPoolInfo, Object>() {
960                                public LongPoolInfo defaultCase(APoolInfo host, Object o) {
961                                    throw new ClassFormatError("Info is of type " + host.getClass().getName() + ", needs to be LongPoolInfo");
962                                }
963                                public LongPoolInfo longCase(LongPoolInfo host, Object o) {
964                                    return host;
965                                }
966                            }, null);
967                            return primitive2Case(pi);
968                        }
969                        public Integer doubleCase(DoublePoolInfo host, Object param) {
970                            DoublePoolInfo pi = new DoublePoolInfo(host.getDoubleValue(), cf.getConstantPool());
971                            int[] l = cf.addConstantPoolItems(new APoolInfo[]{pi});
972                            pi = cf.getConstantPoolItem(l[0]).execute(new ADefaultPoolInfoVisitor<DoublePoolInfo, Object>() {
973                                public DoublePoolInfo defaultCase(APoolInfo host, Object o) {
974                                    throw new ClassFormatError("Info is of type " + host.getClass().getName() + ", needs to be DoublePoolInfo");
975                                }
976                                public DoublePoolInfo doubleCase(DoublePoolInfo host, Object o) {
977                                    return host;
978                                }
979                            }, null);
980                            return primitive2Case(pi);
981                        }
982                        public Integer asciizCase(ASCIIPoolInfo host, Object param) {
983                            ASCIIPoolInfo pi = new ASCIIPoolInfo(host.toString(), cf.getConstantPool());
984                            int[] l = cf.addConstantPoolItems(new APoolInfo[]{pi});
985                            pi = cf.getConstantPoolItem(l[0]).execute(new ADefaultPoolInfoVisitor<ASCIIPoolInfo, Object>() {
986                                public ASCIIPoolInfo defaultCase(APoolInfo host, Object o) {
987                                    throw new ClassFormatError("Info is of type " + host.getClass().getName() + ", needs to be ASCIIPoolInfo");
988                                }
989                                public ASCIIPoolInfo asciizCase(ASCIIPoolInfo host, Object param) {
990                                    return host;
991                                }
992                            }, null);
993                            return utfCase(pi);
994                        }
995                        public Integer unicodeCase(UnicodePoolInfo host, Object param) {
996                            UnicodePoolInfo pi = new UnicodePoolInfo(host.toString(), cf.getConstantPool());
997                            int[] l = cf.addConstantPoolItems(new APoolInfo[]{pi});
998                            pi = cf.getConstantPoolItem(l[0]).execute(new ADefaultPoolInfoVisitor<UnicodePoolInfo, Object>() {
999                                public UnicodePoolInfo defaultCase(APoolInfo host, Object o) {
1000                                    throw new ClassFormatError("Info is of type " + host.getClass().getName() + ", needs to be UnicodePoolInfo");
1001                                }
1002                                public UnicodePoolInfo unicodeCase(UnicodePoolInfo host, Object o) {
1003                                    return host;
1004                                }
1005                            }, null);
1006                            return utfCase(pi);
1007                        }
1008                    }, null);
1009                }
1010                public Integer enumMemberCase(AAnnotationsAttributeInfo.Annotation.EnumMemberValue host, Object o) {
1011                    // add the type name to the constant pool with 'L' and ';' at the beginning and end,
1012                    // required for the name-and-type item
1013                    String enumTypeName = host.getTypeName().toString();
1014                    AUTFPoolInfo typeNamePoolItem = new ASCIIPoolInfo(enumTypeName, cf.getConstantPool());
1015                    int[] l = cf.addConstantPoolItems(new APoolInfo[]{typeNamePoolItem});
1016                    typeNamePoolItem = cf.getConstantPoolItem(l[0]).execute(CheckUTFVisitor.singleton(), null);
1017    
1018                    // add the type name to the constant pool without 'L' and ';', required for the class item
1019                    AUTFPoolInfo simpleTypeNamePoolItem = new ASCIIPoolInfo(enumTypeName.substring(1,enumTypeName.length()-1), cf.getConstantPool());
1020                    l = cf.addConstantPoolItems(new APoolInfo[]{simpleTypeNamePoolItem});
1021                    simpleTypeNamePoolItem = cf.getConstantPoolItem(l[0]).execute(CheckUTFVisitor.singleton(), null);
1022    
1023                    // add the value name to the constant pool
1024                    AUTFPoolInfo valueNamePoolItem = new ASCIIPoolInfo(host.getConstValue().toString(), cf.getConstantPool());
1025                    l = cf.addConstantPoolItems(new APoolInfo[]{valueNamePoolItem});
1026                    valueNamePoolItem = cf.getConstantPoolItem(l[0]).execute(CheckUTFVisitor.singleton(), null);
1027    
1028                    // add a new class item
1029                    ClassPoolInfo cpi = new ClassPoolInfo(simpleTypeNamePoolItem, cf.getConstantPool());
1030                    l = cf.addConstantPoolItems(new APoolInfo[]{cpi});
1031                    cpi = cf.getConstantPoolItem(l[0]).execute(CheckClassVisitor.singleton(), null);
1032    
1033                    // add a new name-and-type item
1034                    NameAndTypePoolInfo natpi = new NameAndTypePoolInfo(valueNamePoolItem, typeNamePoolItem, cf.getConstantPool());
1035                    l = cf.addConstantPoolItems(new APoolInfo[]{natpi});
1036                    natpi = cf.getConstantPoolItem(l[0]).execute(CheckNameAndTypeVisitor.singleton(), null);
1037    
1038                    // add a new field item
1039                    FieldPoolInfo fpi = new FieldPoolInfo(cpi, natpi, cf.getConstantPool());
1040                    l = cf.addConstantPoolItems(new APoolInfo[]{fpi});
1041                    fpi = cf.getConstantPoolItem(l[0]).execute(new ADefaultPoolInfoVisitor<FieldPoolInfo, Object>() {
1042                        public FieldPoolInfo defaultCase(APoolInfo host, Object param) {
1043                            throw new ClassFormatError("Info is of type " + host.getClass().getName() + ", needs to be FieldPoolInfo");
1044                        }
1045                        public FieldPoolInfo fieldCase(FieldPoolInfo host, Object param) {
1046                            return host;
1047                        }
1048                    }, null);
1049    
1050                    if (il!=null) {
1051                        // now load using getstatic
1052                        ReferenceInstruction getstatic = new ReferenceInstruction(Opcode.GETSTATIC, l[0]);
1053                        il.insertInstr(getstatic, mi.getCodeAttributeInfo());
1054                        boolean res = il.advanceIndex();
1055                        assert res == true;
1056                        return 1;
1057                    }
1058                    else {
1059                        return 0;
1060                    }
1061                }
1062                public Integer classMemberCase(AAnnotationsAttributeInfo.Annotation.ClassMemberValue host, Object o) {
1063                    // add the class name to the constant pool, without the 'L' and the ';' at the end,
1064                    // as required by a class item
1065                    String classTypeName = host.getClassName().toString();
1066                    AUTFPoolInfo typeNamePoolItem = new ASCIIPoolInfo(classTypeName.substring(1, classTypeName.length()-1), cf.getConstantPool());
1067                    int[] l = cf.addConstantPoolItems(new APoolInfo[]{typeNamePoolItem});
1068                    typeNamePoolItem = cf.getConstantPoolItem(l[0]).execute(CheckUTFVisitor.singleton(), null);
1069    
1070                    // add a new class item
1071                    ClassPoolInfo cpi = new ClassPoolInfo(typeNamePoolItem, cf.getConstantPool());
1072                    l = cf.addConstantPoolItems(new APoolInfo[]{cpi});
1073                    cpi = cf.getConstantPoolItem(l[0]).execute(CheckClassVisitor.singleton(), null);
1074    
1075                    if (il!=null) {
1076                        // now load using ldc_w
1077                        ReferenceInstruction ldc_w = new ReferenceInstruction(Opcode.LDC_W, l[0]);
1078                        il.insertInstr(ldc_w, mi.getCodeAttributeInfo());
1079                        boolean res = il.advanceIndex();
1080                        assert res == true;
1081                        return 1;
1082                    }
1083                    else {
1084                        return 0;
1085                    }
1086                }
1087                public Integer annotationMemberCase(AAnnotationsAttributeInfo.Annotation.AnnotationMemberValue host, Object o) {
1088                    transferAnnotation(host.getAnnotation(), cf, mi);
1089                    // annotation members are not arguments of the predicate method
1090                    return 0;
1091                }
1092                public Integer arrayMemberCase(AAnnotationsAttributeInfo.Annotation.ArrayMemberValue host, Object o) {
1093                    String elementType = null;
1094                    if (paramType!=null) {
1095                        // cut off the leading '['
1096                        elementType = paramType.substring(1);
1097                    }
1098    
1099                    if (il!=null) {
1100                        if (elementType==null) {
1101                            // we don't have the element type for the array, so we cannot create it
1102                            // load null and issue warning
1103                            _sharedData.addBadPredicateAnnotWarning(new BadPredicateAnnotationWarning(
1104                                "Element type unknown for array for predicate in method "+
1105                                mi.getName().toString()+" in "+cf.getThisClassName()+
1106                                " (processing " + _sharedData.getCurrentClassName() + ")"));
1107                            il.insertInstr(new GenericInstruction(Opcode.ACONST_NULL), mi.getCodeAttributeInfo());
1108                            boolean res = il.advanceIndex();
1109                            assert res == true;
1110    
1111                            return 1;
1112                        }
1113    
1114                        if (elementType.startsWith("L") && elementType.endsWith(";")) {
1115                            // add a new name for the element type
1116                            AUTFPoolInfo eltClassName = new ASCIIPoolInfo(elementType.substring(1, elementType.length()-1), cf.getConstantPool());
1117                            int[] l = cf.addConstantPoolItems(new APoolInfo[]{eltClassName});
1118                            eltClassName = cf.getConstantPoolItem(l[0]).execute(CheckUTFVisitor.singleton(), null);
1119    
1120                            // add a new class item for the element class
1121                            ClassPoolInfo eltClass = new ClassPoolInfo(eltClassName, cf.getConstantPool());
1122                            l = cf.addConstantPoolItems(new APoolInfo[]{eltClass});
1123                            eltClass = cf.getConstantPoolItem(l[0]).execute(CheckClassVisitor.singleton(), null);
1124    
1125                            ReferenceInstruction anewarray = new ReferenceInstruction(Opcode.ANEWARRAY, cf.getConstantPool().indexOf(eltClass));
1126    
1127                            // add number of elements to class pool
1128                            IntegerPoolInfo count = new IntegerPoolInfo(host.getEntries().length, cf.getConstantPool());
1129                            l = cf.addConstantPoolItems(new APoolInfo[]{count});
1130                            count = cf.getConstantPoolItem(l[0]).execute(new ADefaultPoolInfoVisitor<IntegerPoolInfo, Object>() {
1131                                public IntegerPoolInfo defaultCase(APoolInfo host, Object param) {
1132                                    throw new ClassFormatError("Info is of type " + host.getClass().getName() + ", needs to be IntegerPoolInfo");
1133                                }
1134                                public IntegerPoolInfo intCase(IntegerPoolInfo host, Object param) {
1135                                    return host;
1136                                }
1137                            }, null);
1138    
1139                            // load number of arguments onto stack
1140                            il.insertInstr(new ReferenceInstruction(Opcode.LDC_W, cf.getConstantPool().indexOf(count)), mi.getCodeAttributeInfo());
1141                            boolean res = il.advanceIndex();
1142                            assert res == true;
1143    
1144                            // create an array of objects of that size
1145                            il.insertInstr(anewarray, mi.getCodeAttributeInfo());
1146                            res = il.advanceIndex();
1147                            assert res == true;
1148    
1149                            // top of stack: array-ref
1150    
1151                            int index = 0;
1152                            int maxStack = 0;
1153                            byte[] bytes = new byte[] {Opcode.SIPUSH, 0, 0};
1154                            for(AAnnotationsAttributeInfo.Annotation.AMemberValue value: host.getEntries()) {
1155                                // duplicate array-ref to store
1156                                il.insertInstr(new GenericInstruction(Opcode.DUP), mi.getCodeAttributeInfo());
1157                                res = il.advanceIndex();
1158                                assert res == true;
1159    
1160                                // top of stack: array-ref array-ref
1161    
1162                                // push index as short
1163                                Types.bytesFromShort((short)index, bytes, 1);
1164                                il.insertInstr(new GenericInstruction(bytes), mi.getCodeAttributeInfo());
1165                                res = il.advanceIndex();
1166                                assert res == true;
1167    
1168                                // top of stack: array-ref array-ref index
1169    
1170                                // load element
1171                                int curStack = loadAndTransferMemberValue(value, elementType, cf, mi, il);
1172    
1173                                // top of stack: array-ref array-ref index value
1174    
1175                                // store value
1176                                il.insertInstr(new GenericInstruction(Opcode.AASTORE), mi.getCodeAttributeInfo());
1177                                res = il.advanceIndex();
1178                                assert res == true;
1179    
1180                                // top of stack: array-ref
1181    
1182                                if (curStack>maxStack) {
1183                                    maxStack = curStack;
1184                                }
1185                                ++index;
1186                            }
1187    
1188                            return 3+maxStack;
1189                        }
1190    
1191                        if (elementType.length()==1) {
1192                            // primitive type
1193                            char ch = elementType.charAt(0);
1194                            byte atype = 0;
1195                            GenericInstruction astore = null;
1196                            switch(ch) {
1197                                case 'B':
1198                                    atype = 8;
1199                                    astore = new GenericInstruction(Opcode.BASTORE);
1200                                    break;
1201                                case 'C':
1202                                    atype = 5;
1203                                    astore = new GenericInstruction(Opcode.CASTORE);
1204                                    break;
1205                                case 'D':
1206                                    atype = 7;
1207                                    astore = new GenericInstruction(Opcode.DASTORE);
1208                                    break;
1209                                case 'F':
1210                                    atype = 6;
1211                                    astore = new GenericInstruction(Opcode.FASTORE);
1212                                    break;
1213                                case 'I':
1214                                    atype = 10;
1215                                    astore = new GenericInstruction(Opcode.IASTORE);
1216                                    break;
1217                                case 'J':
1218                                    atype = 11;
1219                                    astore = new GenericInstruction(Opcode.LASTORE);
1220                                    break;
1221                                case 'S':
1222                                    atype = 9;
1223                                    astore = new GenericInstruction(Opcode.SASTORE);
1224                                    break;
1225                                case 'Z':
1226                                    atype = 4;
1227                                    astore = new GenericInstruction(Opcode.BASTORE);
1228                                    break;
1229                            }
1230                            if (atype!=0) {
1231                                // type was recognized
1232                                GenericInstruction newarray = new GenericInstruction(Opcode.NEWARRAY,atype);
1233    
1234                                // add number of elements to class pool
1235                                IntegerPoolInfo count = new IntegerPoolInfo(host.getEntries().length, cf.getConstantPool());
1236                                int[] l = cf.addConstantPoolItems(new APoolInfo[]{count});
1237                                count = cf.getConstantPoolItem(l[0]).execute(new ADefaultPoolInfoVisitor<IntegerPoolInfo, Object>() {
1238                                    public IntegerPoolInfo defaultCase(APoolInfo host, Object param) {
1239                                        throw new ClassFormatError("Info is of type " + host.getClass().getName() + ", needs to be IntegerPoolInfo");
1240                                    }
1241                                    public IntegerPoolInfo intCase(IntegerPoolInfo host, Object param) {
1242                                        return host;
1243                                    }
1244                                }, null);
1245    
1246                                // load number of arguments onto stack
1247                                il.insertInstr(new ReferenceInstruction(Opcode.LDC_W, cf.getConstantPool().indexOf(count)), mi.getCodeAttributeInfo());
1248                                boolean res = il.advanceIndex();
1249                                assert res == true;
1250    
1251                                // create an array of primitives of that size
1252                                il.insertInstr(newarray, mi.getCodeAttributeInfo());
1253                                res = il.advanceIndex();
1254                                assert res == true;
1255    
1256                                // top of stack: array-ref
1257    
1258                                int index = 0;
1259                                int maxStack = 0;
1260                                byte[] bytes = new byte[] {Opcode.SIPUSH, 0, 0};
1261                                for(AAnnotationsAttributeInfo.Annotation.AMemberValue value: host.getEntries()) {
1262                                    // duplicate array-ref to store
1263                                    il.insertInstr(new GenericInstruction(Opcode.DUP), mi.getCodeAttributeInfo());
1264                                    res = il.advanceIndex();
1265                                    assert res == true;
1266    
1267                                    // top of stack: array-ref array-ref
1268    
1269                                    // push index as short
1270                                    Types.bytesFromShort((short)index, bytes, 1);
1271                                    il.insertInstr(new GenericInstruction(bytes), mi.getCodeAttributeInfo());
1272                                    res = il.advanceIndex();
1273                                    assert res == true;
1274    
1275                                    // top of stack: array-ref array-ref index
1276    
1277                                    // load element
1278                                    int curStack = loadAndTransferMemberValue(value, elementType, cf, mi, il);
1279    
1280                                    // top of stack: array-ref array-ref index value
1281    
1282                                    // store value
1283                                    il.insertInstr(astore, mi.getCodeAttributeInfo());
1284                                    res = il.advanceIndex();
1285                                    assert res == true;
1286    
1287                                    // top of stack: array-ref
1288    
1289                                    if (curStack>maxStack) {
1290                                        maxStack = curStack;
1291                                    }
1292                                    ++index;
1293                                }
1294    
1295                                return 3+maxStack;
1296                            }
1297                        }
1298    
1299                        // we don't understand what kind of array this is
1300                        // load null and issue warning
1301                        _sharedData.addBadPredicateAnnotWarning(new BadPredicateAnnotationWarning(
1302                            "Element type "+elementType+" not recognized for array for predicate in method "+
1303                            mi.getName().toString()+" in "+cf.getThisClassName()+
1304                            " (processing " + _sharedData.getCurrentClassName() + ")"));
1305                        il.insertInstr(new GenericInstruction(Opcode.ACONST_NULL), mi.getCodeAttributeInfo());
1306                        boolean res = il.advanceIndex();
1307                        assert res == true;
1308    
1309                        return 1;
1310                    }
1311                    else {
1312                        return 0;
1313                    }
1314                }
1315            }, null);
1316        }
1317    
1318        /**
1319         * Transfer the annotation to the class file.
1320         * @param a annotation
1321         * @param cf target class file
1322         * @param mi method info
1323         */
1324        protected void transferAnnotation(AAnnotationsAttributeInfo.Annotation a, final ClassFile cf, final MethodInfo mi) {
1325            int[] l;// add the annotation name to the constant pool
1326            AUTFPoolInfo annPoolItem = new ASCIIPoolInfo(a.getType(), cf.getConstantPool());
1327            l = cf.addConstantPoolItems(new APoolInfo[]{annPoolItem});
1328            annPoolItem = cf.getConstantPoolItem(l[0]).execute(CheckUTFVisitor.singleton(), null);
1329    
1330            for(AAnnotationsAttributeInfo.Annotation.NameValuePair nvp: a.getPairs()) {
1331                // add the pair name to the constant pool
1332                AUTFPoolInfo pairNamePoolItem = new ASCIIPoolInfo(nvp.getName().toString(), cf.getConstantPool());
1333                l = cf.addConstantPoolItems(new APoolInfo[]{pairNamePoolItem});
1334                pairNamePoolItem = cf.getConstantPoolItem(l[0]).execute(CheckUTFVisitor.singleton(), null);
1335    
1336                loadAndTransferMemberValue(nvp.getValue(), null, cf, mi, null);
1337            }
1338        }
1339    
1340        /**
1341         * Return a generated predicate annotation record for a @Combine-type annotation.
1342         * @param cf current class file
1343         * @param par predicate annotation record describing the @Combine-type annotation
1344         * @param miDescriptor method destrictor
1345         * @return generated predicate annotation record that can be used like that of a @PredicateLink-type annotation
1346         */
1347        protected PredicateAnnotationRecord getGeneratedPredicate(ClassFile cf, PredicateAnnotationRecord par,
1348                                                                  String miDescriptor) {
1349            final PredicateAnnotationRecord gr = generatePredicateAnnotationRecord(par, miDescriptor);
1350    
1351            // fill in valueList
1352            gr.valueList = new ArrayList<AAnnotationsAttributeInfo.Annotation.AMemberValue>();
1353            performCombineTreeWalk(par, new ILambda.Ternary<Object, String, String, AAnnotationsAttributeInfo.Annotation.AMemberValue>() {
1354                public Object apply(String param1, String param2, AAnnotationsAttributeInfo.Annotation.AMemberValue param3) {
1355                    // append the value
1356                    gr.valueList.add(param3);
1357                    return null;
1358                }
1359            }, "");
1360    
1361            return gr;
1362        }
1363    
1364        /**
1365         * Generate the predicate annotation record for the @Combine-type annotation that contains all information
1366         * except for the valueList.
1367         * @param par predicate annotation record describing the @Combine-type annotation
1368         * @param miDescriptor method destrictor
1369         * @return predicate annotation record without valueList
1370         */
1371        protected PredicateAnnotationRecord generatePredicateAnnotationRecord(PredicateAnnotationRecord par,
1372                                                                              String miDescriptor) {
1373            // create hash key based on annotation class name and predicate method name, which is dependent on
1374            // annotation array sizes
1375            String annotClass = par.annotation.getType().substring(1, par.annotation.getType().length()-1).replace('/','.');
1376            String methodName = getMethodName(par);
1377            String hashKey = annotClass + CLASS_SIG_SEPARATOR_STRING + methodName;
1378            // System.out.println("\tHash key = "+hashKey);
1379    
1380            PredicateAnnotationRecord gr = _generatedPredicateRecords.get(hashKey);
1381            if (gr!=null) {
1382                _sharedAddData.cacheInfo.incCombinePredicateCacheHit();
1383                return gr;
1384            }
1385            else {
1386                _sharedAddData.cacheInfo.incCombinePredicateCacheMiss();
1387            }
1388    
1389            // not in cache, check class file and perhaps generate new
1390            String predicateClass = ((_predicatePackage.length()>0)?(_predicatePackage + "."):"") + annotClass + "Pred";
1391    
1392            // open predicate class if found, otherwise use template predicate class
1393            ClassFile predicateCF = null;
1394            File clonedFile = new File(_predicatePackageDir, annotClass.replace('.','/')+"Pred.class");
1395            if (clonedFile.exists() && clonedFile.isFile() && clonedFile.canRead()) {
1396                try {
1397                    predicateCF = new ClassFile(new FileInputStream(clonedFile));
1398                }
1399                catch(IOException ioe) {
1400                    throw new ThreadCheckException("Could not open predicate class file, source="+clonedFile, ioe);
1401                }
1402            }
1403            else {
1404                ByteArrayOutputStream baos = new ByteArrayOutputStream();
1405                try {
1406                    _templatePredicateClassFile.write(baos);
1407                    predicateCF = new ClassFile(new ByteArrayInputStream(baos.toByteArray()));
1408                }
1409                catch(IOException ioe) {
1410                    throw new ThreadCheckException("Could not open predicate template class file", ioe);
1411                }
1412            }
1413            // System.out.println("\t-->"+clonedFile);
1414            clonedFile.getParentFile().mkdirs();
1415    
1416            // determine parameter names and types
1417            final ArrayList<String> paramNames = new ArrayList<String>();
1418            final HashMap<String,String> paramTypes = new HashMap<String,String>();
1419    
1420            performCombineTreeWalk(par, new ILambda.Ternary<Object, String, String, AAnnotationsAttributeInfo.Annotation.AMemberValue>() {
1421                public Object apply(String param1, String param2, AAnnotationsAttributeInfo.Annotation.AMemberValue param3) {
1422                    // append the value
1423                    paramNames.add(param1);
1424                    paramTypes.put(param1,param2);
1425                    return null;
1426                }
1427            }, "");
1428    
1429            // collect all members and generate predicates for all members that are @Combine-type annotations
1430            ArrayList<PredicateAnnotationRecord> memberPARs = new ArrayList<PredicateAnnotationRecord>();
1431            for (String key: par.combinedPredicates.keySet()) {
1432                for (PredicateAnnotationRecord memberPAR: par.combinedPredicates.get(key)) {
1433                    if ((memberPAR.predicateClass !=null) && (memberPAR.predicateMI!=null)) {
1434                        // this is a @PredicateLink-type annotation and ready to use
1435                        memberPARs.add(memberPAR);
1436                    }
1437                    else {
1438                        // this member annotation is a @Combine-type annotation itself
1439                        // so we must get the generated predicate annotation record for it, which may recursively generate
1440                        // additional predicate class files
1441                        memberPARs.add(generatePredicateAnnotationRecord(memberPAR, miDescriptor));
1442                    }
1443                }
1444            }
1445            
1446            // modify the package and class name
1447    
1448            // add the type name to the constant pool
1449            AUTFPoolInfo predicateClassNameItem = new ASCIIPoolInfo(predicateClass.replace('.','/'), predicateCF.getConstantPool());
1450            int[] l = predicateCF.addConstantPoolItems(new APoolInfo[]{predicateClassNameItem});
1451            predicateClassNameItem = predicateCF.getConstantPoolItem(l[0]).execute(CheckUTFVisitor.singleton(), null);
1452    
1453            // add a new class item
1454            ClassPoolInfo predicateClassItem = new ClassPoolInfo(predicateClassNameItem, predicateCF.getConstantPool());
1455            l = predicateCF.addConstantPoolItems(new APoolInfo[]{predicateClassItem});
1456            predicateClassItem = predicateCF.getConstantPoolItem(l[0]).execute(CheckClassVisitor.singleton(), null);
1457            predicateCF.setThisClass(predicateClassItem);
1458    
1459            // generate the new descriptor
1460            StringBuilder sb = new StringBuilder();
1461            sb.append("(Ljava/lang/Object;");
1462            if (par.passArguments) { sb.append("[Ljava/lang/Object;"); }
1463            for(String key: paramNames) {
1464                sb.append(paramTypes.get(key));
1465            }
1466            sb.append(")Z");
1467            String methodDesc = sb.toString();
1468    
1469            // find the predicate method (if it already exists) and the template predicate method (if it doesn't)
1470            MethodInfo templateMI = null;
1471            MethodInfo predicateMI = null;
1472            for (MethodInfo mi: predicateCF.getMethods()) {
1473                if ((mi.getName().toString().equals(methodName)) &&
1474                    (mi.getDescriptor().toString().equals(methodDesc))) {
1475                    predicateMI = mi;
1476                    break;
1477                }
1478                else if ((mi.getName().toString().equals("template")) &&
1479                    (mi.getDescriptor().toString().startsWith("(")) &&
1480                    (mi.getDescriptor().toString().endsWith(")Z"))) {
1481                    templateMI = mi;
1482                }
1483            }
1484            if ((templateMI==null) && (predicateMI==null)) {
1485                throw new ThreadCheckException("Could not find template predicate method in class file");
1486            }
1487    
1488            // if a predicate method has not already been found, generate it
1489            if (predicateMI==null) {
1490                // add the new method name
1491                AUTFPoolInfo namecpi = new ASCIIPoolInfo(methodName, predicateCF.getConstantPool());
1492                l = predicateCF.addConstantPoolItems(new APoolInfo[]{namecpi});
1493                namecpi = predicateCF.getConstantPoolItem(l[0]).execute(CheckUTFVisitor.singleton(), null);
1494    
1495                // add the new descriptor to the constant pool
1496                AUTFPoolInfo descpi = new ASCIIPoolInfo(methodDesc, predicateCF.getConstantPool());
1497                l = predicateCF.addConstantPoolItems(new APoolInfo[]{descpi});
1498                descpi = predicateCF.getConstantPoolItem(l[0]).execute(CheckUTFVisitor.singleton(), null);
1499    
1500                // clone the method info
1501                ArrayList<AAttributeInfo> list = new ArrayList<AAttributeInfo>();
1502                for(AAttributeInfo a : templateMI.getAttributes()) {
1503                    try {
1504                        AAttributeInfo clonedA = (AAttributeInfo)a.clone();
1505                        list.add(clonedA);
1506                    }
1507                    catch(CloneNotSupportedException e) {
1508                        throw new InstrumentorException("Could not clone method attributes");
1509                    }
1510                }
1511    
1512                predicateMI = new MethodInfo(templateMI.getAccessFlags(),
1513                                             namecpi,
1514                                             descpi,
1515                                             list.toArray(new AAttributeInfo[]{}));
1516                predicateCF.getMethods().add(predicateMI);
1517    
1518                // change number of locals to fit parameters
1519                CodeAttributeInfo.CodeProperties props = predicateMI.getCodeAttributeInfo().getProperties();
1520                // make room for each parameter in paramTypes; the first parameter, Object thisO, is already in the template
1521                // so we don't have to add 1 for this parameter.
1522                // however, we do add 1 for a boolean local variable that serves as accumulator for the predicate results
1523                props.maxLocals += paramTypes.size() + 1 + (par.passArguments?1:0);
1524    
1525                // generate code to put values on the stack, call the member predicate methods, and then combine their results
1526                InstructionList il = new InstructionList(predicateMI.getCodeAttributeInfo().getCode());
1527    
1528                // depending on the mode, we have to initialize the accumulator to true (AND, NOT) or false (OR, IMPLIES, or 0 for XOR)
1529                if ((par.combineMode==Combine.Mode.OR) ||
1530                    (par.combineMode==Combine.Mode.XOR) ||
1531                    (par.combineMode==Combine.Mode.IMPLIES)) {
1532                    // put false on the stack
1533                    il.insertInstr(new GenericInstruction(Opcode.ICONST_0), predicateMI.getCodeAttributeInfo());
1534                }
1535                else {
1536                    // put true on the stack
1537                    il.insertInstr(new GenericInstruction(Opcode.ICONST_1), predicateMI.getCodeAttributeInfo());
1538                }
1539                boolean res;
1540                res = il.advanceIndex();
1541                assert res == true;
1542    
1543                int accumVarIndex = props.maxLocals - 1; // index of the accumulator for ILOAD and ISTORE
1544                AInstruction loadAccumInstr;
1545                AInstruction storeAccumInstr;
1546                if (accumVarIndex<256) {
1547                    loadAccumInstr = new GenericInstruction(Opcode.ILOAD, (byte)accumVarIndex);
1548                    storeAccumInstr = new GenericInstruction(Opcode.ISTORE, (byte)accumVarIndex);
1549                }
1550                else {
1551                    byte[] bytes = new byte[] { Opcode.ILOAD, 0, 0 };
1552                    Types.bytesFromShort((short)accumVarIndex, bytes, 1);
1553                    loadAccumInstr = new WideInstruction(bytes);
1554                    bytes[0] = Opcode.ISTORE;
1555                    storeAccumInstr = new WideInstruction(bytes);
1556                }
1557                il.insertInstr(storeAccumInstr, predicateMI.getCodeAttributeInfo());
1558                res = il.advanceIndex();
1559                assert res == true;
1560    
1561                int maxStack = 0;
1562                 // parameter index of the argument for the next member predicate method; 0 is Object thisO
1563                int paramIndex = 1;
1564                // local variable index of the argument for the next member predicate method; 0 is Object thisO
1565                // lvIndex and paramIndex are different, lvIndex >= paramIndex, because long and double require 2 local variables
1566                int lvIndex = 1;
1567                // if we are passing arguments, start with a later local variable
1568                if (par.passArguments) { lvIndex += 1; }
1569                int memberCount = 0;
1570                for (PredicateAnnotationRecord memberPAR: memberPARs) {
1571                    ++memberCount;
1572    
1573                    // on stack: nothing
1574    
1575                    // first put Object thisO on the stack
1576                    // if the method is static, then this happens to be null already, so no special treatment is required
1577                    il.insertInstr(new GenericInstruction(Opcode.ALOAD_0), predicateMI.getCodeAttributeInfo());
1578                    res = il.advanceIndex();
1579                    assert res == true;
1580                    int curStack = 1;
1581    
1582                    // on stack: L(thisO)
1583    
1584                    // if passing arguments, put them on stack now
1585                    if (memberPAR.passArguments) {
1586                        if (par.passArguments) {
1587                            // the array has already been created for us, it's the second argument
1588                            // so just load it
1589                            il.insertInstr(new GenericInstruction(Opcode.ALOAD_1), predicateMI.getCodeAttributeInfo());
1590                            res = il.advanceIndex();
1591                            assert res == true;
1592                            curStack+=1;
1593                        }
1594                    }
1595    
1596                    // on stack: L(thisO) {method arguments}
1597    
1598                    // if this member annotation wants arguments passed, start with a later value
1599                    for (int paramNameIndex=0; paramNameIndex<memberPAR.paramNames.size(); ++paramNameIndex) {
1600                        String t = memberPAR.paramTypes.get(memberPAR.paramNames.get(paramNameIndex));
1601                        if (t.length()==0) {
1602                            throw new ThreadCheckException("Length of parameter type no. "+paramIndex+" string is 0 in "+
1603                                                           predicateMI.getName()+" in class "+predicateCF.getThisClassName());
1604                        }
1605                        byte opcode;
1606                        int  nextLVIndex = lvIndex;
1607                        switch(t.charAt(0)) {
1608                            case 'I':
1609                            case 'B':
1610                            case 'C':
1611                            case 'S':
1612                            case 'Z':
1613                                // put int on stack
1614                                opcode = Opcode.ILOAD;
1615                                nextLVIndex+=1;
1616                                curStack+=1;
1617                                break;
1618                            case 'F':
1619                                // put float on stack
1620                                opcode = Opcode.FLOAD;
1621                                nextLVIndex+=1;
1622                                curStack+=1;
1623                                break;
1624                            case '[':
1625                            case 'L':
1626                                // put reference on stack
1627                                opcode = Opcode.ALOAD;
1628                                nextLVIndex+=1;
1629                                curStack+=1;
1630                                break;
1631                            case 'J':
1632                                // put long on stack
1633                                opcode = Opcode.LLOAD;
1634                                nextLVIndex+=2;
1635                                curStack+=2;
1636                                break;
1637                            case 'D':
1638                                // put double on stack
1639                                opcode = Opcode.DLOAD;
1640                                nextLVIndex+=2;
1641                                curStack+=2;
1642                                break;
1643                            default:
1644                                throw new ThreadCheckException("Parameter type no. "+paramIndex+", "+t+", is unknown in "+
1645                                                               predicateMI.getName()+" in class "+predicateCF.getThisClassName());
1646                        }
1647    
1648                        AInstruction load = Opcode.getShortestLoadStoreInstruction(opcode, (short)lvIndex);
1649                        il.insertInstr(load, predicateMI.getCodeAttributeInfo());
1650                        res = il.advanceIndex();
1651                        assert res == true;
1652    
1653                        ++paramIndex;
1654                        lvIndex = nextLVIndex;
1655                    }
1656    
1657                    if (curStack>maxStack) { maxStack = curStack; }
1658    
1659                    // on stack: L(this0) {method arguments} param1 param2 ... paramN
1660    
1661                    // call member predicate method
1662                    ReferenceInstruction predicateCallInstr = new ReferenceInstruction(Opcode.INVOKESTATIC, (short)0);
1663                    int predicateCallIndex = predicateCF.addMethodToConstantPool(memberPAR.predicateClass.replace('.','/'),
1664                                                                                 memberPAR.predicateMI.getName().toString(),
1665                                                                                 memberPAR.predicateMI.getDescriptor().toString());
1666                    predicateCallInstr.setReference(predicateCallIndex);
1667                    il.insertInstr(predicateCallInstr, predicateMI.getCodeAttributeInfo());
1668                    res = il.advanceIndex();
1669                    assert res == true;
1670    
1671                    // on stack: Z(member predicate result)
1672    
1673                    // if mode is NOT, negate result
1674                    // if mode is IMPLIES, and this is the first operand, negate result:
1675                    // (NOT A) OR B
1676                    if ((par.combineMode==Combine.Mode.NOT)||
1677                        ((par.combineMode==Combine.Mode.IMPLIES)&&(memberCount==1))) {
1678                        // inserts these operations:
1679                        // iconst_1
1680                        // swap
1681                        // isub
1682    
1683                        // insert iconst_1
1684                        il.insertInstr(new GenericInstruction(Opcode.ICONST_1), predicateMI.getCodeAttributeInfo());
1685                        res = il.advanceIndex();
1686                        assert res == true;
1687    
1688                        // on stack: Z(member predicate result) I(1)
1689    
1690                        // insert swap
1691                        il.insertInstr(new GenericInstruction(Opcode.SWAP), predicateMI.getCodeAttributeInfo());
1692                        res = il.advanceIndex();
1693                        assert res == true;
1694    
1695                        // on stack: I(1) Z(member predicate result)
1696    
1697                        // insert isub
1698                        il.insertInstr(new GenericInstruction(Opcode.ISUB), predicateMI.getCodeAttributeInfo());
1699                        res = il.advanceIndex();
1700                        assert res == true;
1701    
1702                        // on stack: Z(1-member predicate result)
1703                    }
1704    
1705                    // on stack: Z(member predicate result)
1706    
1707                    // load accumulator
1708                    il.insertInstr(loadAccumInstr, predicateMI.getCodeAttributeInfo());
1709                    res = il.advanceIndex();
1710                    assert res == true;
1711    
1712                    // on stack: Z(member predicate result) Z(accumulator)
1713    
1714                    // combine result with accumulator
1715                    if (par.combineMode==Combine.Mode.OR) {
1716                        // combine with ior
1717                        il.insertInstr(new GenericInstruction(Opcode.IOR), predicateMI.getCodeAttributeInfo());
1718                    }
1719                    else if ((par.combineMode==Combine.Mode.AND)||
1720                             (par.combineMode==Combine.Mode.NOT)) {
1721                        // combine with iand
1722                        il.insertInstr(new GenericInstruction(Opcode.IAND), predicateMI.getCodeAttributeInfo());
1723                    }
1724                    else if (par.combineMode==Combine.Mode.XOR) {
1725                        // combine with iadd for XOR
1726                        il.insertInstr(new GenericInstruction(Opcode.IADD), predicateMI.getCodeAttributeInfo());
1727                    }
1728                    else if (par.combineMode==Combine.Mode.IMPLIES) {
1729                        // if mode is IMPLIES, combine with accumulator using ior:
1730                        // (NOT A) OR B
1731                        il.insertInstr(new GenericInstruction(Opcode.IOR), predicateMI.getCodeAttributeInfo());
1732                    }
1733                    else {
1734                        // this should never happen; we have exhausted all options above
1735                        assert false;
1736                    }
1737                    res = il.advanceIndex();
1738                    assert res == true;
1739    
1740                    // on stack: Z(updated accumulator)
1741    
1742                    // store accumulator
1743                    il.insertInstr(storeAccumInstr, predicateMI.getCodeAttributeInfo());
1744                    res = il.advanceIndex();
1745                    assert res == true;
1746    
1747                    // on stack: nothing
1748                }
1749    
1750                // if we did XOR, then it should be true if = 1, false otherwise
1751                if (par.combineMode==Combine.Mode.XOR) {
1752                    // load the final value in the accumulator so it can be returned
1753                    il.insertInstr(loadAccumInstr, predicateMI.getCodeAttributeInfo());
1754                    res = il.advanceIndex();
1755                    assert res == true;
1756    
1757                    il.insertInstr(new GenericInstruction(Opcode.ICONST_1), predicateMI.getCodeAttributeInfo());
1758                    res = il.advanceIndex();
1759                    assert res == true;
1760    
1761                    // this is where we will insert the IF_ICMPEQ instruction
1762    
1763                    il.insertInstr(new GenericInstruction(Opcode.ICONST_0), predicateMI.getCodeAttributeInfo());
1764                    res = il.advanceIndex();
1765                    assert res == true;
1766    
1767                    WideBranchInstruction br2 = new WideBranchInstruction(Opcode.GOTO_W, il.getIndex()+1);
1768                    il.insertInstr(br2, predicateMI.getCodeAttributeInfo());
1769                    res = il.advanceIndex();
1770                    assert res == true;
1771    
1772                    int jumpIndex = il.getIndex();
1773                    il.insertInstr(new GenericInstruction(Opcode.ICONST_1), predicateMI.getCodeAttributeInfo());
1774                    res = il.advanceIndex();
1775                    assert res == true;
1776    
1777                    res = il.rewindIndex(3);
1778                    assert res == true;
1779                    BranchInstruction br1 = new BranchInstruction(Opcode.IF_ICMPEQ, jumpIndex);
1780                    il.insertInstr(br1, predicateMI.getCodeAttributeInfo());
1781                    res = il.advanceIndex(4);
1782                    assert res == true;
1783                }
1784                else {
1785                    // load the final value in the accumulator so it can be returned
1786                    il.insertInstr(loadAccumInstr, predicateMI.getCodeAttributeInfo());
1787                    res = il.advanceIndex();
1788                    assert res == true;
1789                }
1790    
1791                // remove the iconst_0 instruction that is in the template
1792                il.deleteInstr(predicateMI.getCodeAttributeInfo());
1793    
1794                // write code
1795                predicateMI.getCodeAttributeInfo().setCode(il.getCode());
1796    
1797                // set stack and local variable sizes
1798                props.maxStack = Math.max(maxStack, 2); // we need at least a stack size of 2
1799                predicateMI.getCodeAttributeInfo().setProperties(props.maxStack, props.maxLocals);
1800    
1801                // write cloned predicate class file
1802                try {
1803                    FileOutputStream fos = new FileOutputStream(clonedFile);
1804                    predicateCF.write(fos);
1805                    fos.close();
1806                }
1807                catch(IOException e) {
1808                    throw new ThreadCheckException("Could not write cloned predicate class file, target="+clonedFile);
1809                }
1810            }
1811    
1812            // create the record for the generated predicate
1813            gr = new PredicateAnnotationRecord(par.annotation,
1814                                               predicateClass,
1815                                               predicateMI,
1816                                               paramNames,
1817                                               paramTypes,
1818                                               new ArrayList<AAnnotationsAttributeInfo.Annotation.AMemberValue>(),
1819                                               par.passArguments,
1820                                               null,
1821                                               new HashMap<String,ArrayList<PredicateAnnotationRecord>>());
1822    
1823            _generatedPredicateRecords.put(hashKey, gr);
1824    
1825            return gr;
1826        }
1827    
1828        /**
1829         * Walk the tree of combined predicates and apply the lambda for each parameter name-parameter type-value triple.
1830         * @param par record describing the combined predicate annotation
1831         * @param lambda lambda to apply to each parameter name-parameter type-value triple; return value is ignored
1832         * @param suffix string suffix for variable names to distinguish them
1833         */
1834        protected void performCombineTreeWalk(PredicateAnnotationRecord par,
1835                                              ILambda.Ternary<Object, String, String, AAnnotationsAttributeInfo.Annotation.AMemberValue> lambda,
1836                                              String suffix) {
1837            if (par.combineMode==null) {
1838                // @PredicateLink-type annotation, process parameters and values
1839                int startIndex = 0;
1840                for(int i=startIndex; i<par.paramNames.size(); ++i) {
1841                    String name = par.paramNames.get(i);
1842                    lambda.apply(name + suffix,
1843                                 par.paramTypes.get(name),
1844                                 par.valueList.get(i));
1845                }
1846            }
1847            else {
1848                // @Combine-type annotation, recurse
1849                for(String key: par.combinedPredicates.keySet()) {
1850                    int i = 0;
1851                    for(PredicateAnnotationRecord memberPAR: par.combinedPredicates.get(key)) {
1852                        performCombineTreeWalk(memberPAR, lambda, "$"+key+"$"+i+suffix);
1853                        ++i;
1854                    }
1855                }
1856            }
1857        }
1858    
1859        /**
1860         * Return the name of the predicate method for a @Combine-type annotation, taking annotation array sizes
1861         * into account. The method will start with the method name "check", perform a pre-order tree traversal, and
1862         * for every annotation array found, it will append a string of the format "$value$member$$1$id$$$4", where
1863         * "$value$$0$member$$1$id" represents the path of @Combine-type annotation members that have lead to this
1864         * annotation array, with the last element being the name of the annotation array itself, and the number
1865         * after the "$$$" represents the size of the array. If an annotation member on the path is an array, then
1866         * the index of the array element chosen is represented after the array member name and "$$".
1867         * In the example above, "$value$member$$1$id$$$4", the array "id" has size 4. It is contained in element 1
1868         * of the array "member", which is contained in the non-array member "value".
1869         * @param par record describing the combined predicate annotation
1870         * @return method name
1871         */
1872        protected String getMethodName(PredicateAnnotationRecord par) {
1873            StringBuilder sb = new StringBuilder("check");
1874            if (par.combineMode!=null) {
1875                // @Combine-type annotation
1876                getMethodNameHelper(par, sb, "");
1877            }
1878            return sb.toString();
1879        }
1880    
1881        /**
1882         * Return part of the method name.
1883         * @param par record describing the combined predicate annotation
1884         * @param sb string builder accumulating the nethod name
1885         * @param suffix string suffix for variable names to distinguish them
1886         */
1887        protected void getMethodNameHelper(PredicateAnnotationRecord par, StringBuilder sb, String suffix) {
1888            if (par.combineMode!=null) {
1889                // @Combine-type annotation
1890                for(String key: par.combinedPredicates.keySet()) {
1891                    if (par.paramTypes.get(key).startsWith("[")) {
1892                        // array of annotations, include size and put index into suffix
1893                        sb.append(suffix);
1894                        sb.append('$');
1895                        sb.append(key);
1896                        sb.append("$$$");
1897                        sb.append(par.combinedPredicates.get(key).size());
1898                        int i = 0;
1899                        for(PredicateAnnotationRecord memberPAR: par.combinedPredicates.get(key)) {
1900                            getMethodNameHelper(memberPAR, sb, suffix+"$"+key+"$$"+i);
1901                            ++i;
1902                        }
1903                    }
1904                    else {
1905                        // not an array of annotations, there should really only be one
1906                        for(PredicateAnnotationRecord memberPAR: par.combinedPredicates.get(key)) {
1907                            getMethodNameHelper(memberPAR, sb, suffix+"$"+key);
1908                        }
1909                    }
1910                }
1911            }
1912        }
1913    }