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 }