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 }