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.CodeAttributeInfo;
007    import edu.rice.cs.cunit.classFile.code.InstructionList;
008    import edu.rice.cs.cunit.classFile.code.Opcode;
009    import edu.rice.cs.cunit.classFile.code.instructions.GenericInstruction;
010    import edu.rice.cs.cunit.classFile.code.instructions.ReferenceInstruction;
011    import edu.rice.cs.cunit.classFile.constantPool.*;
012    import edu.rice.cs.cunit.classFile.constantPool.visitors.ADefaultPoolInfoVisitor;
013    import edu.rice.cs.cunit.classFile.constantPool.visitors.CheckMethodVisitor;
014    import edu.rice.cs.cunit.classFile.constantPool.visitors.CheckUTFVisitor;
015    import edu.rice.cs.cunit.classFile.constantPool.visitors.CheckClassVisitor;
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.util.Types;
020    
021    import java.io.IOException;
022    import java.util.ArrayList;
023    import java.util.List;
024    
025    /**
026     * Instrumentor to add calls to ThreadCheck.checkCurrentThreadReflection to check if the current thread is not
027     * allowed to execute a class or method.
028     *
029     * @author Mathias Ricken
030     */
031    public class AddReflectionThreadCheckStrategy extends AAddThreadCheckStrategy {
032        /**
033         * Constructor for this strategy.
034         * @param shared data shared among all AThreadCheckStrategy instances
035         * @param sharedAdd data for all AAddThreadCheckStrategy instances
036         */
037        public AddReflectionThreadCheckStrategy(SharedData shared, SharedAddData sharedAdd) {
038            this(new ArrayList<String>(), shared, sharedAdd);
039        }
040    
041        /**
042         * Constructor for this strategy.
043         * @param parameters parameters for the instrumentors
044         * @param shared data shared among all AThreadCheckStrategy instances
045         * @param sharedAdd data for all AAddThreadCheckStrategy instances
046         */
047        public AddReflectionThreadCheckStrategy(List<String> parameters, SharedData shared, SharedAddData sharedAdd) {
048            super(parameters, shared, sharedAdd);
049        }
050    
051        /**
052         * Instrument the class.
053         *
054         * @param cf class file info
055         */
056        public void instrument(final ClassFile cf) {
057            _sharedData.setCurrentClassName(cf.getThisClassName());
058            ReferenceInstruction checkCallInstr = new ReferenceInstruction(Opcode.INVOKESTATIC, (short)0);
059            ReferenceInstruction loadInstr = new ReferenceInstruction(Opcode.LDC_W, (short)0);
060            int checkReflCallIndex = 0;
061    
062            // process all methods in this class
063            for(MethodInfo mi : cf.getMethods()) {
064                // proces if not a native or abstract method, should have a body
065                if ((mi.getAccessFlags() & (ClassFile.ACC_NATIVE | ClassFile.ACC_ABSTRACT)) == 0) {
066                    long beginMillis = System.currentTimeMillis();
067                    ThreadCheckAnnotationRecord methodAR = getMethodAnnotations(cf, mi);
068                    long endMillis = System.currentTimeMillis();
069                    _sharedAddData.cacheInfo.addTimeSpent(endMillis-beginMillis);
070    
071                    if (!methodAR.empty()) {
072                        boolean changed = false;
073                        InstructionList il = new InstructionList(mi.getCodeAttributeInfo().getCode());
074    
075                        // if this is a constructor ("<init>"), then we should perhaps wait until
076                        // after the this() or super() call; try to find it
077                        if (mi.getName().toString().equals("<init>")) {
078                            // check if this ctor calls other ctors
079                            boolean ctorCalled = false;
080                            do {
081                                if (il.getOpcode() == Opcode.INVOKESPECIAL) {
082                                    ReferenceInstruction ri = (ReferenceInstruction)il.getInstr();
083                                    short method = Types.shortFromBytes(ri.getBytecode(), 1);
084                                    MethodPoolInfo mpi = cf.getConstantPoolItem(method).execute(CheckMethodVisitor.singleton(), null);
085                                    if (mpi.getNameAndType().getName().toString().equals("<init>")) {
086                                        ClassFile curcf = cf;
087                                        while((curcf.getThisClassName()!=null) && (!curcf.getThisClassName().equals(""))) {
088                                            if (curcf.getThisClass().toString().equals(mpi.getClassInfo().getName().toString())) {
089                                                // super() call
090                                                ctorCalled = true;
091                                                break;
092                                            }
093                                            ClassFileTools.ClassLocation cl = null;
094                                            try {
095                                                cl = ClassFileTools.findClassFile(curcf.getSuperClassName(), _sharedData.getClassPath());
096                                                if (cl != null) {
097                                                    curcf = cl.getClassFile();
098                                                }
099                                                else {
100                                                    _sharedData.addClassNotFoundWarning(new ClassNotFoundWarning(curcf.getSuperClassName(),_sharedData.getCurrentClassName()));
101                                                    //Debug.out.println("Warning: Could not find " + curcf.getSuperClassName());
102                                                    break;
103                                                }
104                                            }
105                                            finally {
106                                                try { if (cl!=null) cl.close(); }
107                                                catch(IOException e) { /* ignore; shouldn't cause any problems except on Windows with read locks */ }
108                                            }
109                                        }
110                                        if (ctorCalled) { break; }
111                                    }
112                                }
113                            } while(il.advanceIndex());
114                            if (ctorCalled) {
115                                // super call found
116                                // advance one past the super call
117                                boolean res = il.advanceIndex();
118                                assert res == true;
119                            }
120                            else {
121                                // no this() or super() call found, start at the beginning
122                                il.setIndex(0);
123                                _sharedAddData.otherWarnings.add(new OnlyAfterRealizedWarning("ignored, no this() or super() call found in constructor "+
124                                                                                            cf.getThisClassName()+"."+mi.getName()+mi.getDescriptor()));
125                                methodAR.allowEventThread = OnlyRunBy.EVENT_THREAD.NO;
126                            }
127                        }
128    
129                        // =====================
130                        // Predicate Annotations
131                        // =====================
132                        if (methodAR.predicateAnnotations.size()>0) {
133                            for (PredicateAnnotationRecord par: methodAR.predicateAnnotations) {
134                                changed = true;
135                                // System.out.println(cf.getThisClassName()+CLASS_SIG_SEPARATOR_STRING+mi.getName().toString()+mi.getDescriptor()+":");
136                                if (checkReflCallIndex==0) {
137                                    checkReflCallIndex = cf.addMethodToConstantPool(ThreadCheck.class.getName().replace('.','/'),
138                                                                                    "checkCurrentThreadReflection",
139                                                                                    "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;[Ljava/lang/Object;)V");
140                                }
141                                checkCallInstr.setReference(checkReflCallIndex);
142    
143                                boolean res;
144    
145                                // add the class
146                                loadInstr.setReference(cf.getConstantPool().indexOf(cf.getThisClass()));
147                                il.insertInstr(loadInstr, mi.getCodeAttributeInfo());
148                                res = il.advanceIndex();
149                                assert res == true;
150    
151                                // add the name
152                                StringPoolInfo spi = new StringPoolInfo(mi.getName(), cf.getConstantPool());
153                                int[] l = cf.addConstantPoolItems(new APoolInfo[]{spi});
154                                spi = cf.getConstantPoolItem(l[0]).execute(new ADefaultPoolInfoVisitor<StringPoolInfo, Object>() {
155                                    public StringPoolInfo defaultCase(APoolInfo host, Object o) {
156                                        throw new ClassFormatError("Info is of type " + host.getClass().getName() + ", needs to be StringPoolInfo");
157                                    }
158                                    public StringPoolInfo stringCase(StringPoolInfo host, Object o) {
159                                        return host;
160                                    }
161                                }, null);
162    
163                                loadInstr.setReference(l[0]);
164                                il.insertInstr(loadInstr, mi.getCodeAttributeInfo());
165                                res = il.advanceIndex();
166                                assert res == true;
167    
168                                // add the descriptor
169                                spi = new StringPoolInfo(mi.getDescriptor(), cf.getConstantPool());
170                                l = cf.addConstantPoolItems(new APoolInfo[]{spi});
171                                spi = cf.getConstantPoolItem(l[0]).execute(new ADefaultPoolInfoVisitor<StringPoolInfo, Object>() {
172                                    public StringPoolInfo defaultCase(APoolInfo host, Object o) {
173                                        throw new ClassFormatError("Info is of type " + host.getClass().getName() + ", needs to be StringPoolInfo");
174                                    }
175                                    public StringPoolInfo stringCase(StringPoolInfo host, Object o) {
176                                        return host;
177                                    }
178                                }, null);
179    
180                                loadInstr.setReference(l[0]);
181                                il.insertInstr(loadInstr, mi.getCodeAttributeInfo());
182                                res = il.advanceIndex();
183                                assert res == true;
184    
185                                // add "this" or null if static
186                                if ((mi.getAccessFlags() & ClassFile.ACC_STATIC)==0) {
187                                    // not static, add "this"
188                                    il.insertInstr(new GenericInstruction(new byte[] { Opcode.ALOAD_0 }), mi.getCodeAttributeInfo());
189                                    res = il.advanceIndex();
190                                    assert res == true;
191                                }
192                                else {
193                                    // static, add null
194                                    il.insertInstr(new GenericInstruction(new byte[] { Opcode.ACONST_NULL }), mi.getCodeAttributeInfo());
195                                    res = il.advanceIndex();
196                                    assert res == true;
197                                }
198    
199                                if (par.passArguments) {
200                                    // add a new name for the element type
201                                    AUTFPoolInfo objectClassName = new ASCIIPoolInfo("java/lang/Object", cf.getConstantPool());
202                                    l = cf.addConstantPoolItems(new APoolInfo[]{objectClassName});
203                                    objectClassName = cf.getConstantPoolItem(l[0]).execute(CheckUTFVisitor.singleton(), null);
204    
205                                    // add a new class item for the element class
206                                    ClassPoolInfo objectClass = new ClassPoolInfo(objectClassName, cf.getConstantPool());
207                                    l = cf.addConstantPoolItems(new APoolInfo[]{objectClass});
208                                    objectClass = cf.getConstantPoolItem(l[0]).execute(CheckClassVisitor.singleton(), null);
209    
210                                    ReferenceInstruction anewarray = new ReferenceInstruction(Opcode.ANEWARRAY, cf.getConstantPool().indexOf(objectClass));
211    
212                                    // add number of elements to class pool
213                                    String sig = mi.getDescriptor().toString();
214                                    List<String> methodArgTypes = ClassFileTools.getSignatures(sig.substring(1,sig.lastIndexOf(')')));
215                                    IntegerPoolInfo count = new IntegerPoolInfo(methodArgTypes.size(), cf.getConstantPool());
216                                    l = cf.addConstantPoolItems(new APoolInfo[]{count});
217                                    count = cf.getConstantPoolItem(l[0]).execute(new ADefaultPoolInfoVisitor<IntegerPoolInfo, Object>() {
218                                        public IntegerPoolInfo defaultCase(APoolInfo host, Object param) {
219                                            throw new ClassFormatError("Info is of type " + host.getClass().getName() + ", needs to be IntegerPoolInfo");
220                                        }
221                                        public IntegerPoolInfo intCase(IntegerPoolInfo host, Object param) {
222                                            return host;
223                                        }
224                                    }, null);
225    
226                                    // load number of arguments onto stack
227                                    il.insertInstr(new ReferenceInstruction(Opcode.LDC_W, cf.getConstantPool().indexOf(count)), mi.getCodeAttributeInfo());
228                                    res = il.advanceIndex();
229                                    assert res == true;
230    
231                                    // create an array of objects of that size
232                                    il.insertInstr(anewarray, mi.getCodeAttributeInfo());
233                                    res = il.advanceIndex();
234                                    assert res == true;
235    
236                                    // top of stack: array-ref
237    
238                                    // for static methods, the first method argument has to be loaded with ALOAD_0;
239                                    // for non-static methods, it's ALOAD_1
240                                    int lvIndex = (ClassFile.ACC_STATIC == (mi.getAccessFlags() & ClassFile.ACC_STATIC))?0:1;
241                                    int nextLVIndex;
242                                    int arrayIndex = 0;
243                                    for(String type: methodArgTypes) {
244                                        IntegerPoolInfo pi;
245                                        ReferenceInstruction ldc_w;
246                                        switch(type.charAt(0)) {
247                                            case '[':
248                                            case 'L':
249                                                nextLVIndex = lvIndex + 1;
250    
251                                                // top of stack: array-ref
252                                                il.insertInstr(new GenericInstruction(Opcode.DUP), mi.getCodeAttributeInfo());
253                                                res = il.advanceIndex();
254                                                assert res == true;
255    
256                                                // top of stack: array-ref array-ref
257    
258                                                pi = new IntegerPoolInfo(arrayIndex, cf.getConstantPool());
259                                                l = cf.addConstantPoolItems(new APoolInfo[]{pi});
260                                                pi = cf.getConstantPoolItem(l[0]).execute(new ADefaultPoolInfoVisitor<IntegerPoolInfo, Object>() {
261                                                    public IntegerPoolInfo defaultCase(APoolInfo host, Object o) {
262                                                        throw new ClassFormatError("Info is of type " + host.getClass().getName() + ", needs to be IntegerPoolInfo");
263                                                    }
264                                                    public IntegerPoolInfo intCase(IntegerPoolInfo host, Object o) {
265                                                        return host;
266                                                    }
267                                                }, null);
268                                                ldc_w = new ReferenceInstruction(Opcode.LDC_W, cf.getConstantPool().indexOf(pi));
269                                                il.insertInstr(ldc_w, mi.getCodeAttributeInfo());
270                                                res = il.advanceIndex();
271                                                assert res == true;
272    
273                                                // top of stack: array-ref array-ref index
274    
275                                                il.insertInstr(Opcode.getShortestLoadStoreInstruction(Opcode.ALOAD, (short)lvIndex), mi.getCodeAttributeInfo());
276                                                res = il.advanceIndex();
277                                                assert res == true;
278                                                break;
279    
280                                                // top of stack: array-ref array-ref index ref
281                                            case 'B':
282                                                nextLVIndex = lvIndex + 1;
283    
284                                                // top of stack: array-ref
285                                                il.insertInstr(new GenericInstruction(Opcode.DUP), mi.getCodeAttributeInfo());
286                                                res = il.advanceIndex();
287                                                assert res == true;
288    
289                                                // top of stack: array-ref array-ref
290    
291                                                pi = new IntegerPoolInfo(arrayIndex, cf.getConstantPool());
292                                                l = cf.addConstantPoolItems(new APoolInfo[]{pi});
293                                                pi = cf.getConstantPoolItem(l[0]).execute(new ADefaultPoolInfoVisitor<IntegerPoolInfo, Object>() {
294                                                    public IntegerPoolInfo defaultCase(APoolInfo host, Object o) {
295                                                        throw new ClassFormatError("Info is of type " + host.getClass().getName() + ", needs to be IntegerPoolInfo");
296                                                    }
297                                                    public IntegerPoolInfo intCase(IntegerPoolInfo host, Object o) {
298                                                        return host;
299                                                    }
300                                                }, null);
301                                                ldc_w = new ReferenceInstruction(Opcode.LDC_W, cf.getConstantPool().indexOf(pi));
302                                                il.insertInstr(ldc_w, mi.getCodeAttributeInfo());
303                                                res = il.advanceIndex();
304                                                assert res == true;
305    
306                                                // top of stack: array-ref array-ref index
307    
308                                                insertCtorCall(cf, mi, il, "java/lang/Byte", "(B)V",
309                                                               Opcode.getShortestLoadStoreInstruction(Opcode.ILOAD, (short)lvIndex));
310    
311                                                // top of stack: array-ref array-ref index Byte-ref
312                                                break;
313                                            case 'C':
314                                                nextLVIndex = lvIndex + 1;
315    
316                                                // top of stack: array-ref
317                                                il.insertInstr(new GenericInstruction(Opcode.DUP), mi.getCodeAttributeInfo());
318                                                res = il.advanceIndex();
319                                                assert res == true;
320    
321                                                // top of stack: array-ref array-ref
322    
323                                                pi = new IntegerPoolInfo(arrayIndex, cf.getConstantPool());
324                                                l = cf.addConstantPoolItems(new APoolInfo[]{pi});
325                                                pi = cf.getConstantPoolItem(l[0]).execute(new ADefaultPoolInfoVisitor<IntegerPoolInfo, Object>() {
326                                                    public IntegerPoolInfo defaultCase(APoolInfo host, Object o) {
327                                                        throw new ClassFormatError("Info is of type " + host.getClass().getName() + ", needs to be IntegerPoolInfo");
328                                                    }
329                                                    public IntegerPoolInfo intCase(IntegerPoolInfo host, Object o) {
330                                                        return host;
331                                                    }
332                                                }, null);
333                                                ldc_w = new ReferenceInstruction(Opcode.LDC_W, cf.getConstantPool().indexOf(pi));
334                                                il.insertInstr(ldc_w, mi.getCodeAttributeInfo());
335                                                res = il.advanceIndex();
336                                                assert res == true;
337    
338                                                // top of stack: array-ref array-ref index
339    
340                                                insertCtorCall(cf, mi, il, "java/lang/Character", "(C)V",
341                                                               Opcode.getShortestLoadStoreInstruction(Opcode.ILOAD, (short)lvIndex));
342    
343                                                // top of stack: array-ref array-ref index Char-ref
344                                                break;
345                                            case 'I':
346                                                nextLVIndex = lvIndex + 1;
347    
348                                                // top of stack: array-ref
349                                                il.insertInstr(new GenericInstruction(Opcode.DUP), mi.getCodeAttributeInfo());
350                                                res = il.advanceIndex();
351                                                assert res == true;
352    
353                                                // top of stack: array-ref array-ref
354    
355                                                pi = new IntegerPoolInfo(arrayIndex, cf.getConstantPool());
356                                                l = cf.addConstantPoolItems(new APoolInfo[]{pi});
357                                                pi = cf.getConstantPoolItem(l[0]).execute(new ADefaultPoolInfoVisitor<IntegerPoolInfo, Object>() {
358                                                    public IntegerPoolInfo defaultCase(APoolInfo host, Object o) {
359                                                        throw new ClassFormatError("Info is of type " + host.getClass().getName() + ", needs to be IntegerPoolInfo");
360                                                    }
361                                                    public IntegerPoolInfo intCase(IntegerPoolInfo host, Object o) {
362                                                        return host;
363                                                    }
364                                                }, null);
365                                                ldc_w = new ReferenceInstruction(Opcode.LDC_W, cf.getConstantPool().indexOf(pi));
366                                                il.insertInstr(ldc_w, mi.getCodeAttributeInfo());
367                                                res = il.advanceIndex();
368                                                assert res == true;
369    
370                                                // top of stack: array-ref array-ref index
371    
372                                                insertCtorCall(cf, mi, il, "java/lang/Integer", "(I)V",
373                                                               Opcode.getShortestLoadStoreInstruction(Opcode.ILOAD, (short)lvIndex));
374    
375                                                // top of stack: array-ref array-ref index Integer-ref
376                                                break;
377                                            case 'S':
378                                                nextLVIndex = lvIndex + 1;
379    
380                                                // top of stack: array-ref
381                                                il.insertInstr(new GenericInstruction(Opcode.DUP), mi.getCodeAttributeInfo());
382                                                res = il.advanceIndex();
383                                                assert res == true;
384    
385                                                // top of stack: array-ref array-ref
386    
387                                                pi = new IntegerPoolInfo(arrayIndex, cf.getConstantPool());
388                                                l = cf.addConstantPoolItems(new APoolInfo[]{pi});
389                                                pi = cf.getConstantPoolItem(l[0]).execute(new ADefaultPoolInfoVisitor<IntegerPoolInfo, Object>() {
390                                                    public IntegerPoolInfo defaultCase(APoolInfo host, Object o) {
391                                                        throw new ClassFormatError("Info is of type " + host.getClass().getName() + ", needs to be IntegerPoolInfo");
392                                                    }
393                                                    public IntegerPoolInfo intCase(IntegerPoolInfo host, Object o) {
394                                                        return host;
395                                                    }
396                                                }, null);
397                                                ldc_w = new ReferenceInstruction(Opcode.LDC_W, cf.getConstantPool().indexOf(pi));
398                                                il.insertInstr(ldc_w, mi.getCodeAttributeInfo());
399                                                res = il.advanceIndex();
400                                                assert res == true;
401    
402                                                // top of stack: array-ref array-ref index
403    
404                                                insertCtorCall(cf, mi, il, "java/lang/Short", "(S)V",
405                                                               Opcode.getShortestLoadStoreInstruction(Opcode.ILOAD, (short)lvIndex));
406    
407                                                // top of stack: array-ref array-ref index Short-ref
408                                                break;
409                                            case 'Z':
410                                                nextLVIndex = lvIndex + 1;
411    
412                                                // top of stack: array-ref
413                                                il.insertInstr(new GenericInstruction(Opcode.DUP), mi.getCodeAttributeInfo());
414                                                res = il.advanceIndex();
415                                                assert res == true;
416    
417                                                // top of stack: array-ref array-ref
418    
419                                                pi = new IntegerPoolInfo(arrayIndex, cf.getConstantPool());
420                                                l = cf.addConstantPoolItems(new APoolInfo[]{pi});
421                                                pi = cf.getConstantPoolItem(l[0]).execute(new ADefaultPoolInfoVisitor<IntegerPoolInfo, Object>() {
422                                                    public IntegerPoolInfo defaultCase(APoolInfo host, Object o) {
423                                                        throw new ClassFormatError("Info is of type " + host.getClass().getName() + ", needs to be IntegerPoolInfo");
424                                                    }
425                                                    public IntegerPoolInfo intCase(IntegerPoolInfo host, Object o) {
426                                                        return host;
427                                                    }
428                                                }, null);
429                                                ldc_w = new ReferenceInstruction(Opcode.LDC_W, cf.getConstantPool().indexOf(pi));
430                                                il.insertInstr(ldc_w, mi.getCodeAttributeInfo());
431                                                res = il.advanceIndex();
432                                                assert res == true;
433    
434                                                // top of stack: array-ref array-ref index
435    
436                                                insertCtorCall(cf, mi, il, "java/lang/Boolean", "(Z)V",
437                                                               Opcode.getShortestLoadStoreInstruction(Opcode.ILOAD, (short)lvIndex));
438    
439                                                // top of stack: array-ref array-ref index Boolean-ref
440                                                break;
441                                            case 'F':
442                                                nextLVIndex = lvIndex + 1;
443    
444                                                // top of stack: array-ref
445                                                il.insertInstr(new GenericInstruction(Opcode.DUP), mi.getCodeAttributeInfo());
446                                                res = il.advanceIndex();
447                                                assert res == true;
448    
449                                                // top of stack: array-ref array-ref
450    
451                                                pi = new IntegerPoolInfo(arrayIndex, cf.getConstantPool());
452                                                l = cf.addConstantPoolItems(new APoolInfo[]{pi});
453                                                pi = cf.getConstantPoolItem(l[0]).execute(new ADefaultPoolInfoVisitor<IntegerPoolInfo, Object>() {
454                                                    public IntegerPoolInfo defaultCase(APoolInfo host, Object o) {
455                                                        throw new ClassFormatError("Info is of type " + host.getClass().getName() + ", needs to be IntegerPoolInfo");
456                                                    }
457                                                    public IntegerPoolInfo intCase(IntegerPoolInfo host, Object o) {
458                                                        return host;
459                                                    }
460                                                }, null);
461                                                ldc_w = new ReferenceInstruction(Opcode.LDC_W, cf.getConstantPool().indexOf(pi));
462                                                il.insertInstr(ldc_w, mi.getCodeAttributeInfo());
463                                                res = il.advanceIndex();
464                                                assert res == true;
465    
466                                                // top of stack: array-ref array-ref index
467    
468                                                insertCtorCall(cf, mi, il, "java/lang/Float", "(F)V",
469                                                               Opcode.getShortestLoadStoreInstruction(Opcode.FLOAD, (short)lvIndex));
470    
471                                                // top of stack: array-ref array-ref index Float-ref
472                                                break;
473                                            case 'J':
474                                                nextLVIndex = lvIndex + 2;
475    
476                                                // top of stack: array-ref
477                                                il.insertInstr(new GenericInstruction(Opcode.DUP), mi.getCodeAttributeInfo());
478                                                res = il.advanceIndex();
479                                                assert res == true;
480    
481                                                // top of stack: array-ref array-ref
482    
483                                                pi = new IntegerPoolInfo(arrayIndex, cf.getConstantPool());
484                                                l = cf.addConstantPoolItems(new APoolInfo[]{pi});
485                                                pi = cf.getConstantPoolItem(l[0]).execute(new ADefaultPoolInfoVisitor<IntegerPoolInfo, Object>() {
486                                                    public IntegerPoolInfo defaultCase(APoolInfo host, Object o) {
487                                                        throw new ClassFormatError("Info is of type " + host.getClass().getName() + ", needs to be IntegerPoolInfo");
488                                                    }
489                                                    public IntegerPoolInfo intCase(IntegerPoolInfo host, Object o) {
490                                                        return host;
491                                                    }
492                                                }, null);
493                                                ldc_w = new ReferenceInstruction(Opcode.LDC_W, cf.getConstantPool().indexOf(pi));
494                                                il.insertInstr(ldc_w, mi.getCodeAttributeInfo());
495                                                res = il.advanceIndex();
496                                                assert res == true;
497    
498                                                // top of stack: array-ref array-ref index
499    
500                                                insertCtorCall(cf, mi, il, "java/lang/Long", "(L)V",
501                                                               Opcode.getShortestLoadStoreInstruction(Opcode.LLOAD, (short)lvIndex));
502    
503                                                // top of stack: array-ref array-ref index Long-ref
504                                                break;
505                                            case 'D':
506                                                nextLVIndex = lvIndex + 2;
507    
508                                                // top of stack: array-ref
509                                                il.insertInstr(new GenericInstruction(Opcode.DUP), mi.getCodeAttributeInfo());
510                                                res = il.advanceIndex();
511                                                assert res == true;
512    
513                                                // top of stack: array-ref array-ref
514    
515                                                pi = new IntegerPoolInfo(arrayIndex, cf.getConstantPool());
516                                                l = cf.addConstantPoolItems(new APoolInfo[]{pi});
517                                                pi = cf.getConstantPoolItem(l[0]).execute(new ADefaultPoolInfoVisitor<IntegerPoolInfo, Object>() {
518                                                    public IntegerPoolInfo defaultCase(APoolInfo host, Object o) {
519                                                        throw new ClassFormatError("Info is of type " + host.getClass().getName() + ", needs to be IntegerPoolInfo");
520                                                    }
521                                                    public IntegerPoolInfo intCase(IntegerPoolInfo host, Object o) {
522                                                        return host;
523                                                    }
524                                                }, null);
525                                                ldc_w = new ReferenceInstruction(Opcode.LDC_W, cf.getConstantPool().indexOf(pi));
526                                                il.insertInstr(ldc_w, mi.getCodeAttributeInfo());
527                                                res = il.advanceIndex();
528                                                assert res == true;
529    
530                                                // top of stack: array-ref array-ref index
531    
532                                                insertCtorCall(cf, mi, il, "java/lang/Double", "(D)V",
533                                                               Opcode.getShortestLoadStoreInstruction(Opcode.DLOAD, (short)lvIndex));
534    
535                                                // top of stack: array-ref array-ref index Double-ref
536                                                break;
537                                            case 'V':
538                                                throw new ThreadCheckException("One of the method parameters had type void (processing "+
539                                                                               cf.getThisClassName()+", "+mi.getName().toString()+
540                                                                               mi.getDescriptor().toString()+")");
541                                            default:
542                                                throw new ThreadCheckException("One of the method parameters had an unknown type '"+type.charAt(0)+"' (processing "+
543                                                                               cf.getThisClassName()+", "+mi.getName().toString()+
544                                                                               mi.getDescriptor().toString()+")");
545                                        }
546    
547                                        // top of stack: array-ref array-ref index value
548    
549                                        il.insertInstr(new GenericInstruction(Opcode.AASTORE), mi.getCodeAttributeInfo());
550                                        res = il.advanceIndex();
551                                        assert res == true;
552    
553                                        // top of stack: array-ref
554                                        ++arrayIndex;
555                                        lvIndex = nextLVIndex;
556                                    }
557    
558                                    // on stack: {array-ref}
559                                }
560                                else {
561                                    // no arguments
562                                    il.insertInstr(new GenericInstruction(new byte[] { Opcode.ACONST_NULL }), mi.getCodeAttributeInfo());
563                                    res = il.advanceIndex();
564                                    assert res == true;
565                                }
566    
567                                il.insertInstr(checkCallInstr, mi.getCodeAttributeInfo());
568                                res = il.advanceIndex();
569                                assert res == true;
570                            }
571                        }
572    
573                        if (changed) {
574                            // write code back
575                            mi.getCodeAttributeInfo().setCode(il.getCode());
576    
577                            // make sure we have at least five slots on the stack
578                            CodeAttributeInfo.CodeProperties cProps = mi.getCodeAttributeInfo().getProperties();
579                            cProps.maxStack = Math.max(11, cProps.maxStack);
580                            mi.getCodeAttributeInfo().setProperties(cProps.maxStack, cProps.maxLocals);
581                        }
582                    }
583                }
584            }
585        }
586    }