1/* 2 * Javassist, a Java-bytecode translator toolkit. 3 * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. 4 * 5 * The contents of this file are subject to the Mozilla Public License Version 6 * 1.1 (the "License"); you may not use this file except in compliance with 7 * the License. Alternatively, the contents of this file may be used under 8 * the terms of the GNU Lesser General Public License Version 2.1 or later. 9 * 10 * Software distributed under the License is distributed on an "AS IS" basis, 11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 12 * for the specific language governing rights and limitations under the 13 * License. 14 */ 15 16package javassist; 17 18import javassist.bytecode.*; 19import javassist.compiler.Javac; 20import javassist.compiler.CompileError; 21import javassist.expr.ExprEditor; 22 23/** 24 * <code>CtBehavior</code> represents a method, a constructor, 25 * or a static constructor (class initializer). 26 * It is the abstract super class of 27 * <code>CtMethod</code> and <code>CtConstructor</code>. 28 */ 29public abstract class CtBehavior extends CtMember { 30 protected MethodInfo methodInfo; 31 32 protected CtBehavior(CtClass clazz, MethodInfo minfo) { 33 super(clazz); 34 methodInfo = minfo; 35 } 36 37 /** 38 * @param isCons true if this is a constructor. 39 */ 40 void copy(CtBehavior src, boolean isCons, ClassMap map) 41 throws CannotCompileException 42 { 43 CtClass declaring = declaringClass; 44 MethodInfo srcInfo = src.methodInfo; 45 CtClass srcClass = src.getDeclaringClass(); 46 ConstPool cp = declaring.getClassFile2().getConstPool(); 47 48 map = new ClassMap(map); 49 map.put(srcClass.getName(), declaring.getName()); 50 try { 51 boolean patch = false; 52 CtClass srcSuper = srcClass.getSuperclass(); 53 CtClass destSuper = declaring.getSuperclass(); 54 String destSuperName = null; 55 if (srcSuper != null && destSuper != null) { 56 String srcSuperName = srcSuper.getName(); 57 destSuperName = destSuper.getName(); 58 if (!srcSuperName.equals(destSuperName)) 59 if (srcSuperName.equals(CtClass.javaLangObject)) 60 patch = true; 61 else 62 map.putIfNone(srcSuperName, destSuperName); 63 } 64 65 // a stack map table is copied from srcInfo. 66 methodInfo = new MethodInfo(cp, srcInfo.getName(), srcInfo, map); 67 if (isCons && patch) 68 methodInfo.setSuperclass(destSuperName); 69 } 70 catch (NotFoundException e) { 71 throw new CannotCompileException(e); 72 } 73 catch (BadBytecode e) { 74 throw new CannotCompileException(e); 75 } 76 } 77 78 protected void extendToString(StringBuffer buffer) { 79 buffer.append(' '); 80 buffer.append(getName()); 81 buffer.append(' '); 82 buffer.append(methodInfo.getDescriptor()); 83 } 84 85 /** 86 * Returns the method or constructor name followed by parameter types 87 * such as <code>javassist.CtBehavior.stBody(String)</code>. 88 * 89 * @since 3.5 90 */ 91 public abstract String getLongName(); 92 93 /** 94 * Returns the MethodInfo representing this method/constructor in the 95 * class file. 96 */ 97 public MethodInfo getMethodInfo() { 98 declaringClass.checkModify(); 99 return methodInfo; 100 } 101 102 /** 103 * Returns the MethodInfo representing the method/constructor in the 104 * class file (read only). 105 * Normal applications do not need calling this method. Use 106 * <code>getMethodInfo()</code>. 107 * 108 * <p>The <code>MethodInfo</code> object obtained by this method 109 * is read only. Changes to this object might not be reflected 110 * on a class file generated by <code>toBytecode()</code>, 111 * <code>toClass()</code>, etc in <code>CtClass</code>. 112 * 113 * <p>This method is available even if the <code>CtClass</code> 114 * containing this method is frozen. However, if the class is 115 * frozen, the <code>MethodInfo</code> might be also pruned. 116 * 117 * @see #getMethodInfo() 118 * @see CtClass#isFrozen() 119 * @see CtClass#prune() 120 */ 121 public MethodInfo getMethodInfo2() { return methodInfo; } 122 123 /** 124 * Obtains the modifiers of the method/constructor. 125 * 126 * @return modifiers encoded with 127 * <code>javassist.Modifier</code>. 128 * @see Modifier 129 */ 130 public int getModifiers() { 131 return AccessFlag.toModifier(methodInfo.getAccessFlags()); 132 } 133 134 /** 135 * Sets the encoded modifiers of the method/constructor. 136 * 137 * <p>Changing the modifiers may cause a problem. 138 * For example, if a non-static method is changed to static, 139 * the method will be rejected by the bytecode verifier. 140 * 141 * @see Modifier 142 */ 143 public void setModifiers(int mod) { 144 declaringClass.checkModify(); 145 methodInfo.setAccessFlags(AccessFlag.of(mod)); 146 } 147 148 /** 149 * Returns true if the class has the specified annotation class. 150 * 151 * @param clz the annotation class. 152 * @return <code>true</code> if the annotation is found, 153 * otherwise <code>false</code>. 154 * @since 3.11 155 */ 156 public boolean hasAnnotation(Class clz) { 157 MethodInfo mi = getMethodInfo2(); 158 AnnotationsAttribute ainfo = (AnnotationsAttribute) 159 mi.getAttribute(AnnotationsAttribute.invisibleTag); 160 AnnotationsAttribute ainfo2 = (AnnotationsAttribute) 161 mi.getAttribute(AnnotationsAttribute.visibleTag); 162 return CtClassType.hasAnnotationType(clz, 163 getDeclaringClass().getClassPool(), 164 ainfo, ainfo2); 165 } 166 167 /** 168 * Returns the annotation if the class has the specified annotation class. 169 * For example, if an annotation <code>@Author</code> is associated 170 * with this method/constructor, an <code>Author</code> object is returned. 171 * The member values can be obtained by calling methods on 172 * the <code>Author</code> object. 173 * 174 * @param clz the annotation class. 175 * @return the annotation if found, otherwise <code>null</code>. 176 * @since 3.11 177 */ 178 public Object getAnnotation(Class clz) throws ClassNotFoundException { 179 MethodInfo mi = getMethodInfo2(); 180 AnnotationsAttribute ainfo = (AnnotationsAttribute) 181 mi.getAttribute(AnnotationsAttribute.invisibleTag); 182 AnnotationsAttribute ainfo2 = (AnnotationsAttribute) 183 mi.getAttribute(AnnotationsAttribute.visibleTag); 184 return CtClassType.getAnnotationType(clz, 185 getDeclaringClass().getClassPool(), 186 ainfo, ainfo2); 187 } 188 189 /** 190 * Returns the annotations associated with this method or constructor. 191 * 192 * @return an array of annotation-type objects. 193 * @see #getAvailableAnnotations() 194 * @since 3.1 195 */ 196 public Object[] getAnnotations() throws ClassNotFoundException { 197 return getAnnotations(false); 198 } 199 200 /** 201 * Returns the annotations associated with this method or constructor. 202 * If any annotations are not on the classpath, they are not included 203 * in the returned array. 204 * 205 * @return an array of annotation-type objects. 206 * @see #getAnnotations() 207 * @since 3.3 208 */ 209 public Object[] getAvailableAnnotations(){ 210 try{ 211 return getAnnotations(true); 212 } 213 catch (ClassNotFoundException e){ 214 throw new RuntimeException("Unexpected exception", e); 215 } 216 } 217 218 private Object[] getAnnotations(boolean ignoreNotFound) 219 throws ClassNotFoundException 220 { 221 MethodInfo mi = getMethodInfo2(); 222 AnnotationsAttribute ainfo = (AnnotationsAttribute) 223 mi.getAttribute(AnnotationsAttribute.invisibleTag); 224 AnnotationsAttribute ainfo2 = (AnnotationsAttribute) 225 mi.getAttribute(AnnotationsAttribute.visibleTag); 226 return CtClassType.toAnnotationType(ignoreNotFound, 227 getDeclaringClass().getClassPool(), 228 ainfo, ainfo2); 229 } 230 231 /** 232 * Returns the parameter annotations associated with this method or constructor. 233 * 234 * @return an array of annotation-type objects. The length of the returned array is 235 * equal to the number of the formal parameters. If each parameter has no 236 * annotation, the elements of the returned array are empty arrays. 237 * 238 * @see #getAvailableParameterAnnotations() 239 * @see #getAnnotations() 240 * @since 3.1 241 */ 242 public Object[][] getParameterAnnotations() throws ClassNotFoundException { 243 return getParameterAnnotations(false); 244 } 245 246 /** 247 * Returns the parameter annotations associated with this method or constructor. 248 * If any annotations are not on the classpath, they are not included in the 249 * returned array. 250 * 251 * @return an array of annotation-type objects. The length of the returned array is 252 * equal to the number of the formal parameters. If each parameter has no 253 * annotation, the elements of the returned array are empty arrays. 254 * 255 * @see #getParameterAnnotations() 256 * @see #getAvailableAnnotations() 257 * @since 3.3 258 */ 259 public Object[][] getAvailableParameterAnnotations(){ 260 try { 261 return getParameterAnnotations(true); 262 } 263 catch(ClassNotFoundException e) { 264 throw new RuntimeException("Unexpected exception", e); 265 } 266 } 267 268 Object[][] getParameterAnnotations(boolean ignoreNotFound) 269 throws ClassNotFoundException 270 { 271 MethodInfo mi = getMethodInfo2(); 272 ParameterAnnotationsAttribute ainfo = (ParameterAnnotationsAttribute) 273 mi.getAttribute(ParameterAnnotationsAttribute.invisibleTag); 274 ParameterAnnotationsAttribute ainfo2 = (ParameterAnnotationsAttribute) 275 mi.getAttribute(ParameterAnnotationsAttribute.visibleTag); 276 return CtClassType.toAnnotationType(ignoreNotFound, 277 getDeclaringClass().getClassPool(), 278 ainfo, ainfo2, mi); 279 } 280 281 /** 282 * Obtains parameter types of this method/constructor. 283 */ 284 public CtClass[] getParameterTypes() throws NotFoundException { 285 return Descriptor.getParameterTypes(methodInfo.getDescriptor(), 286 declaringClass.getClassPool()); 287 } 288 289 /** 290 * Obtains the type of the returned value. 291 */ 292 CtClass getReturnType0() throws NotFoundException { 293 return Descriptor.getReturnType(methodInfo.getDescriptor(), 294 declaringClass.getClassPool()); 295 } 296 297 /** 298 * Returns the method signature (the parameter types 299 * and the return type). 300 * The method signature is represented by a character string 301 * called method descriptor, which is defined in the JVM specification. 302 * If two methods/constructors have 303 * the same parameter types 304 * and the return type, <code>getSignature()</code> returns the 305 * same string (the return type of constructors is <code>void</code>). 306 * 307 * <p>Note that the returned string is not the type signature 308 * contained in the <code>SignatureAttirbute</code>. It is 309 * a descriptor. To obtain a type signature, call the following 310 * methods: 311 * 312 * <ul><pre>getMethodInfo().getAttribute(SignatureAttribute.tag) 313 * </pre></ul> 314 * 315 * @see javassist.bytecode.Descriptor 316 * @see javassist.bytecode.SignatureAttribute 317 */ 318 public String getSignature() { 319 return methodInfo.getDescriptor(); 320 } 321 322 /** 323 * Obtains exceptions that this method/constructor may throw. 324 * 325 * @return a zero-length array if there is no throws clause. 326 */ 327 public CtClass[] getExceptionTypes() throws NotFoundException { 328 String[] exceptions; 329 ExceptionsAttribute ea = methodInfo.getExceptionsAttribute(); 330 if (ea == null) 331 exceptions = null; 332 else 333 exceptions = ea.getExceptions(); 334 335 return declaringClass.getClassPool().get(exceptions); 336 } 337 338 /** 339 * Sets exceptions that this method/constructor may throw. 340 */ 341 public void setExceptionTypes(CtClass[] types) throws NotFoundException { 342 declaringClass.checkModify(); 343 if (types == null || types.length == 0) { 344 methodInfo.removeExceptionsAttribute(); 345 return; 346 } 347 348 String[] names = new String[types.length]; 349 for (int i = 0; i < types.length; ++i) 350 names[i] = types[i].getName(); 351 352 ExceptionsAttribute ea = methodInfo.getExceptionsAttribute(); 353 if (ea == null) { 354 ea = new ExceptionsAttribute(methodInfo.getConstPool()); 355 methodInfo.setExceptionsAttribute(ea); 356 } 357 358 ea.setExceptions(names); 359 } 360 361 /** 362 * Returns true if the body is empty. 363 */ 364 public abstract boolean isEmpty(); 365 366 /** 367 * Sets a method/constructor body. 368 * 369 * @param src the source code representing the body. 370 * It must be a single statement or block. 371 * If it is <code>null</code>, the substituted 372 * body does nothing except returning zero or null. 373 */ 374 public void setBody(String src) throws CannotCompileException { 375 setBody(src, null, null); 376 } 377 378 /** 379 * Sets a method/constructor body. 380 * 381 * @param src the source code representing the body. 382 * It must be a single statement or block. 383 * If it is <code>null</code>, the substituted 384 * body does nothing except returning zero or null. 385 * @param delegateObj the source text specifying the object 386 * that is called on by <code>$proceed()</code>. 387 * @param delegateMethod the name of the method 388 * that is called by <code>$proceed()</code>. 389 */ 390 public void setBody(String src, 391 String delegateObj, String delegateMethod) 392 throws CannotCompileException 393 { 394 CtClass cc = declaringClass; 395 cc.checkModify(); 396 try { 397 Javac jv = new Javac(cc); 398 if (delegateMethod != null) 399 jv.recordProceed(delegateObj, delegateMethod); 400 401 Bytecode b = jv.compileBody(this, src); 402 methodInfo.setCodeAttribute(b.toCodeAttribute()); 403 methodInfo.setAccessFlags(methodInfo.getAccessFlags() 404 & ~AccessFlag.ABSTRACT); 405 methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2()); 406 declaringClass.rebuildClassFile(); 407 } 408 catch (CompileError e) { 409 throw new CannotCompileException(e); 410 } catch (BadBytecode e) { 411 throw new CannotCompileException(e); 412 } 413 } 414 415 static void setBody0(CtClass srcClass, MethodInfo srcInfo, 416 CtClass destClass, MethodInfo destInfo, 417 ClassMap map) 418 throws CannotCompileException 419 { 420 destClass.checkModify(); 421 422 map = new ClassMap(map); 423 map.put(srcClass.getName(), destClass.getName()); 424 try { 425 CodeAttribute cattr = srcInfo.getCodeAttribute(); 426 if (cattr != null) { 427 ConstPool cp = destInfo.getConstPool(); 428 CodeAttribute ca = (CodeAttribute)cattr.copy(cp, map); 429 destInfo.setCodeAttribute(ca); 430 // a stack map table is copied to destInfo. 431 } 432 } 433 catch (CodeAttribute.RuntimeCopyException e) { 434 /* the exception may be thrown by copy() in CodeAttribute. 435 */ 436 throw new CannotCompileException(e); 437 } 438 439 destInfo.setAccessFlags(destInfo.getAccessFlags() 440 & ~AccessFlag.ABSTRACT); 441 destClass.rebuildClassFile(); 442 } 443 444 /** 445 * Obtains an attribute with the given name. 446 * If that attribute is not found in the class file, this 447 * method returns null. 448 * 449 * <p>Note that an attribute is a data block specified by 450 * the class file format. It is not an annotation. 451 * See {@link javassist.bytecode.AttributeInfo}. 452 * 453 * @param name attribute name 454 */ 455 public byte[] getAttribute(String name) { 456 AttributeInfo ai = methodInfo.getAttribute(name); 457 if (ai == null) 458 return null; 459 else 460 return ai.get(); 461 } 462 463 /** 464 * Adds an attribute. The attribute is saved in the class file. 465 * 466 * <p>Note that an attribute is a data block specified by 467 * the class file format. It is not an annotation. 468 * See {@link javassist.bytecode.AttributeInfo}. 469 * 470 * @param name attribute name 471 * @param data attribute value 472 */ 473 public void setAttribute(String name, byte[] data) { 474 declaringClass.checkModify(); 475 methodInfo.addAttribute(new AttributeInfo(methodInfo.getConstPool(), 476 name, data)); 477 } 478 479 /** 480 * Declares to use <code>$cflow</code> for this method/constructor. 481 * If <code>$cflow</code> is used, the class files modified 482 * with Javassist requires a support class 483 * <code>javassist.runtime.Cflow</code> at runtime 484 * (other Javassist classes are not required at runtime). 485 * 486 * <p>Every <code>$cflow</code> variable is given a unique name. 487 * For example, if the given name is <code>"Point.paint"</code>, 488 * then the variable is indicated by <code>$cflow(Point.paint)</code>. 489 * 490 * @param name <code>$cflow</code> name. It can include 491 * alphabets, numbers, <code>_</code>, 492 * <code>$</code>, and <code>.</code> (dot). 493 * 494 * @see javassist.runtime.Cflow 495 */ 496 public void useCflow(String name) throws CannotCompileException { 497 CtClass cc = declaringClass; 498 cc.checkModify(); 499 ClassPool pool = cc.getClassPool(); 500 String fname; 501 int i = 0; 502 while (true) { 503 fname = "_cflow$" + i++; 504 try { 505 cc.getDeclaredField(fname); 506 } 507 catch(NotFoundException e) { 508 break; 509 } 510 } 511 512 pool.recordCflow(name, declaringClass.getName(), fname); 513 try { 514 CtClass type = pool.get("javassist.runtime.Cflow"); 515 CtField field = new CtField(type, fname, cc); 516 field.setModifiers(Modifier.PUBLIC | Modifier.STATIC); 517 cc.addField(field, CtField.Initializer.byNew(type)); 518 insertBefore(fname + ".enter();", false); 519 String src = fname + ".exit();"; 520 insertAfter(src, true); 521 } 522 catch (NotFoundException e) { 523 throw new CannotCompileException(e); 524 } 525 } 526 527 /** 528 * Declares a new local variable. The scope of this variable is the 529 * whole method body. The initial value of that variable is not set. 530 * The declared variable can be accessed in the code snippet inserted 531 * by <code>insertBefore()</code>, <code>insertAfter()</code>, etc. 532 * 533 * <p>If the second parameter <code>asFinally</code> to 534 * <code>insertAfter()</code> is true, the declared local variable 535 * is not visible from the code inserted by <code>insertAfter()</code>. 536 * 537 * @param name the name of the variable 538 * @param type the type of the variable 539 * @see #insertBefore(String) 540 * @see #insertAfter(String) 541 */ 542 public void addLocalVariable(String name, CtClass type) 543 throws CannotCompileException 544 { 545 declaringClass.checkModify(); 546 ConstPool cp = methodInfo.getConstPool(); 547 CodeAttribute ca = methodInfo.getCodeAttribute(); 548 if (ca == null) 549 throw new CannotCompileException("no method body"); 550 551 LocalVariableAttribute va = (LocalVariableAttribute)ca.getAttribute( 552 LocalVariableAttribute.tag); 553 if (va == null) { 554 va = new LocalVariableAttribute(cp); 555 ca.getAttributes().add(va); 556 } 557 558 int maxLocals = ca.getMaxLocals(); 559 String desc = Descriptor.of(type); 560 va.addEntry(0, ca.getCodeLength(), 561 cp.addUtf8Info(name), cp.addUtf8Info(desc), maxLocals); 562 ca.setMaxLocals(maxLocals + Descriptor.dataSize(desc)); 563 } 564 565 /** 566 * Inserts a new parameter, which becomes the first parameter. 567 */ 568 public void insertParameter(CtClass type) 569 throws CannotCompileException 570 { 571 declaringClass.checkModify(); 572 String desc = methodInfo.getDescriptor(); 573 String desc2 = Descriptor.insertParameter(type, desc); 574 try { 575 addParameter2(Modifier.isStatic(getModifiers()) ? 0 : 1, type, desc); 576 } 577 catch (BadBytecode e) { 578 throw new CannotCompileException(e); 579 } 580 581 methodInfo.setDescriptor(desc2); 582 } 583 584 /** 585 * Appends a new parameter, which becomes the last parameter. 586 */ 587 public void addParameter(CtClass type) 588 throws CannotCompileException 589 { 590 declaringClass.checkModify(); 591 String desc = methodInfo.getDescriptor(); 592 String desc2 = Descriptor.appendParameter(type, desc); 593 int offset = Modifier.isStatic(getModifiers()) ? 0 : 1; 594 try { 595 addParameter2(offset + Descriptor.paramSize(desc), type, desc); 596 } 597 catch (BadBytecode e) { 598 throw new CannotCompileException(e); 599 } 600 601 methodInfo.setDescriptor(desc2); 602 } 603 604 private void addParameter2(int where, CtClass type, String desc) 605 throws BadBytecode 606 { 607 CodeAttribute ca = methodInfo.getCodeAttribute(); 608 if (ca != null) { 609 int size = 1; 610 char typeDesc = 'L'; 611 int classInfo = 0; 612 if (type.isPrimitive()) { 613 CtPrimitiveType cpt = (CtPrimitiveType)type; 614 size = cpt.getDataSize(); 615 typeDesc = cpt.getDescriptor(); 616 } 617 else 618 classInfo = methodInfo.getConstPool().addClassInfo(type); 619 620 ca.insertLocalVar(where, size); 621 LocalVariableAttribute va 622 = (LocalVariableAttribute) 623 ca.getAttribute(LocalVariableAttribute.tag); 624 if (va != null) 625 va.shiftIndex(where, size); 626 627 StackMapTable smt = (StackMapTable)ca.getAttribute(StackMapTable.tag); 628 if (smt != null) 629 smt.insertLocal(where, StackMapTable.typeTagOf(typeDesc), classInfo); 630 631 StackMap sm = (StackMap)ca.getAttribute(StackMap.tag); 632 if (sm != null) 633 sm.insertLocal(where, StackMapTable.typeTagOf(typeDesc), classInfo); 634 } 635 } 636 637 /** 638 * Modifies the method/constructor body. 639 * 640 * @param converter specifies how to modify. 641 */ 642 public void instrument(CodeConverter converter) 643 throws CannotCompileException 644 { 645 declaringClass.checkModify(); 646 ConstPool cp = methodInfo.getConstPool(); 647 converter.doit(getDeclaringClass(), methodInfo, cp); 648 } 649 650 /** 651 * Modifies the method/constructor body. 652 * 653 * @param editor specifies how to modify. 654 */ 655 public void instrument(ExprEditor editor) 656 throws CannotCompileException 657 { 658 // if the class is not frozen, 659 // does not turn the modified flag on. 660 if (declaringClass.isFrozen()) 661 declaringClass.checkModify(); 662 663 if (editor.doit(declaringClass, methodInfo)) 664 declaringClass.checkModify(); 665 } 666 667 /** 668 * Inserts bytecode at the beginning of the body. 669 * 670 * <p>If this object represents a constructor, 671 * the bytecode is inserted before 672 * a constructor in the super class or this class is called. 673 * Therefore, the inserted bytecode is subject to constraints described 674 * in Section 4.8.2 of The Java Virtual Machine Specification (2nd ed). 675 * For example, it cannot access instance fields or methods although 676 * it may assign a value to an instance field directly declared in this 677 * class. Accessing static fields and methods is allowed. 678 * Use <code>insertBeforeBody()</code> in <code>CtConstructor</code>. 679 * 680 * @param src the source code representing the inserted bytecode. 681 * It must be a single statement or block. 682 * @see CtConstructor#insertBeforeBody(String) 683 */ 684 public void insertBefore(String src) throws CannotCompileException { 685 insertBefore(src, true); 686 } 687 688 private void insertBefore(String src, boolean rebuild) 689 throws CannotCompileException 690 { 691 CtClass cc = declaringClass; 692 cc.checkModify(); 693 CodeAttribute ca = methodInfo.getCodeAttribute(); 694 if (ca == null) 695 throw new CannotCompileException("no method body"); 696 697 CodeIterator iterator = ca.iterator(); 698 Javac jv = new Javac(cc); 699 try { 700 int nvars = jv.recordParams(getParameterTypes(), 701 Modifier.isStatic(getModifiers())); 702 jv.recordParamNames(ca, nvars); 703 jv.recordLocalVariables(ca, 0); 704 jv.recordType(getReturnType0()); 705 jv.compileStmnt(src); 706 Bytecode b = jv.getBytecode(); 707 int stack = b.getMaxStack(); 708 int locals = b.getMaxLocals(); 709 710 if (stack > ca.getMaxStack()) 711 ca.setMaxStack(stack); 712 713 if (locals > ca.getMaxLocals()) 714 ca.setMaxLocals(locals); 715 716 int pos = iterator.insertEx(b.get()); 717 iterator.insert(b.getExceptionTable(), pos); 718 if (rebuild) 719 methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2()); 720 } 721 catch (NotFoundException e) { 722 throw new CannotCompileException(e); 723 } 724 catch (CompileError e) { 725 throw new CannotCompileException(e); 726 } 727 catch (BadBytecode e) { 728 throw new CannotCompileException(e); 729 } 730 } 731 732 /** 733 * Inserts bytecode at the end of the body. 734 * The bytecode is inserted just before every return insturction. 735 * It is not executed when an exception is thrown. 736 * 737 * @param src the source code representing the inserted bytecode. 738 * It must be a single statement or block. 739 */ 740 public void insertAfter(String src) 741 throws CannotCompileException 742 { 743 insertAfter(src, false); 744 } 745 746 /** 747 * Inserts bytecode at the end of the body. 748 * The bytecode is inserted just before every return insturction. 749 * 750 * @param src the source code representing the inserted bytecode. 751 * It must be a single statement or block. 752 * @param asFinally true if the inserted bytecode is executed 753 * not only when the control normally returns 754 * but also when an exception is thrown. 755 * If this parameter is true, the inserted code cannot 756 * access local variables. 757 */ 758 public void insertAfter(String src, boolean asFinally) 759 throws CannotCompileException 760 { 761 CtClass cc = declaringClass; 762 cc.checkModify(); 763 ConstPool pool = methodInfo.getConstPool(); 764 CodeAttribute ca = methodInfo.getCodeAttribute(); 765 if (ca == null) 766 throw new CannotCompileException("no method body"); 767 768 CodeIterator iterator = ca.iterator(); 769 int retAddr = ca.getMaxLocals(); 770 Bytecode b = new Bytecode(pool, 0, retAddr + 1); 771 b.setStackDepth(ca.getMaxStack() + 1); 772 Javac jv = new Javac(b, cc); 773 try { 774 int nvars = jv.recordParams(getParameterTypes(), 775 Modifier.isStatic(getModifiers())); 776 jv.recordParamNames(ca, nvars); 777 CtClass rtype = getReturnType0(); 778 int varNo = jv.recordReturnType(rtype, true); 779 jv.recordLocalVariables(ca, 0); 780 781 // finally clause for exceptions 782 int handlerLen = insertAfterHandler(asFinally, b, rtype, varNo, 783 jv, src); 784 // finally clause for normal termination 785 insertAfterAdvice(b, jv, src, pool, rtype, varNo); 786 787 ca.setMaxStack(b.getMaxStack()); 788 ca.setMaxLocals(b.getMaxLocals()); 789 790 int gapPos = iterator.append(b.get()); 791 iterator.append(b.getExceptionTable(), gapPos); 792 793 if (asFinally) 794 ca.getExceptionTable().add(getStartPosOfBody(ca), gapPos, gapPos, 0); 795 796 int gapLen = iterator.getCodeLength() - gapPos - handlerLen; 797 int subr = iterator.getCodeLength() - gapLen; 798 799 while (iterator.hasNext()) { 800 int pos = iterator.next(); 801 if (pos >= subr) 802 break; 803 804 int c = iterator.byteAt(pos); 805 if (c == Opcode.ARETURN || c == Opcode.IRETURN 806 || c == Opcode.FRETURN || c == Opcode.LRETURN 807 || c == Opcode.DRETURN || c == Opcode.RETURN) { 808 insertGoto(iterator, subr, pos); 809 subr = iterator.getCodeLength() - gapLen; 810 } 811 } 812 813 methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2()); 814 } 815 catch (NotFoundException e) { 816 throw new CannotCompileException(e); 817 } 818 catch (CompileError e) { 819 throw new CannotCompileException(e); 820 } 821 catch (BadBytecode e) { 822 throw new CannotCompileException(e); 823 } 824 } 825 826 private void insertAfterAdvice(Bytecode code, Javac jv, String src, 827 ConstPool cp, CtClass rtype, int varNo) 828 throws CompileError 829 { 830 if (rtype == CtClass.voidType) { 831 code.addOpcode(Opcode.ACONST_NULL); 832 code.addAstore(varNo); 833 jv.compileStmnt(src); 834 code.addOpcode(Opcode.RETURN); 835 if (code.getMaxLocals() < 1) 836 code.setMaxLocals(1); 837 } 838 else { 839 code.addStore(varNo, rtype); 840 jv.compileStmnt(src); 841 code.addLoad(varNo, rtype); 842 if (rtype.isPrimitive()) 843 code.addOpcode(((CtPrimitiveType)rtype).getReturnOp()); 844 else 845 code.addOpcode(Opcode.ARETURN); 846 } 847 } 848 849 /* 850 * assert subr > pos 851 */ 852 private void insertGoto(CodeIterator iterator, int subr, int pos) 853 throws BadBytecode 854 { 855 iterator.setMark(subr); 856 // the gap length might be a multiple of 4. 857 iterator.writeByte(Opcode.NOP, pos); 858 boolean wide = subr + 2 - pos > Short.MAX_VALUE; 859 pos = iterator.insertGapAt(pos, wide ? 4 : 2, false).position; 860 int offset = iterator.getMark() - pos; 861 if (wide) { 862 iterator.writeByte(Opcode.GOTO_W, pos); 863 iterator.write32bit(offset, pos + 1); 864 } 865 else if (offset <= Short.MAX_VALUE) { 866 iterator.writeByte(Opcode.GOTO, pos); 867 iterator.write16bit(offset, pos + 1); 868 } 869 else { 870 pos = iterator.insertGapAt(pos, 2, false).position; 871 iterator.writeByte(Opcode.GOTO_W, pos); 872 iterator.write32bit(iterator.getMark() - pos, pos + 1); 873 } 874 } 875 876 /* insert a finally clause 877 */ 878 private int insertAfterHandler(boolean asFinally, Bytecode b, 879 CtClass rtype, int returnVarNo, 880 Javac javac, String src) 881 throws CompileError 882 { 883 if (!asFinally) 884 return 0; 885 886 int var = b.getMaxLocals(); 887 b.incMaxLocals(1); 888 int pc = b.currentPc(); 889 b.addAstore(var); // store an exception 890 if (rtype.isPrimitive()) { 891 char c = ((CtPrimitiveType)rtype).getDescriptor(); 892 if (c == 'D') { 893 b.addDconst(0.0); 894 b.addDstore(returnVarNo); 895 } 896 else if (c == 'F') { 897 b.addFconst(0); 898 b.addFstore(returnVarNo); 899 } 900 else if (c == 'J') { 901 b.addLconst(0); 902 b.addLstore(returnVarNo); 903 } 904 else if (c == 'V') { 905 b.addOpcode(Opcode.ACONST_NULL); 906 b.addAstore(returnVarNo); 907 } 908 else { // int, boolean, char, short, ... 909 b.addIconst(0); 910 b.addIstore(returnVarNo); 911 } 912 } 913 else { 914 b.addOpcode(Opcode.ACONST_NULL); 915 b.addAstore(returnVarNo); 916 } 917 918 javac.compileStmnt(src); 919 b.addAload(var); 920 b.addOpcode(Opcode.ATHROW); 921 return b.currentPc() - pc; 922 } 923 924 /* -- OLD version -- 925 926 public void insertAfter(String src) throws CannotCompileException { 927 declaringClass.checkModify(); 928 CodeAttribute ca = methodInfo.getCodeAttribute(); 929 CodeIterator iterator = ca.iterator(); 930 Bytecode b = new Bytecode(methodInfo.getConstPool(), 931 ca.getMaxStack(), ca.getMaxLocals()); 932 b.setStackDepth(ca.getMaxStack()); 933 Javac jv = new Javac(b, declaringClass); 934 try { 935 jv.recordParams(getParameterTypes(), 936 Modifier.isStatic(getModifiers())); 937 CtClass rtype = getReturnType0(); 938 int varNo = jv.recordReturnType(rtype, true); 939 boolean isVoid = rtype == CtClass.voidType; 940 if (isVoid) { 941 b.addOpcode(Opcode.ACONST_NULL); 942 b.addAstore(varNo); 943 jv.compileStmnt(src); 944 } 945 else { 946 b.addStore(varNo, rtype); 947 jv.compileStmnt(src); 948 b.addLoad(varNo, rtype); 949 } 950 951 byte[] code = b.get(); 952 ca.setMaxStack(b.getMaxStack()); 953 ca.setMaxLocals(b.getMaxLocals()); 954 while (iterator.hasNext()) { 955 int pos = iterator.next(); 956 int c = iterator.byteAt(pos); 957 if (c == Opcode.ARETURN || c == Opcode.IRETURN 958 || c == Opcode.FRETURN || c == Opcode.LRETURN 959 || c == Opcode.DRETURN || c == Opcode.RETURN) 960 iterator.insert(pos, code); 961 } 962 } 963 catch (NotFoundException e) { 964 throw new CannotCompileException(e); 965 } 966 catch (CompileError e) { 967 throw new CannotCompileException(e); 968 } 969 catch (BadBytecode e) { 970 throw new CannotCompileException(e); 971 } 972 } 973 */ 974 975 /** 976 * Adds a catch clause that handles an exception thrown in the 977 * body. The catch clause must end with a return or throw statement. 978 * 979 * @param src the source code representing the catch clause. 980 * It must be a single statement or block. 981 * @param exceptionType the type of the exception handled by the 982 * catch clause. 983 */ 984 public void addCatch(String src, CtClass exceptionType) 985 throws CannotCompileException 986 { 987 addCatch(src, exceptionType, "$e"); 988 } 989 990 /** 991 * Adds a catch clause that handles an exception thrown in the 992 * body. The catch clause must end with a return or throw statement. 993 * 994 * @param src the source code representing the catch clause. 995 * It must be a single statement or block. 996 * @param exceptionType the type of the exception handled by the 997 * catch clause. 998 * @param exceptionName the name of the variable containing the 999 * caught exception, for example, 1000 * <code>$e</code>. 1001 */ 1002 public void addCatch(String src, CtClass exceptionType, 1003 String exceptionName) 1004 throws CannotCompileException 1005 { 1006 CtClass cc = declaringClass; 1007 cc.checkModify(); 1008 ConstPool cp = methodInfo.getConstPool(); 1009 CodeAttribute ca = methodInfo.getCodeAttribute(); 1010 CodeIterator iterator = ca.iterator(); 1011 Bytecode b = new Bytecode(cp, ca.getMaxStack(), ca.getMaxLocals()); 1012 b.setStackDepth(1); 1013 Javac jv = new Javac(b, cc); 1014 try { 1015 jv.recordParams(getParameterTypes(), 1016 Modifier.isStatic(getModifiers())); 1017 int var = jv.recordVariable(exceptionType, exceptionName); 1018 b.addAstore(var); 1019 jv.compileStmnt(src); 1020 1021 int stack = b.getMaxStack(); 1022 int locals = b.getMaxLocals(); 1023 1024 if (stack > ca.getMaxStack()) 1025 ca.setMaxStack(stack); 1026 1027 if (locals > ca.getMaxLocals()) 1028 ca.setMaxLocals(locals); 1029 1030 int len = iterator.getCodeLength(); 1031 int pos = iterator.append(b.get()); 1032 ca.getExceptionTable().add(getStartPosOfBody(ca), len, len, 1033 cp.addClassInfo(exceptionType)); 1034 iterator.append(b.getExceptionTable(), pos); 1035 methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2()); 1036 } 1037 catch (NotFoundException e) { 1038 throw new CannotCompileException(e); 1039 } 1040 catch (CompileError e) { 1041 throw new CannotCompileException(e); 1042 } catch (BadBytecode e) { 1043 throw new CannotCompileException(e); 1044 } 1045 } 1046 1047 /* CtConstructor overrides this method. 1048 */ 1049 int getStartPosOfBody(CodeAttribute ca) throws CannotCompileException { 1050 return 0; 1051 } 1052 1053 /** 1054 * Inserts bytecode at the specified line in the body. 1055 * It is equivalent to: 1056 * 1057 * <br><code>insertAt(lineNum, true, src)</code> 1058 * 1059 * <br>See this method as well. 1060 * 1061 * @param lineNum the line number. The bytecode is inserted at the 1062 * beginning of the code at the line specified by this 1063 * line number. 1064 * @param src the source code representing the inserted bytecode. 1065 * It must be a single statement or block. 1066 * @return the line number at which the bytecode has been inserted. 1067 * 1068 * @see CtBehavior#insertAt(int,boolean,String) 1069 */ 1070 public int insertAt(int lineNum, String src) 1071 throws CannotCompileException 1072 { 1073 return insertAt(lineNum, true, src); 1074 } 1075 1076 /** 1077 * Inserts bytecode at the specified line in the body. 1078 * 1079 * <p>If there is not 1080 * a statement at the specified line, the bytecode might be inserted 1081 * at the line including the first statement after that line specified. 1082 * For example, if there is only a closing brace at that line, the 1083 * bytecode would be inserted at another line below. 1084 * To know exactly where the bytecode will be inserted, call with 1085 * <code>modify</code> set to <code>false</code>. 1086 * 1087 * @param lineNum the line number. The bytecode is inserted at the 1088 * beginning of the code at the line specified by this 1089 * line number. 1090 * @param modify if false, this method does not insert the bytecode. 1091 * It instead only returns the line number at which 1092 * the bytecode would be inserted. 1093 * @param src the source code representing the inserted bytecode. 1094 * It must be a single statement or block. 1095 * If modify is false, the value of src can be null. 1096 * @return the line number at which the bytecode has been inserted. 1097 */ 1098 public int insertAt(int lineNum, boolean modify, String src) 1099 throws CannotCompileException 1100 { 1101 CodeAttribute ca = methodInfo.getCodeAttribute(); 1102 if (ca == null) 1103 throw new CannotCompileException("no method body"); 1104 1105 LineNumberAttribute ainfo 1106 = (LineNumberAttribute)ca.getAttribute(LineNumberAttribute.tag); 1107 if (ainfo == null) 1108 throw new CannotCompileException("no line number info"); 1109 1110 LineNumberAttribute.Pc pc = ainfo.toNearPc(lineNum); 1111 lineNum = pc.line; 1112 int index = pc.index; 1113 if (!modify) 1114 return lineNum; 1115 1116 CtClass cc = declaringClass; 1117 cc.checkModify(); 1118 CodeIterator iterator = ca.iterator(); 1119 Javac jv = new Javac(cc); 1120 try { 1121 jv.recordLocalVariables(ca, index); 1122 jv.recordParams(getParameterTypes(), 1123 Modifier.isStatic(getModifiers())); 1124 jv.setMaxLocals(ca.getMaxLocals()); 1125 jv.compileStmnt(src); 1126 Bytecode b = jv.getBytecode(); 1127 int locals = b.getMaxLocals(); 1128 int stack = b.getMaxStack(); 1129 ca.setMaxLocals(locals); 1130 1131 /* We assume that there is no values in the operand stack 1132 * at the position where the bytecode is inserted. 1133 */ 1134 if (stack > ca.getMaxStack()) 1135 ca.setMaxStack(stack); 1136 1137 index = iterator.insertAt(index, b.get()); 1138 iterator.insert(b.getExceptionTable(), index); 1139 methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2()); 1140 return lineNum; 1141 } 1142 catch (NotFoundException e) { 1143 throw new CannotCompileException(e); 1144 } 1145 catch (CompileError e) { 1146 throw new CannotCompileException(e); 1147 } 1148 catch (BadBytecode e) { 1149 throw new CannotCompileException(e); 1150 } 1151 } 1152} 1153