1 package ch.ethz.prose.jvmai.jikesrvm.advice_weaver; 2 3 import java.io.*; 4 import java.lang.reflect.Field ; 5 import java.lang.reflect.Method ; 6 import java.util.*; 7 8 import org.apache.bcel.Constants; 9 import org.apache.bcel.Repository; 10 import org.apache.bcel.classfile.*; 11 import org.apache.bcel.generic.*; 12 import org.apache.bcel.verifier.*; 13 14 import com.ibm.JikesRVM.classloader.VM_ClassEvolution; 15 import com.ibm.JikesRVM.classloader.VM_Field; 16 17 24 public class MethodWeaver { 25 26 29 public static final String JVMAI_CLASS = "ch.ethz.prose.jvmai.jikesrvm.advice_weaver.AdviceJVMAI"; 30 31 34 protected static final boolean verifyBytecodes; 35 36 static { 37 String p = System.getProperty("ch.ethz.prose.jikesrvm.MethodWeaver.verifyBytecodes", ""); 38 verifyBytecodes = p.toLowerCase().equals("true"); 39 } 40 41 45 protected static Map weavers = new HashMap(); 46 47 52 public static synchronized MethodWeaver getWeaver(Method target) { 53 if (target == null) 54 throw new NullPointerException ("Parameter `target' must not be null."); 55 56 MethodWeaver result = (MethodWeaver) weavers.get(target.toString()); 57 if (result == null) { 58 result = new MethodWeaver(target); 59 weavers.put(target.toString(), result); 60 } 61 62 return result; 63 }; 64 65 68 public static synchronized void commit() { 69 Iterator it = weavers.values().iterator(); 70 while (it.hasNext()) { 71 MethodWeaver mw = (MethodWeaver) it.next(); 72 if (mw.modified) { 73 mw.weave(); 75 } 76 } 77 78 VM_ClassEvolution.commit(); 79 } 80 81 84 public static synchronized void resetAll() { 85 Iterator it = weavers.values().iterator(); 86 while (it.hasNext()) { 87 MethodWeaver mw = (MethodWeaver) it.next(); 88 if (mw.woven) { 89 VM_ClassEvolution.redefineMethod(mw.target, mw.originalCode); 91 } 92 } 93 94 VM_ClassEvolution.commit(); 95 weavers = new HashMap(); 96 } 97 98 101 protected Method target; 102 103 106 protected int targetId; 107 108 111 protected Class targetClass; 112 113 116 protected byte[] originalCode; 117 118 121 protected Method redefineAdvice; 122 123 126 protected boolean methodEntryEnabled; 127 128 131 protected boolean methodExitEnabled; 132 133 136 protected Map fieldAccessors = new HashMap(); 137 138 141 protected Map fieldModifiers = new HashMap(); 142 143 146 protected boolean modified; 147 148 151 protected boolean woven; 152 153 156 protected ConstantPoolGen cpGen; 157 158 161 protected InstructionFactory instructionFactory; 162 163 166 protected MethodGen methodGen; 167 168 172 protected InstructionList instructions; 173 174 180 protected MethodWeaver(Method target) { 181 this.target = target; 182 targetClass = target.getDeclaringClass(); 183 targetId = java.lang.reflect.JikesRVMSupport.getMethodOf(target).getId(); 184 originalCode = VM_ClassEvolution.getMethodCode(target); 185 } 186 187 192 public void setRedefineAdvice(Method advice) { 193 redefineAdvice = advice; 194 modified = true; 195 } 196 197 202 public void setMethodEntryEnabled(boolean flag) { 203 methodEntryEnabled = flag; 204 modified = true; 205 } 206 207 212 public void setMethodExitEnabled(boolean flag) { 213 methodExitEnabled = flag; 214 modified = true; 215 } 216 217 222 public void addFieldAccessor(java.lang.reflect.Field f) { 223 String key = f.getDeclaringClass().getName() + "#" + f.getName(); 224 225 if (!fieldAccessors.containsKey(key)) { 226 fieldAccessors.put(key, f); 228 modified = true; 229 } 230 } 231 232 237 public void removeFieldAccessor(java.lang.reflect.Field f) { 238 String key = f.getDeclaringClass().getName() + "#" + f.getName(); 239 240 if (fieldAccessors.containsKey(key)) { 241 fieldAccessors.remove(key); 242 modified = true; 243 } 244 } 245 246 251 public void addFieldModifier(java.lang.reflect.Field f) { 252 String key = f.getDeclaringClass().getName() + "#" + f.getName(); 253 254 if (!fieldModifiers.containsKey(key)) { 255 fieldModifiers.put(key, f); 257 modified = true; 258 } 259 } 260 261 266 public void removeFieldModifier(java.lang.reflect.Field f) { 267 String key = f.getDeclaringClass().getName() + "#" + f.getName(); 268 269 if (fieldModifiers.containsKey(key)) { 270 fieldModifiers.remove(key); 271 modified = true; 272 } 273 } 274 275 public String debugString() { 276 StringBuffer sb = new StringBuffer (); 277 278 sb.append("MethodWeaver for: "); 279 sb.append(target); 280 sb.append("\n\tredefineAdvice: "); 281 sb.append(redefineAdvice); 282 sb.append("\n\tmethodEntryEnabled: "); 283 sb.append(methodEntryEnabled); 284 sb.append("\n\tmethodExitEnabled: "); 285 sb.append(methodExitEnabled); 286 sb.append("\n\tfieldAccessors: "); 287 sb.append(fieldAccessors.values()); 288 sb.append("\n\tfieldModifiers: "); 289 sb.append(fieldModifiers.values()); 290 sb.append("\n\tmodified: "); 291 sb.append(modified); 292 293 return sb.toString(); 294 } 295 296 299 protected void weave() { 300 initWeaving(); 301 302 if (redefineAdvice != null) 303 weaveRedefineAdvice(); 304 305 weaveFieldAdvice(); 306 307 if (methodEntryEnabled) 308 weaveMethodEntryAdvice(); 309 310 if (methodExitEnabled) 311 weaveMethodExitAdvice(); 312 313 installBytecode(); 314 315 finishWeaving(); 316 } 317 318 321 protected void initWeaving() { 322 byte[] code = VM_ClassEvolution.getConstantPoolCode(targetClass); 323 ConstantPool target_cp = ProseSupport.getConstantPool(code); 324 cpGen = new ConstantPoolGen(target_cp); 325 instructionFactory = new InstructionFactory(cpGen); 326 328 org.apache.bcel.classfile.Method bm = ProseSupport.getMethod(originalCode, target_cp); 329 methodGen = new MethodGen(bm, targetClass.getName(), cpGen); 333 instructions = methodGen.getInstructionList(); 334 } 335 336 339 protected void finishWeaving() { 340 modified = false; 341 woven = true; 342 cpGen = null; 343 methodGen = null; 344 instructionFactory = null; 345 instructions = null; 346 } 347 348 351 protected void weaveFieldAdvice() { 352 for (InstructionHandle h = instructions.getStart(); h != null; h = h.getNext()) { 353 Instruction instr = h.getInstruction(); 354 355 if (instr instanceof FieldInstruction) { 356 FieldInstruction fi = (FieldInstruction) instr; 357 String key = fi.getClassName(cpGen) + "#" + fi.getName(cpGen); 358 359 if ((instr instanceof GETSTATIC || instr instanceof GETFIELD) && fieldAccessors.containsKey(key)) { 360 Field field = (Field ) fieldAccessors.get(key); 361 VM_Field vm_field = java.lang.reflect.JikesRVMSupport.getFieldOf(field); 362 363 instructions.insert(h, createFieldAdviceCallback("onFieldAccess", vm_field)); 365 } else if ((instr instanceof PUTSTATIC || instr instanceof PUTFIELD) && fieldModifiers.containsKey(key)) { 366 Field field = (Field ) fieldModifiers.get(key); 367 VM_Field vm_field = java.lang.reflect.JikesRVMSupport.getFieldOf(field); 368 369 Type field_type = fi.getFieldType(cpGen); 371 int local_index = methodGen.getMaxLocals(); 372 instructions.insert(h, InstructionFactory.createStore(field_type, local_index)); 373 methodGen.setMaxLocals(local_index + field_type.getSize()); 374 375 instructions.insert(h, createFieldAdviceCallback("onFieldModification", vm_field)); 377 378 instructions.insert(h, InstructionFactory.createLoad(field_type, local_index)); 380 } 381 } 382 } 383 } 384 385 389 protected void weaveRedefineAdvice() { 390 Class advice_class = redefineAdvice.getDeclaringClass(); 392 byte[] cp_code = VM_ClassEvolution.getConstantPoolCode(advice_class); 393 ConstantPool advice_cp = ProseSupport.getConstantPool(cp_code); 394 ConstantPoolGen advice_cpg = new ConstantPoolGen(advice_cp); 395 396 byte[] advice_code = VM_ClassEvolution.getMethodCode(redefineAdvice); 397 org.apache.bcel.classfile.Method bm = ProseSupport.getMethod(advice_code, advice_cp); 398 MethodGen advice_mg = new MethodGen(bm, advice_class.getName(), advice_cpg); 399 instructions = advice_mg.getInstructionList(); 400 methodGen.setInstructionList(instructions); 401 methodGen.setMaxLocals(advice_mg.getMaxLocals()); 402 403 for (InstructionHandle h = instructions.getStart(); h != null; h = h.getNext()) { 404 Instruction instr = h.getInstruction(); 405 406 if (instr instanceof LocalVariableInstruction) { 409 LocalVariableInstruction lv_instr = (LocalVariableInstruction) instr; 410 if (lv_instr.getIndex() == 0) 411 throw new RuntimeException ("No (implicit) `this' usage allowed in advice method: " + redefineAdvice); 412 lv_instr.setIndex(lv_instr.getIndex() - 1); 413 } 414 415 if (instr instanceof CPInstruction) { 418 CPInstruction cp_instr = (CPInstruction) instr; 419 Constant c = advice_cp.getConstant(cp_instr.getIndex()); 420 int new_index = cpGen.addConstant(c, advice_cpg); 421 cp_instr.setIndex(new_index); 422 } 423 } 424 425 methodGen.removeExceptionHandlers(); 427 CodeExceptionGen[] cegs = advice_mg.getExceptionHandlers(); 428 for (int i = 0; i < cegs.length; i++) { 429 CodeExceptionGen ceg = cegs[i]; 430 methodGen.addExceptionHandler(ceg.getStartPC(), ceg.getEndPC(), ceg.getHandlerPC(), ceg.getCatchType()); 431 } 432 } 433 434 437 protected void weaveMethodEntryAdvice() { 438 instructions.insert(createMethodAdviceCallback("onMethodEntry")); 440 } 441 442 445 protected void weaveMethodExitAdvice() { 446 InstructionHandle try_start = instructions.getStart(); 448 InstructionHandle try_end = instructions.getEnd(); 449 450 int local_index = methodGen.getMaxLocals(); 452 InstructionHandle finally_start = instructions.append(new ASTORE(local_index)); 453 instructions.append(createMethodAdviceCallback("onMethodExit")); 454 instructions.append(new RET(local_index++)); 455 456 InstructionHandle catch_start = instructions.insert(finally_start, new ASTORE(local_index)); 458 instructions.insert(finally_start, new JSR(finally_start)); 459 instructions.insert(finally_start, new ALOAD(local_index++)); 460 instructions.insert(finally_start, new ATHROW()); 461 462 JumpFinallyVisitor visitor = new JumpFinallyVisitor(instructions, try_start, try_end, finally_start, local_index); 464 visitor.go(); 465 local_index += visitor.getLocalSize(); 466 467 methodGen.setMaxLocals(local_index); 468 methodGen.addExceptionHandler(try_start, try_end, catch_start, null); 469 } 470 471 477 protected InstructionList createMethodAdviceCallback(String callbackMethod) { 478 InstructionList il = new InstructionList(); 479 480 il.append(new PUSH(cpGen, targetId)); 482 483 if (methodGen.isStatic()) 485 il.append(InstructionConstants.ACONST_NULL); 486 else 487 il.append(InstructionFactory.createThis()); 488 489 loadArgs(il); 491 492 il.append( 494 instructionFactory.createInvoke( 495 JVMAI_CLASS, 496 callbackMethod, 497 Type.VOID, 498 new Type[] { Type.INT, Type.OBJECT, new ArrayType(Type.OBJECT, 1)}, 499 Constants.INVOKESTATIC)); 500 501 return il; 502 } 503 504 511 protected InstructionList createFieldAdviceCallback(String callbackMethod, VM_Field field) { 512 InstructionList il = new InstructionList(); 513 514 if (field.isStatic()) 516 il.append(InstructionConstants.ACONST_NULL); 517 else 518 il.append(InstructionConstants.DUP); 519 520 il.append(new PUSH(cpGen, field.getId())); 522 523 il.append(new PUSH(cpGen, targetId)); 525 526 if (methodGen.isStatic()) 528 il.append(InstructionConstants.ACONST_NULL); 529 else 530 il.append(InstructionFactory.createThis()); 531 532 loadArgs(il); 534 535 il.append( 537 instructionFactory.createInvoke( 538 JVMAI_CLASS, 539 callbackMethod, 540 Type.VOID, 541 new Type[] { Type.OBJECT, Type.INT, Type.INT, Type.OBJECT, new ArrayType(Type.OBJECT, 1)}, 542 Constants.INVOKESTATIC)); 543 544 return il; 545 } 546 547 552 protected void loadArgs(InstructionList il) { 553 Type[] arg_types = methodGen.getArgumentTypes(); 554 555 il.append(new PUSH(cpGen, arg_types.length)); 556 il.append(instructionFactory.createNewArray(Type.OBJECT, (short) 1)); 557 558 int varnum = methodGen.isStatic()? 0: 1; 559 for (int i = 0; i < methodGen.getArgumentTypes().length; i++) { 560 il.append(InstructionConstants.DUP); 561 il.append(new PUSH(cpGen, i)); 562 loadArg(il, arg_types[i], varnum); 563 il.append(InstructionConstants.AASTORE); 564 varnum += arg_types[i].getSize(); 565 } 566 } 567 568 575 protected void loadArg(InstructionList il, Type type, int index) { 576 if (type instanceof BasicType) { 577 String wrapper = Constants.CLASS_TYPE_NAMES[type.getType()]; 578 il.append(instructionFactory.createNew(wrapper)); 579 il.append(InstructionConstants.DUP); 580 il.append(InstructionFactory.createLoad(type, index)); 581 il.append( 582 instructionFactory.createInvoke( 583 wrapper, 584 Constants.CONSTRUCTOR_NAME, 585 Type.VOID, 586 new Type[] { type }, 587 Constants.INVOKESPECIAL)); 588 } else if (type instanceof ReferenceType) { 589 il.append(InstructionFactory.createLoad(type, index)); 590 } else { 591 throw new RuntimeException ("Invalid type: " + type); 592 } 593 } 594 595 598 protected void installBytecode() { 599 try { 600 methodGen.setMaxStack(); 602 methodGen.setMaxLocals(); 603 org.apache.bcel.classfile.Method bm = methodGen.getMethod(); 604 instructions.dispose(); 605 bm.getCode().setAttributes(null); 606 607 ByteArrayOutputStream bout = new ByteArrayOutputStream(); 609 DataOutputStream out = new DataOutputStream(bout); 610 ConstantPool pool = cpGen.getFinalConstantPool(); 611 if (verifyBytecodes) 612 verify(pool, bm); 613 pool.dump(out); 614 VM_ClassEvolution.extendConstantPool(targetClass, bout.toByteArray()); 617 618 bout = new ByteArrayOutputStream(); 620 out = new DataOutputStream(bout); 621 bm.dump(out); 622 VM_ClassEvolution.redefineMethod(target, bout.toByteArray()); 626 } catch (IOException e) { 627 throw new RuntimeException ("Oops.", e); 628 } 629 } 630 631 640 protected void verify(ConstantPool cp, org.apache.bcel.classfile.Method bm) { 641 String class_name = target.getDeclaringClass().getName(); 642 644 Repository.clearCache(); 645 JavaClass jc = Repository.lookupClass(class_name); 646 jc.setConstantPool(cp); 647 648 org.apache.bcel.classfile.Method[] methods = jc.getMethods(); 649 int method_index = 0; 650 for (method_index = 0; method_index < methods.length; method_index++) 651 if (methods[method_index].toString().equals(bm.toString())) 652 break; 653 if (method_index == methods.length) 654 throw new RuntimeException ("Method not found!"); 655 methods[method_index] = bm; 656 657 Verifier v = VerifierFactory.getVerifier(class_name); 658 checkVerificationResult(v.doPass1(), "1"); 659 checkVerificationResult(v.doPass2(), "2"); 660 checkVerificationResult(v.doPass3a(method_index), "3a"); 661 checkVerificationResult(v.doPass3b(method_index), "3b"); 662 663 String [] warnings = v.getMessages(); 664 if (warnings.length != 0) 665 System.err.println("Messages:"); 666 for (int j = 0; j < warnings.length; j++) 667 System.err.println(warnings[j]); 668 } 669 670 676 protected void checkVerificationResult(VerificationResult vr, String pass) { 677 if (vr != VerificationResult.VR_OK) { 678 System.err.println("Verification failed in pass " + pass + " for " + target + "."); 679 System.err.println(vr); 680 } 681 } 682 683 } 684 | Popular Tags |