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 16 package javassist; 17 18 import javassist.bytecode.*; 19 import javassist.compiler.Javac; 20 import javassist.compiler.CompileError; 21 import 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 */ 29 public abstract class CtBehavior extends CtMember { 30 protected MethodInfo methodInfo; 31 CtBehavior(CtClass clazz, MethodInfo minfo)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 */ copy(CtBehavior src, boolean isCons, ClassMap map)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 extendToString(StringBuffer buffer)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 */ getLongName()91 public abstract String getLongName(); 92 93 /** 94 * Returns the MethodInfo representing this method/constructor in the 95 * class file. 96 */ getMethodInfo()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 */ getMethodInfo2()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 */ getModifiers()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 */ setModifiers(int mod)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 */ hasAnnotation(Class clz)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 */ getAnnotation(Class clz)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 */ getAnnotations()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 */ getAvailableAnnotations()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 getAnnotations(boolean ignoreNotFound)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 */ getParameterAnnotations()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 */ getAvailableParameterAnnotations()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 getParameterAnnotations(boolean ignoreNotFound)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 */ getParameterTypes()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 */ getReturnType0()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 */ getSignature()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 */ getExceptionTypes()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 */ setExceptionTypes(CtClass[] types)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 */ isEmpty()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 */ setBody(String src)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 */ setBody(String src, String delegateObj, String delegateMethod)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 setBody0(CtClass srcClass, MethodInfo srcInfo, CtClass destClass, MethodInfo destInfo, ClassMap map)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 */ getAttribute(String name)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 */ setAttribute(String name, byte[] data)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 */ useCflow(String name)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 */ addLocalVariable(String name, CtClass type)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 */ insertParameter(CtClass type)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 */ addParameter(CtClass type)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 addParameter2(int where, CtClass type, String desc)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 */ instrument(CodeConverter converter)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 */ instrument(ExprEditor editor)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 */ insertBefore(String src)684 public void insertBefore(String src) throws CannotCompileException { 685 insertBefore(src, true); 686 } 687 insertBefore(String src, boolean rebuild)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 */ insertAfter(String src)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 */ insertAfter(String src, boolean asFinally)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 insertAfterAdvice(Bytecode code, Javac jv, String src, ConstPool cp, CtClass rtype, int varNo)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 */ insertGoto(CodeIterator iterator, int subr, int pos)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 */ insertAfterHandler(boolean asFinally, Bytecode b, CtClass rtype, int returnVarNo, Javac javac, String src)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 */ addCatch(String src, CtClass exceptionType)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 */ addCatch(String src, CtClass exceptionType, String exceptionName)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 */ getStartPosOfBody(CodeAttribute ca)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 */ insertAt(int lineNum, String src)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 */ insertAt(int lineNum, boolean modify, String src)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