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