Mercurial > lasercutter
view src/clojure/asm/MethodWriter.java @ 10:ef7dbbd6452c
added clojure source goodness
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Sat, 21 Aug 2010 06:25:44 -0400 |
parents | |
children |
line wrap: on
line source
1 /***2 * ASM: a very small and fast Java bytecode manipulation framework3 * Copyright (c) 2000-2005 INRIA, France Telecom4 * All rights reserved.5 *6 * Redistribution and use in source and binary forms, with or without7 * modification, are permitted provided that the following conditions8 * are met:9 * 1. Redistributions of source code must retain the above copyright10 * notice, this list of conditions and the following disclaimer.11 * 2. Redistributions in binary form must reproduce the above copyright12 * notice, this list of conditions and the following disclaimer in the13 * documentation and/or other materials provided with the distribution.14 * 3. Neither the name of the copyright holders nor the names of its15 * contributors may be used to endorse or promote products derived from16 * this software without specific prior written permission.17 *18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF28 * THE POSSIBILITY OF SUCH DAMAGE.29 */30 package clojure.asm;32 /**33 * A {@link MethodVisitor} that generates methods in bytecode form. Each visit34 * method of this class appends the bytecode corresponding to the visited35 * instruction to a byte vector, in the order these methods are called.36 *37 * @author Eric Bruneton38 * @author Eugene Kuleshov39 */40 class MethodWriter implements MethodVisitor{42 /**43 * Pseudo access flag used to denote constructors.44 */45 final static int ACC_CONSTRUCTOR = 262144;47 /**48 * Frame has exactly the same locals as the previous stack map frame and49 * number of stack items is zero.50 */51 final static int SAME_FRAME = 0; // to 63 (0-3f)53 /**54 * Frame has exactly the same locals as the previous stack map frame and55 * number of stack items is 156 */57 final static int SAME_LOCALS_1_STACK_ITEM_FRAME = 64; // to 127 (40-7f)59 /**60 * Reserved for future use61 */62 final static int RESERVED = 128;64 /**65 * Frame has exactly the same locals as the previous stack map frame and66 * number of stack items is 1. Offset is bigger then 63;67 */68 final static int SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED = 247; // f770 /**71 * Frame where current locals are the same as the locals in the previous72 * frame, except that the k last locals are absent. The value of k is given73 * by the formula 251-frame_type.74 */75 final static int CHOP_FRAME = 248; // to 250 (f8-fA)77 /**78 * Frame has exactly the same locals as the previous stack map frame and79 * number of stack items is zero. Offset is bigger then 63;80 */81 final static int SAME_FRAME_EXTENDED = 251; // fb83 /**84 * Frame where current locals are the same as the locals in the previous85 * frame, except that k additional locals are defined. The value of k is86 * given by the formula frame_type-251.87 */88 final static int APPEND_FRAME = 252; // to 254 // fc-fe90 /**91 * Full frame92 */93 final static int FULL_FRAME = 255; // ff95 /**96 * Indicates that the stack map frames must be recomputed from scratch. In97 * this case the maximum stack size and number of local variables is also98 * recomputed from scratch.99 *100 * @see #compute101 */102 private final static int FRAMES = 0;104 /**105 * Indicates that the maximum stack size and number of local variables must106 * be automatically computed.107 *108 * @see #compute109 */110 private final static int MAXS = 1;112 /**113 * Indicates that nothing must be automatically computed.114 *115 * @see #compute116 */117 private final static int NOTHING = 2;119 /**120 * Next method writer (see {@link ClassWriter#firstMethod firstMethod}).121 */122 MethodWriter next;124 /**125 * The class writer to which this method must be added.126 */127 ClassWriter cw;129 /**130 * Access flags of this method.131 */132 private int access;134 /**135 * The index of the constant pool item that contains the name of this136 * method.137 */138 private int name;140 /**141 * The index of the constant pool item that contains the descriptor of this142 * method.143 */144 private int desc;146 /**147 * The descriptor of this method.148 */149 private String descriptor;151 /**152 * The signature of this method.153 */154 String signature;156 /**157 * If not zero, indicates that the code of this method must be copied from158 * the ClassReader associated to this writer in <code>cw.cr</code>. More159 * precisely, this field gives the index of the first byte to copied from160 * <code>cw.cr.b</code>.161 */162 int classReaderOffset;164 /**165 * If not zero, indicates that the code of this method must be copied from166 * the ClassReader associated to this writer in <code>cw.cr</code>. More167 * precisely, this field gives the number of bytes to copied from168 * <code>cw.cr.b</code>.169 */170 int classReaderLength;172 /**173 * Number of exceptions that can be thrown by this method.174 */175 int exceptionCount;177 /**178 * The exceptions that can be thrown by this method. More precisely, this179 * array contains the indexes of the constant pool items that contain the180 * internal names of these exception classes.181 */182 int[] exceptions;184 /**185 * The annotation default attribute of this method. May be <tt>null</tt>.186 */187 private ByteVector annd;189 /**190 * The runtime visible annotations of this method. May be <tt>null</tt>.191 */192 private AnnotationWriter anns;194 /**195 * The runtime invisible annotations of this method. May be <tt>null</tt>.196 */197 private AnnotationWriter ianns;199 /**200 * The runtime visible parameter annotations of this method. May be201 * <tt>null</tt>.202 */203 private AnnotationWriter[] panns;205 /**206 * The runtime invisible parameter annotations of this method. May be207 * <tt>null</tt>.208 */209 private AnnotationWriter[] ipanns;211 /**212 * The non standard attributes of the method.213 */214 private Attribute attrs;216 /**217 * The bytecode of this method.218 */219 private ByteVector code = new ByteVector();221 /**222 * Maximum stack size of this method.223 */224 private int maxStack;226 /**227 * Maximum number of local variables for this method.228 */229 private int maxLocals;231 /**232 * Number of stack map frames in the StackMapTable attribute.233 */234 private int frameCount;236 /**237 * The StackMapTable attribute.238 */239 private ByteVector stackMap;241 /**242 * The offset of the last frame that was written in the StackMapTable243 * attribute.244 */245 private int previousFrameOffset;247 /**248 * The last frame that was written in the StackMapTable attribute.249 *250 * @see #frame251 */252 private int[] previousFrame;254 /**255 * Index of the next element to be added in {@link #frame}.256 */257 private int frameIndex;259 /**260 * The current stack map frame. The first element contains the offset of the261 * instruction to which the frame corresponds, the second element is the262 * number of locals and the third one is the number of stack elements. The263 * local variables start at index 3 and are followed by the operand stack264 * values. In summary frame[0] = offset, frame[1] = nLocal, frame[2] =265 * nStack, frame[3] = nLocal. All types are encoded as integers, with the266 * same format as the one used in {@link Label}, but limited to BASE types.267 */268 private int[] frame;270 /**271 * Number of elements in the exception handler list.272 */273 private int handlerCount;275 /**276 * The first element in the exception handler list.277 */278 private Handler firstHandler;280 /**281 * The last element in the exception handler list.282 */283 private Handler lastHandler;285 /**286 * Number of entries in the LocalVariableTable attribute.287 */288 private int localVarCount;290 /**291 * The LocalVariableTable attribute.292 */293 private ByteVector localVar;295 /**296 * Number of entries in the LocalVariableTypeTable attribute.297 */298 private int localVarTypeCount;300 /**301 * The LocalVariableTypeTable attribute.302 */303 private ByteVector localVarType;305 /**306 * Number of entries in the LineNumberTable attribute.307 */308 private int lineNumberCount;310 /**311 * The LineNumberTable attribute.312 */313 private ByteVector lineNumber;315 /**316 * The non standard attributes of the method's code.317 */318 private Attribute cattrs;320 /**321 * Indicates if some jump instructions are too small and need to be resized.322 */323 private boolean resize;325 /**326 * Indicates if the instructions contain at least one JSR instruction.327 */328 private boolean jsr;330 // ------------------------------------------------------------------------332 /*333 * Fields for the control flow graph analysis algorithm (used to compute the334 * maximum stack size). A control flow graph contains one node per "basic335 * block", and one edge per "jump" from one basic block to another. Each336 * node (i.e., each basic block) is represented by the Label object that337 * corresponds to the first instruction of this basic block. Each node also338 * stores the list of its successors in the graph, as a linked list of Edge339 * objects.340 */342 /**343 * Indicates what must be automatically computed.344 *345 * @see FRAMES346 * @see MAXS347 * @see NOTHING348 */349 private int compute;351 /**352 * A list of labels. This list is the list of basic blocks in the method,353 * i.e. a list of Label objects linked to each other by their354 * {@link Label#successor} field, in the order they are visited by355 * {@link visitLabel}, and starting with the first basic block.356 */357 private Label labels;359 /**360 * The previous basic block.361 */362 private Label previousBlock;364 /**365 * The current basic block.366 */367 private Label currentBlock;369 /**370 * The (relative) stack size after the last visited instruction. This size371 * is relative to the beginning of the current basic block, i.e., the true372 * stack size after the last visited instruction is equal to the373 * {@link Label#inputStackTop beginStackSize} of the current basic block374 * plus <tt>stackSize</tt>.375 */376 private int stackSize;378 /**379 * The (relative) maximum stack size after the last visited instruction.380 * This size is relative to the beginning of the current basic block, i.e.,381 * the true maximum stack size after the last visited instruction is equal382 * to the {@link Label#inputStackTop beginStackSize} of the current basic383 * block plus <tt>stackSize</tt>.384 */385 private int maxStackSize;387 // ------------------------------------------------------------------------388 // Constructor389 // ------------------------------------------------------------------------391 /**392 * Constructs a new {@link MethodWriter}.393 *394 * @param cw the class writer in which the method must be added.395 * @param access the method's access flags (see {@link Opcodes}).396 * @param name the method's name.397 * @param desc the method's descriptor (see {@link Type}).398 * @param signature the method's signature. May be <tt>null</tt>.399 * @param exceptions the internal names of the method's exceptions. May be400 * <tt>null</tt>.401 * @param computeMaxs <tt>true</tt> if the maximum stack size and number402 * of local variables must be automatically computed.403 * @param computeFrames <tt>true</tt> if the stack map tables must be404 * recomputed from scratch.405 */406 MethodWriter(407 final ClassWriter cw,408 final int access,409 final String name,410 final String desc,411 final String signature,412 final String[] exceptions,413 final boolean computeMaxs,414 final boolean computeFrames){415 if(cw.firstMethod == null)416 {417 cw.firstMethod = this;418 }419 else420 {421 cw.lastMethod.next = this;422 }423 cw.lastMethod = this;424 this.cw = cw;425 this.access = access;426 this.name = cw.newUTF8(name);427 this.desc = cw.newUTF8(desc);428 this.descriptor = desc;429 this.signature = signature;430 if(exceptions != null && exceptions.length > 0)431 {432 exceptionCount = exceptions.length;433 this.exceptions = new int[exceptionCount];434 for(int i = 0; i < exceptionCount; ++i)435 {436 this.exceptions[i] = cw.newClass(exceptions[i]);437 }438 }439 this.compute = computeFrames ? FRAMES : (computeMaxs ? MAXS : NOTHING);440 if(computeMaxs || computeFrames)441 {442 if(computeFrames && name.equals("<init>"))443 {444 this.access |= ACC_CONSTRUCTOR;445 }446 // updates maxLocals447 int size = getArgumentsAndReturnSizes(descriptor) >> 2;448 if((access & Opcodes.ACC_STATIC) != 0)449 {450 --size;451 }452 maxLocals = size;453 // creates and visits the label for the first basic block454 labels = new Label();455 labels.status |= Label.PUSHED;456 visitLabel(labels);457 }458 }460 // ------------------------------------------------------------------------461 // Implementation of the MethodVisitor interface462 // ------------------------------------------------------------------------464 public AnnotationVisitor visitAnnotationDefault(){465 annd = new ByteVector();466 return new AnnotationWriter(cw, false, annd, null, 0);467 }469 public AnnotationVisitor visitAnnotation(470 final String desc,471 final boolean visible){472 ByteVector bv = new ByteVector();473 // write type, and reserve space for values count474 bv.putShort(cw.newUTF8(desc)).putShort(0);475 AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, 2);476 if(visible)477 {478 aw.next = anns;479 anns = aw;480 }481 else482 {483 aw.next = ianns;484 ianns = aw;485 }486 return aw;487 }489 public AnnotationVisitor visitParameterAnnotation(490 final int parameter,491 final String desc,492 final boolean visible){493 ByteVector bv = new ByteVector();494 // write type, and reserve space for values count495 bv.putShort(cw.newUTF8(desc)).putShort(0);496 AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, 2);497 if(visible)498 {499 if(panns == null)500 {501 panns = new AnnotationWriter[Type.getArgumentTypes(descriptor).length];502 }503 aw.next = panns[parameter];504 panns[parameter] = aw;505 }506 else507 {508 if(ipanns == null)509 {510 ipanns = new AnnotationWriter[Type.getArgumentTypes(descriptor).length];511 }512 aw.next = ipanns[parameter];513 ipanns[parameter] = aw;514 }515 return aw;516 }518 public void visitAttribute(final Attribute attr){519 if(attr.isCodeAttribute())520 {521 attr.next = cattrs;522 cattrs = attr;523 }524 else525 {526 attr.next = attrs;527 attrs = attr;528 }529 }531 public void visitCode(){532 }534 public void visitFrame(535 final int type,536 final int nLocal,537 final Object[] local,538 final int nStack,539 final Object[] stack){540 if(compute == FRAMES)541 {542 return;543 }545 if(type == Opcodes.F_NEW)546 {547 startFrame(code.length, nLocal, nStack);548 for(int i = 0; i < nLocal; ++i)549 {550 if(local[i] instanceof String)551 {552 frame[frameIndex++] = Frame.OBJECT553 | cw.addType((String) local[i]);554 }555 else if(local[i] instanceof Integer)556 {557 frame[frameIndex++] = ((Integer) local[i]).intValue();558 }559 else560 {561 frame[frameIndex++] = Frame.UNINITIALIZED562 | cw.addUninitializedType("",563 ((Label) local[i]).position);564 }565 }566 for(int i = 0; i < nStack; ++i)567 {568 if(stack[i] instanceof String)569 {570 frame[frameIndex++] = Frame.OBJECT571 | cw.addType((String) stack[i]);572 }573 else if(stack[i] instanceof Integer)574 {575 frame[frameIndex++] = ((Integer) stack[i]).intValue();576 }577 else578 {579 frame[frameIndex++] = Frame.UNINITIALIZED580 | cw.addUninitializedType("",581 ((Label) stack[i]).position);582 }583 }584 endFrame();585 }586 else587 {588 int delta;589 if(stackMap == null)590 {591 stackMap = new ByteVector();592 delta = code.length;593 }594 else595 {596 delta = code.length - previousFrameOffset - 1;597 }599 switch(type)600 {601 case Opcodes.F_FULL:602 stackMap.putByte(FULL_FRAME)603 .putShort(delta)604 .putShort(nLocal);605 for(int i = 0; i < nLocal; ++i)606 {607 writeFrameType(local[i]);608 }609 stackMap.putShort(nStack);610 for(int i = 0; i < nStack; ++i)611 {612 writeFrameType(stack[i]);613 }614 break;615 case Opcodes.F_APPEND:616 stackMap.putByte(SAME_FRAME_EXTENDED + nLocal)617 .putShort(delta);618 for(int i = 0; i < nLocal; ++i)619 {620 writeFrameType(local[i]);621 }622 break;623 case Opcodes.F_CHOP:624 stackMap.putByte(SAME_FRAME_EXTENDED - nLocal)625 .putShort(delta);626 break;627 case Opcodes.F_SAME:628 if(delta < 64)629 {630 stackMap.putByte(delta);631 }632 else633 {634 stackMap.putByte(SAME_FRAME_EXTENDED).putShort(delta);635 }636 break;637 case Opcodes.F_SAME1:638 if(delta < 64)639 {640 stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME + delta);641 }642 else643 {644 stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED)645 .putShort(delta);646 }647 writeFrameType(stack[0]);648 break;649 }651 previousFrameOffset = code.length;652 ++frameCount;653 }654 }656 public void visitInsn(final int opcode){657 // adds the instruction to the bytecode of the method658 code.putByte(opcode);659 // update currentBlock660 // Label currentBlock = this.currentBlock;661 if(currentBlock != null)662 {663 if(compute == FRAMES)664 {665 currentBlock.frame.execute(opcode, 0, null, null);666 }667 else668 {669 // updates current and max stack sizes670 int size = stackSize + Frame.SIZE[opcode];671 if(size > maxStackSize)672 {673 maxStackSize = size;674 }675 stackSize = size;676 }677 // if opcode == ATHROW or xRETURN, ends current block (no successor)678 if((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN)679 || opcode == Opcodes.ATHROW)680 {681 noSuccessor();682 }683 }684 }686 public void visitIntInsn(final int opcode, final int operand){687 // Label currentBlock = this.currentBlock;688 if(currentBlock != null)689 {690 if(compute == FRAMES)691 {692 currentBlock.frame.execute(opcode, operand, null, null);693 }694 else if(opcode != Opcodes.NEWARRAY)695 {696 // updates current and max stack sizes only for NEWARRAY697 // (stack size variation = 0 for BIPUSH or SIPUSH)698 int size = stackSize + 1;699 if(size > maxStackSize)700 {701 maxStackSize = size;702 }703 stackSize = size;704 }705 }706 // adds the instruction to the bytecode of the method707 if(opcode == Opcodes.SIPUSH)708 {709 code.put12(opcode, operand);710 }711 else712 { // BIPUSH or NEWARRAY713 code.put11(opcode, operand);714 }715 }717 public void visitVarInsn(final int opcode, final int var){718 // Label currentBlock = this.currentBlock;719 if(currentBlock != null)720 {721 if(compute == FRAMES)722 {723 currentBlock.frame.execute(opcode, var, null, null);724 }725 else726 {727 // updates current and max stack sizes728 if(opcode == Opcodes.RET)729 {730 // no stack change, but end of current block (no successor)731 currentBlock.status |= Label.RET;732 // save 'stackSize' here for future use733 // (see {@link #findSubroutineSuccessors})734 currentBlock.inputStackTop = stackSize;735 noSuccessor();736 }737 else738 { // xLOAD or xSTORE739 int size = stackSize + Frame.SIZE[opcode];740 if(size > maxStackSize)741 {742 maxStackSize = size;743 }744 stackSize = size;745 }746 }747 }748 if(compute != NOTHING)749 {750 // updates max locals751 int n;752 if(opcode == Opcodes.LLOAD || opcode == Opcodes.DLOAD753 || opcode == Opcodes.LSTORE || opcode == Opcodes.DSTORE)754 {755 n = var + 2;756 }757 else758 {759 n = var + 1;760 }761 if(n > maxLocals)762 {763 maxLocals = n;764 }765 }766 // adds the instruction to the bytecode of the method767 if(var < 4 && opcode != Opcodes.RET)768 {769 int opt;770 if(opcode < Opcodes.ISTORE)771 {772 /* ILOAD_0 */773 opt = 26 + ((opcode - Opcodes.ILOAD) << 2) + var;774 }775 else776 {777 /* ISTORE_0 */778 opt = 59 + ((opcode - Opcodes.ISTORE) << 2) + var;779 }780 code.putByte(opt);781 }782 else if(var >= 256)783 {784 code.putByte(196 /* WIDE */).put12(opcode, var);785 }786 else787 {788 code.put11(opcode, var);789 }790 if(opcode >= Opcodes.ISTORE && compute == FRAMES && handlerCount > 0)791 {792 visitLabel(new Label());793 }794 }796 public void visitTypeInsn(final int opcode, final String desc){797 Item i = cw.newClassItem(desc);798 // Label currentBlock = this.currentBlock;799 if(currentBlock != null)800 {801 if(compute == FRAMES)802 {803 currentBlock.frame.execute(opcode, code.length, cw, i);804 }805 else if(opcode == Opcodes.NEW)806 {807 // updates current and max stack sizes only if opcode == NEW808 // (no stack change for ANEWARRAY, CHECKCAST, INSTANCEOF)809 int size = stackSize + 1;810 if(size > maxStackSize)811 {812 maxStackSize = size;813 }814 stackSize = size;815 }816 }817 // adds the instruction to the bytecode of the method818 code.put12(opcode, i.index);819 }821 public void visitFieldInsn(822 final int opcode,823 final String owner,824 final String name,825 final String desc){826 Item i = cw.newFieldItem(owner, name, desc);827 // Label currentBlock = this.currentBlock;828 if(currentBlock != null)829 {830 if(compute == FRAMES)831 {832 currentBlock.frame.execute(opcode, 0, cw, i);833 }834 else835 {836 int size;837 // computes the stack size variation838 char c = desc.charAt(0);839 switch(opcode)840 {841 case Opcodes.GETSTATIC:842 size = stackSize + (c == 'D' || c == 'J' ? 2 : 1);843 break;844 case Opcodes.PUTSTATIC:845 size = stackSize + (c == 'D' || c == 'J' ? -2 : -1);846 break;847 case Opcodes.GETFIELD:848 size = stackSize + (c == 'D' || c == 'J' ? 1 : 0);849 break;850 // case Constants.PUTFIELD:851 default:852 size = stackSize + (c == 'D' || c == 'J' ? -3 : -2);853 break;854 }855 // updates current and max stack sizes856 if(size > maxStackSize)857 {858 maxStackSize = size;859 }860 stackSize = size;861 }862 }863 // adds the instruction to the bytecode of the method864 code.put12(opcode, i.index);865 }867 public void visitMethodInsn(868 final int opcode,869 final String owner,870 final String name,871 final String desc){872 boolean itf = opcode == Opcodes.INVOKEINTERFACE;873 Item i = cw.newMethodItem(owner, name, desc, itf);874 int argSize = i.intVal;875 // Label currentBlock = this.currentBlock;876 if(currentBlock != null)877 {878 if(compute == FRAMES)879 {880 currentBlock.frame.execute(opcode, 0, cw, i);881 }882 else883 {884 /*885 * computes the stack size variation. In order not to recompute886 * several times this variation for the same Item, we use the887 * intVal field of this item to store this variation, once it888 * has been computed. More precisely this intVal field stores889 * the sizes of the arguments and of the return value890 * corresponding to desc.891 */892 if(argSize == 0)893 {894 // the above sizes have not been computed yet,895 // so we compute them...896 argSize = getArgumentsAndReturnSizes(desc);897 // ... and we save them in order898 // not to recompute them in the future899 i.intVal = argSize;900 }901 int size;902 if(opcode == Opcodes.INVOKESTATIC)903 {904 size = stackSize - (argSize >> 2) + (argSize & 0x03) + 1;905 }906 else907 {908 size = stackSize - (argSize >> 2) + (argSize & 0x03);909 }910 // updates current and max stack sizes911 if(size > maxStackSize)912 {913 maxStackSize = size;914 }915 stackSize = size;916 }917 }918 // adds the instruction to the bytecode of the method919 if(itf)920 {921 if(argSize == 0)922 {923 argSize = getArgumentsAndReturnSizes(desc);924 i.intVal = argSize;925 }926 code.put12(Opcodes.INVOKEINTERFACE, i.index).put11(argSize >> 2, 0);927 }928 else929 {930 code.put12(opcode, i.index);931 }932 }934 public void visitJumpInsn(final int opcode, final Label label){935 Label nextInsn = null;936 // Label currentBlock = this.currentBlock;937 if(currentBlock != null)938 {939 if(compute == FRAMES)940 {941 currentBlock.frame.execute(opcode, 0, null, null);942 // 'label' is the target of a jump instruction943 label.getFirst().status |= Label.TARGET;944 // adds 'label' as a successor of this basic block945 addSuccessor(Edge.NORMAL, label);946 if(opcode != Opcodes.GOTO)947 {948 // creates a Label for the next basic block949 nextInsn = new Label();950 }951 }952 else953 {954 if(opcode == Opcodes.JSR)955 {956 jsr = true;957 currentBlock.status |= Label.JSR;958 addSuccessor(stackSize + 1, label);959 // creates a Label for the next basic block960 nextInsn = new Label();961 /*962 * note that, by construction in this method, a JSR block963 * has at least two successors in the control flow graph:964 * the first one leads the next instruction after the JSR,965 * while the second one leads to the JSR target.966 */967 }968 else969 {970 // updates current stack size (max stack size unchanged971 // because stack size variation always negative in this972 // case)973 stackSize += Frame.SIZE[opcode];974 addSuccessor(stackSize, label);975 }976 }977 }978 // adds the instruction to the bytecode of the method979 if((label.status & Label.RESOLVED) != 0980 && label.position - code.length < Short.MIN_VALUE)981 {982 /*983 * case of a backward jump with an offset < -32768. In this case we984 * automatically replace GOTO with GOTO_W, JSR with JSR_W and IFxxx985 * <l> with IFNOTxxx <l'> GOTO_W <l>, where IFNOTxxx is the986 * "opposite" opcode of IFxxx (i.e., IFNE for IFEQ) and where <l'>987 * designates the instruction just after the GOTO_W.988 */989 if(opcode == Opcodes.GOTO)990 {991 code.putByte(200); // GOTO_W992 }993 else if(opcode == Opcodes.JSR)994 {995 code.putByte(201); // JSR_W996 }997 else998 {999 // if the IF instruction is transformed into IFNOT GOTO_W the1000 // next instruction becomes the target of the IFNOT instruction1001 if(nextInsn != null)1002 {1003 nextInsn.status |= Label.TARGET;1004 }1005 code.putByte(opcode <= 1661006 ? ((opcode + 1) ^ 1) - 11007 : opcode ^ 1);1008 code.putShort(8); // jump offset1009 code.putByte(200); // GOTO_W1010 }1011 label.put(this, code, code.length - 1, true);1012 }1013 else1014 {1015 /*1016 * case of a backward jump with an offset >= -32768, or of a forward1017 * jump with, of course, an unknown offset. In these cases we store1018 * the offset in 2 bytes (which will be increased in1019 * resizeInstructions, if needed).1020 */1021 code.putByte(opcode);1022 label.put(this, code, code.length - 1, false);1023 }1024 if(currentBlock != null)1025 {1026 if(nextInsn != null)1027 {1028 // if the jump instruction is not a GOTO, the next instruction1029 // is also a successor of this instruction. Calling visitLabel1030 // adds the label of this next instruction as a successor of the1031 // current block, and starts a new basic block1032 visitLabel(nextInsn);1033 }1034 if(opcode == Opcodes.GOTO)1035 {1036 noSuccessor();1037 }1038 }1039 }1041 public void visitLabel(final Label label){1042 // resolves previous forward references to label, if any1043 resize |= label.resolve(this, code.length, code.data);1044 // updates currentBlock1045 if((label.status & Label.DEBUG) != 0)1046 {1047 return;1048 }1049 if(compute == FRAMES)1050 {1051 if(currentBlock != null)1052 {1053 if(label.position == currentBlock.position)1054 {1055 // successive labels, do not start a new basic block1056 currentBlock.status |= (label.status & Label.TARGET);1057 label.frame = currentBlock.frame;1058 return;1059 }1060 // ends current block (with one new successor)1061 addSuccessor(Edge.NORMAL, label);1062 }1063 // begins a new current block1064 currentBlock = label;1065 if(label.frame == null)1066 {1067 label.frame = new Frame();1068 label.frame.owner = label;1069 }1070 // updates the basic block list1071 if(previousBlock != null)1072 {1073 if(label.position == previousBlock.position)1074 {1075 previousBlock.status |= (label.status & Label.TARGET);1076 label.frame = previousBlock.frame;1077 currentBlock = previousBlock;1078 return;1079 }1080 previousBlock.successor = label;1081 }1082 previousBlock = label;1083 }1084 else if(compute == MAXS)1085 {1086 if(currentBlock != null)1087 {1088 // ends current block (with one new successor)1089 currentBlock.outputStackMax = maxStackSize;1090 addSuccessor(stackSize, label);1091 }1092 // begins a new current block1093 currentBlock = label;1094 // resets the relative current and max stack sizes1095 stackSize = 0;1096 maxStackSize = 0;1097 // updates the basic block list1098 if(previousBlock != null)1099 {1100 previousBlock.successor = label;1101 }1102 previousBlock = label;1103 }1104 }1106 public void visitLdcInsn(final Object cst){1107 Item i = cw.newConstItem(cst);1108 // Label currentBlock = this.currentBlock;1109 if(currentBlock != null)1110 {1111 if(compute == FRAMES)1112 {1113 currentBlock.frame.execute(Opcodes.LDC, 0, cw, i);1114 }1115 else1116 {1117 int size;1118 // computes the stack size variation1119 if(i.type == ClassWriter.LONG || i.type == ClassWriter.DOUBLE)1120 {1121 size = stackSize + 2;1122 }1123 else1124 {1125 size = stackSize + 1;1126 }1127 // updates current and max stack sizes1128 if(size > maxStackSize)1129 {1130 maxStackSize = size;1131 }1132 stackSize = size;1133 }1134 }1135 // adds the instruction to the bytecode of the method1136 int index = i.index;1137 if(i.type == ClassWriter.LONG || i.type == ClassWriter.DOUBLE)1138 {1139 code.put12(20 /* LDC2_W */, index);1140 }1141 else if(index >= 256)1142 {1143 code.put12(19 /* LDC_W */, index);1144 }1145 else1146 {1147 code.put11(Opcodes.LDC, index);1148 }1149 }1151 public void visitIincInsn(final int var, final int increment){1152 if(currentBlock != null)1153 {1154 if(compute == FRAMES)1155 {1156 currentBlock.frame.execute(Opcodes.IINC, var, null, null);1157 }1158 }1159 if(compute != NOTHING)1160 {1161 // updates max locals1162 int n = var + 1;1163 if(n > maxLocals)1164 {1165 maxLocals = n;1166 }1167 }1168 // adds the instruction to the bytecode of the method1169 if((var > 255) || (increment > 127) || (increment < -128))1170 {1171 code.putByte(196 /* WIDE */)1172 .put12(Opcodes.IINC, var)1173 .putShort(increment);1174 }1175 else1176 {1177 code.putByte(Opcodes.IINC).put11(var, increment);1178 }1179 }1181 public void visitTableSwitchInsn(1182 final int min,1183 final int max,1184 final Label dflt,1185 final Label labels[]){1186 // adds the instruction to the bytecode of the method1187 int source = code.length;1188 code.putByte(Opcodes.TABLESWITCH);1189 code.length += (4 - code.length % 4) % 4;1190 dflt.put(this, code, source, true);1191 code.putInt(min).putInt(max);1192 for(int i = 0; i < labels.length; ++i)1193 {1194 labels[i].put(this, code, source, true);1195 }1196 // updates currentBlock1197 visitSwitchInsn(dflt, labels);1198 }1200 public void visitLookupSwitchInsn(1201 final Label dflt,1202 final int keys[],1203 final Label labels[]){1204 // adds the instruction to the bytecode of the method1205 int source = code.length;1206 code.putByte(Opcodes.LOOKUPSWITCH);1207 code.length += (4 - code.length % 4) % 4;1208 dflt.put(this, code, source, true);1209 code.putInt(labels.length);1210 for(int i = 0; i < labels.length; ++i)1211 {1212 code.putInt(keys[i]);1213 labels[i].put(this, code, source, true);1214 }1215 // updates currentBlock1216 visitSwitchInsn(dflt, labels);1217 }1219 private void visitSwitchInsn(final Label dflt, final Label[] labels){1220 // Label currentBlock = this.currentBlock;1221 if(currentBlock != null)1222 {1223 if(compute == FRAMES)1224 {1225 currentBlock.frame.execute(Opcodes.LOOKUPSWITCH, 0, null, null);1226 // adds current block successors1227 addSuccessor(Edge.NORMAL, dflt);1228 dflt.getFirst().status |= Label.TARGET;1229 for(int i = 0; i < labels.length; ++i)1230 {1231 addSuccessor(Edge.NORMAL, labels[i]);1232 labels[i].getFirst().status |= Label.TARGET;1233 }1234 }1235 else1236 {1237 // updates current stack size (max stack size unchanged)1238 --stackSize;1239 // adds current block successors1240 addSuccessor(stackSize, dflt);1241 for(int i = 0; i < labels.length; ++i)1242 {1243 addSuccessor(stackSize, labels[i]);1244 }1245 }1246 // ends current block1247 noSuccessor();1248 }1249 }1251 public void visitMultiANewArrayInsn(final String desc, final int dims){1252 Item i = cw.newClassItem(desc);1253 // Label currentBlock = this.currentBlock;1254 if(currentBlock != null)1255 {1256 if(compute == FRAMES)1257 {1258 currentBlock.frame.execute(Opcodes.MULTIANEWARRAY, dims, cw, i);1259 }1260 else1261 {1262 // updates current stack size (max stack size unchanged because1263 // stack size variation always negative or null)1264 stackSize += 1 - dims;1265 }1266 }1267 // adds the instruction to the bytecode of the method1268 code.put12(Opcodes.MULTIANEWARRAY, i.index).putByte(dims);1269 }1271 public void visitTryCatchBlock(1272 final Label start,1273 final Label end,1274 final Label handler,1275 final String type){1276 ++handlerCount;1277 Handler h = new Handler();1278 h.start = start;1279 h.end = end;1280 h.handler = handler;1281 h.desc = type;1282 h.type = type != null ? cw.newClass(type) : 0;1283 if(lastHandler == null)1284 {1285 firstHandler = h;1286 }1287 else1288 {1289 lastHandler.next = h;1290 }1291 lastHandler = h;1292 }1294 public void visitLocalVariable(1295 final String name,1296 final String desc,1297 final String signature,1298 final Label start,1299 final Label end,1300 final int index){1301 if(signature != null)1302 {1303 if(localVarType == null)1304 {1305 localVarType = new ByteVector();1306 }1307 ++localVarTypeCount;1308 localVarType.putShort(start.position)1309 .putShort(end.position - start.position)1310 .putShort(cw.newUTF8(name))1311 .putShort(cw.newUTF8(signature))1312 .putShort(index);1313 }1314 if(localVar == null)1315 {1316 localVar = new ByteVector();1317 }1318 ++localVarCount;1319 localVar.putShort(start.position)1320 .putShort(end.position - start.position)1321 .putShort(cw.newUTF8(name))1322 .putShort(cw.newUTF8(desc))1323 .putShort(index);1324 if(compute != NOTHING)1325 {1326 // updates max locals1327 char c = desc.charAt(0);1328 int n = index + (c == 'J' || c == 'D' ? 2 : 1);1329 if(n > maxLocals)1330 {1331 maxLocals = n;1332 }1333 }1334 }1336 public void visitLineNumber(final int line, final Label start){1337 if(lineNumber == null)1338 {1339 lineNumber = new ByteVector();1340 }1341 ++lineNumberCount;1342 lineNumber.putShort(start.position);1343 lineNumber.putShort(line);1344 }1346 public void visitMaxs(final int maxStack, final int maxLocals){1347 if(compute == FRAMES)1348 {1349 // completes the control flow graph with exception handler blocks1350 Handler handler = firstHandler;1351 while(handler != null)1352 {1353 Label l = handler.start.getFirst();1354 Label h = handler.handler.getFirst();1355 Label e = handler.end.getFirst();1356 // computes the kind of the edges to 'h'1357 String t = handler.desc == null1358 ? "java/lang/Throwable"1359 : handler.desc;1360 int kind = Frame.OBJECT | cw.addType(t);1361 // h is an exception handler1362 h.status |= Label.TARGET;1363 // adds 'h' as a successor of labels between 'start' and 'end'1364 while(l != e)1365 {1366 // creates an edge to 'h'1367 Edge b = new Edge();1368 b.info = kind;1369 b.successor = h;1370 // adds it to the successors of 'l'1371 b.next = l.successors;1372 l.successors = b;1373 // goes to the next label1374 l = l.successor;1375 }1376 handler = handler.next;1377 }1379 // creates and visits the first (implicit) frame1380 Frame f = labels.frame;1381 Type[] args = Type.getArgumentTypes(descriptor);1382 f.initInputFrame(cw, access, args, this.maxLocals);1383 visitFrame(f);1385 /*1386 * fix point algorithm: mark the first basic block as 'changed'1387 * (i.e. put it in the 'changed' list) and, while there are changed1388 * basic blocks, choose one, mark it as unchanged, and update its1389 * successors (which can be changed in the process).1390 */1391 int max = 0;1392 Label changed = labels;1393 while(changed != null)1394 {1395 // removes a basic block from the list of changed basic blocks1396 Label l = changed;1397 changed = changed.next;1398 l.next = null;1399 f = l.frame;1400 // a reacheable jump target must be stored in the stack map1401 if((l.status & Label.TARGET) != 0)1402 {1403 l.status |= Label.STORE;1404 }1405 // all visited labels are reacheable, by definition1406 l.status |= Label.REACHABLE;1407 // updates the (absolute) maximum stack size1408 int blockMax = f.inputStack.length + l.outputStackMax;1409 if(blockMax > max)1410 {1411 max = blockMax;1412 }1413 // updates the successors of the current basic block1414 Edge e = l.successors;1415 while(e != null)1416 {1417 Label n = e.successor.getFirst();1418 boolean change = f.merge(cw, n.frame, e.info);1419 if(change && n.next == null)1420 {1421 // if n has changed and is not already in the 'changed'1422 // list, adds it to this list1423 n.next = changed;1424 changed = n;1425 }1426 e = e.next;1427 }1428 }1429 this.maxStack = max;1431 // visits all the frames that must be stored in the stack map1432 Label l = labels;1433 while(l != null)1434 {1435 f = l.frame;1436 if((l.status & Label.STORE) != 0)1437 {1438 visitFrame(f);1439 }1440 if((l.status & Label.REACHABLE) == 0)1441 {1442 // finds start and end of dead basic block1443 Label k = l.successor;1444 int start = l.position;1445 int end = (k == null ? code.length : k.position) - 1;1446 // if non empty basic block1447 if(end >= start)1448 {1449 // replaces instructions with NOP ... NOP ATHROW1450 for(int i = start; i < end; ++i)1451 {1452 code.data[i] = Opcodes.NOP;1453 }1454 code.data[end] = (byte) Opcodes.ATHROW;1455 // emits a frame for this unreachable block1456 startFrame(start, 0, 1);1457 frame[frameIndex++] = Frame.OBJECT1458 | cw.addType("java/lang/Throwable");1459 endFrame();1460 }1461 }1462 l = l.successor;1463 }1464 }1465 else if(compute == MAXS)1466 {1467 // completes the control flow graph with exception handler blocks1468 Handler handler = firstHandler;1469 while(handler != null)1470 {1471 Label l = handler.start;1472 Label h = handler.handler;1473 Label e = handler.end;1474 // adds 'h' as a successor of labels between 'start' and 'end'1475 while(l != e)1476 {1477 // creates an edge to 'h'1478 Edge b = new Edge();1479 b.info = Edge.EXCEPTION;1480 b.successor = h;1481 // adds it to the successors of 'l'1482 if((l.status & Label.JSR) != 0)1483 {1484 // if l is a JSR block, adds b after the first two edges1485 // to preserve the hypothesis about JSR block successors1486 // order (see {@link #visitJumpInsn})1487 b.next = l.successors.next.next;1488 l.successors.next.next = b;1489 }1490 else1491 {1492 b.next = l.successors;1493 l.successors = b;1494 }1495 // goes to the next label1496 l = l.successor;1497 }1498 handler = handler.next;1499 }1501 if(jsr)1502 {1503 // completes the control flow graph with the RET successors1504 /*1505 * first step: finds the subroutines. This step determines, for1506 * each basic block, to which subroutine(s) it belongs, and1507 * stores this set as a bit set in the {@link Label#status}1508 * field. Subroutines are numbered with powers of two, from1509 * 0x1000 to 0x80000000 (so there must be at most 20 subroutines1510 * in a method).1511 */1512 // finds the basic blocks that belong to the "main" subroutine1513 int id = 0x1000;1514 findSubroutine(labels, id);1515 // finds the basic blocks that belong to the real subroutines1516 Label l = labels;1517 while(l != null)1518 {1519 if((l.status & Label.JSR) != 0)1520 {1521 // the subroutine is defined by l's TARGET, not by l1522 Label subroutine = l.successors.next.successor;1523 // if this subroutine does not have an id yet...1524 if((subroutine.status & ~0xFFF) == 0)1525 {1526 // ...assigns it a new id and finds its basic blocks1527 id = id << 1;1528 findSubroutine(subroutine, id);1529 }1530 }1531 l = l.successor;1532 }1533 // second step: finds the successors of RET blocks1534 findSubroutineSuccessors(0x1000, new Label[10], 0);1535 }1537 /*1538 * control flow analysis algorithm: while the block stack is not1539 * empty, pop a block from this stack, update the max stack size,1540 * compute the true (non relative) begin stack size of the1541 * successors of this block, and push these successors onto the1542 * stack (unless they have already been pushed onto the stack).1543 * Note: by hypothesis, the {@link Label#inputStackTop} of the1544 * blocks in the block stack are the true (non relative) beginning1545 * stack sizes of these blocks.1546 */1547 int max = 0;1548 Label stack = labels;1549 while(stack != null)1550 {1551 // pops a block from the stack1552 Label l = stack;1553 stack = stack.next;1554 // computes the true (non relative) max stack size of this block1555 int start = l.inputStackTop;1556 int blockMax = start + l.outputStackMax;1557 // updates the global max stack size1558 if(blockMax > max)1559 {1560 max = blockMax;1561 }1562 // analyses the successors of the block1563 Edge b = l.successors;1564 if((l.status & Label.JSR) != 0)1565 {1566 // ignores the first edge of JSR blocks (virtual successor)1567 b = b.next;1568 }1569 while(b != null)1570 {1571 l = b.successor;1572 // if this successor has not already been pushed...1573 if((l.status & Label.PUSHED) == 0)1574 {1575 // computes its true beginning stack size...1576 l.inputStackTop = b.info == Edge.EXCEPTION ? 1 : start1577 + b.info;1578 // ...and pushes it onto the stack1579 l.status |= Label.PUSHED;1580 l.next = stack;1581 stack = l;1582 }1583 b = b.next;1584 }1585 }1586 this.maxStack = max;1587 }1588 else1589 {1590 this.maxStack = maxStack;1591 this.maxLocals = maxLocals;1592 }1593 }1595 public void visitEnd(){1596 }1598 // ------------------------------------------------------------------------1599 // Utility methods: control flow analysis algorithm1600 // ------------------------------------------------------------------------1602 /**1603 * Computes the size of the arguments and of the return value of a method.1604 *1605 * @param desc the descriptor of a method.1606 * @return the size of the arguments of the method (plus one for the1607 * implicit this argument), argSize, and the size of its return1608 * value, retSize, packed into a single int i =1609 * <tt>(argSize << 2) | retSize</tt> (argSize is therefore equal1610 * to <tt>i >> 2</tt>, and retSize to <tt>i & 0x03</tt>).1611 */1612 static int getArgumentsAndReturnSizes(final String desc){1613 int n = 1;1614 int c = 1;1615 while(true)1616 {1617 char car = desc.charAt(c++);1618 if(car == ')')1619 {1620 car = desc.charAt(c);1621 return n << 21622 | (car == 'V' ? 0 : (car == 'D' || car == 'J' ? 2 : 1));1623 }1624 else if(car == 'L')1625 {1626 while(desc.charAt(c++) != ';')1627 {1628 }1629 n += 1;1630 }1631 else if(car == '[')1632 {1633 while((car = desc.charAt(c)) == '[')1634 {1635 ++c;1636 }1637 if(car == 'D' || car == 'J')1638 {1639 n -= 1;1640 }1641 }1642 else if(car == 'D' || car == 'J')1643 {1644 n += 2;1645 }1646 else1647 {1648 n += 1;1649 }1650 }1651 }1653 /**1654 * Adds a successor to the {@link #currentBlock currentBlock} block.1655 *1656 * @param info information about the control flow edge to be added.1657 * @param successor the successor block to be added to the current block.1658 */1659 private void addSuccessor(final int info, final Label successor){1660 // creates and initializes an Edge object...1661 Edge b = new Edge();1662 b.info = info;1663 b.successor = successor;1664 // ...and adds it to the successor list of the currentBlock block1665 b.next = currentBlock.successors;1666 currentBlock.successors = b;1667 }1669 /**1670 * Ends the current basic block. This method must be used in the case where1671 * the current basic block does not have any successor.1672 */1673 private void noSuccessor(){1674 if(compute == FRAMES)1675 {1676 Label l = new Label();1677 l.frame = new Frame();1678 l.frame.owner = l;1679 l.resolve(this, code.length, code.data);1680 previousBlock.successor = l;1681 previousBlock = l;1682 }1683 else1684 {1685 currentBlock.outputStackMax = maxStackSize;1686 }1687 currentBlock = null;1688 }1690 /**1691 * Finds the basic blocks that belong to a given subroutine, and marks these1692 * blocks as belonging to this subroutine (by using {@link Label#status} as1693 * a bit set (see {@link #visitMaxs}). This recursive method follows the1694 * control flow graph to find all the blocks that are reachable from the1695 * given block WITHOUT following any JSR target.1696 *1697 * @param block a block that belongs to the subroutine1698 * @param id the id of this subroutine1699 */1700 private void findSubroutine(final Label block, final int id){1701 // if 'block' is already marked as belonging to subroutine 'id', returns1702 if((block.status & id) != 0)1703 {1704 return;1705 }1706 // marks 'block' as belonging to subroutine 'id'1707 block.status |= id;1708 // calls this method recursively on each successor, except JSR targets1709 Edge e = block.successors;1710 while(e != null)1711 {1712 // if 'block' is a JSR block, then 'block.successors.next' leads1713 // to the JSR target (see {@link #visitJumpInsn}) and must therefore1714 // not be followed1715 if((block.status & Label.JSR) == 0 || e != block.successors.next)1716 {1717 findSubroutine(e.successor, id);1718 }1719 e = e.next;1720 }1721 }1723 /**1724 * Finds the successors of the RET blocks of the specified subroutine, and1725 * of any nested subroutine it calls.1726 *1727 * @param id id of the subroutine whose RET block successors must be found.1728 * @param JSRs the JSR blocks that were followed to reach this subroutine.1729 * @param nJSRs number of JSR blocks in the JSRs array.1730 */1731 private void findSubroutineSuccessors(1732 final int id,1733 final Label[] JSRs,1734 final int nJSRs){1735 // iterates over all the basic blocks...1736 Label l = labels;1737 while(l != null)1738 {1739 // for those that belong to subroutine 'id'...1740 if((l.status & id) != 0)1741 {1742 if((l.status & Label.JSR) != 0)1743 {1744 // finds the subroutine to which 'l' leads by following the1745 // second edge of l.successors (see {@link #visitJumpInsn})1746 int nId = l.successors.next.successor.status & ~0xFFF;1747 if(nId != id)1748 {1749 // calls this method recursively with l pushed onto the1750 // JSRs stack to find the successors of the RET blocks1751 // of this nested subroutine 'nId'1752 JSRs[nJSRs] = l;1753 findSubroutineSuccessors(nId, JSRs, nJSRs + 1);1754 }1755 }1756 else if((l.status & Label.RET) != 0)1757 {1758 /*1759 * finds the JSR block in the JSRs stack that corresponds to1760 * this RET block, and updates the successors of this RET1761 * block accordingly. This corresponding JSR is the one that1762 * leads to the subroutine to which the RET block belongs.1763 * But the RET block can belong to several subroutines (if a1764 * nested subroutine returns to its parent subroutine1765 * implicitely, without a RET). So, in fact, the JSR that1766 * corresponds to this RET is the first block in the JSRs1767 * stack, starting from the bottom of the stack, that leads1768 * to a subroutine to which the RET block belongs.1769 */1770 for(int i = 0; i < nJSRs; ++i)1771 {1772 int JSRstatus = JSRs[i].successors.next.successor.status;1773 if(((JSRstatus & ~0xFFF) & (l.status & ~0xFFF)) != 0)1774 {1775 Edge e = new Edge();1776 e.info = l.inputStackTop;1777 e.successor = JSRs[i].successors.successor;1778 e.next = l.successors;1779 l.successors = e;1780 break;1781 }1782 }1783 }1784 }1785 l = l.successor;1786 }1787 }1789 // ------------------------------------------------------------------------1790 // Utility methods: stack map frames1791 // ------------------------------------------------------------------------1793 /**1794 * Visits a frame that has been computed from scratch.1795 *1796 * @param f the frame that must be visited.1797 */1798 private void visitFrame(final Frame f){1799 int i, t;1800 int nTop = 0;1801 int nLocal = 0;1802 int nStack = 0;1803 int[] locals = f.inputLocals;1804 int[] stacks = f.inputStack;1805 // computes the number of locals (ignores TOP types that are just after1806 // a LONG or a DOUBLE, and all trailing TOP types)1807 for(i = 0; i < locals.length; ++i)1808 {1809 t = locals[i];1810 if(t == Frame.TOP)1811 {1812 ++nTop;1813 }1814 else1815 {1816 nLocal += nTop + 1;1817 nTop = 0;1818 }1819 if(t == Frame.LONG || t == Frame.DOUBLE)1820 {1821 ++i;1822 }1823 }1824 // computes the stack size (ignores TOP types that are just after1825 // a LONG or a DOUBLE)1826 for(i = 0; i < stacks.length; ++i)1827 {1828 t = stacks[i];1829 ++nStack;1830 if(t == Frame.LONG || t == Frame.DOUBLE)1831 {1832 ++i;1833 }1834 }1835 // visits the frame and its content1836 startFrame(f.owner.position, nLocal, nStack);1837 for(i = 0; nLocal > 0; ++i, --nLocal)1838 {1839 t = locals[i];1840 frame[frameIndex++] = t;1841 if(t == Frame.LONG || t == Frame.DOUBLE)1842 {1843 ++i;1844 }1845 }1846 for(i = 0; i < stacks.length; ++i)1847 {1848 t = stacks[i];1849 frame[frameIndex++] = t;1850 if(t == Frame.LONG || t == Frame.DOUBLE)1851 {1852 ++i;1853 }1854 }1855 endFrame();1856 }1858 /**1859 * Starts the visit of a stack map frame.1860 *1861 * @param offset the offset of the instruction to which the frame1862 * corresponds.1863 * @param nLocal the number of local variables in the frame.1864 * @param nStack the number of stack elements in the frame.1865 */1866 private void startFrame(final int offset, final int nLocal, final int nStack){1867 int n = 3 + nLocal + nStack;1868 if(frame == null || frame.length < n)1869 {1870 frame = new int[n];1871 }1872 frame[0] = offset;1873 frame[1] = nLocal;1874 frame[2] = nStack;1875 frameIndex = 3;1876 }1878 /**1879 * Checks if the visit of the current frame {@link #frame} is finished, and1880 * if yes, write it in the StackMapTable attribute.1881 */1882 private void endFrame(){1883 if(previousFrame != null)1884 { // do not write the first frame1885 if(stackMap == null)1886 {1887 stackMap = new ByteVector();1888 }1889 writeFrame();1890 ++frameCount;1891 }1892 previousFrame = frame;1893 frame = null;1894 }1896 /**1897 * Compress and writes the current frame {@link #frame} in the StackMapTable1898 * attribute.1899 */1900 private void writeFrame(){1901 int clocalsSize = frame[1];1902 int cstackSize = frame[2];1903 if((cw.version & 0xFFFF) < Opcodes.V1_6)1904 {1905 stackMap.putShort(frame[0]).putShort(clocalsSize);1906 writeFrameTypes(3, 3 + clocalsSize);1907 stackMap.putShort(cstackSize);1908 writeFrameTypes(3 + clocalsSize, 3 + clocalsSize + cstackSize);1909 return;1910 }1911 int localsSize = previousFrame[1];1912 int type = FULL_FRAME;1913 int k = 0;1914 int delta;1915 if(frameCount == 0)1916 {1917 delta = frame[0];1918 }1919 else1920 {1921 delta = frame[0] - previousFrame[0] - 1;1922 }1923 if(cstackSize == 0)1924 {1925 k = clocalsSize - localsSize;1926 switch(k)1927 {1928 case-3:1929 case-2:1930 case-1:1931 type = CHOP_FRAME;1932 localsSize = clocalsSize;1933 break;1934 case 0:1935 type = delta < 64 ? SAME_FRAME : SAME_FRAME_EXTENDED;1936 break;1937 case 1:1938 case 2:1939 case 3:1940 type = APPEND_FRAME;1941 break;1942 }1943 }1944 else if(clocalsSize == localsSize && cstackSize == 1)1945 {1946 type = delta < 631947 ? SAME_LOCALS_1_STACK_ITEM_FRAME1948 : SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED;1949 }1950 if(type != FULL_FRAME)1951 {1952 // verify if locals are the same1953 int l = 3;1954 for(int j = 0; j < localsSize; j++)1955 {1956 if(frame[l] != previousFrame[l])1957 {1958 type = FULL_FRAME;1959 break;1960 }1961 l++;1962 }1963 }1964 switch(type)1965 {1966 case SAME_FRAME:1967 stackMap.putByte(delta);1968 break;1969 case SAME_LOCALS_1_STACK_ITEM_FRAME:1970 stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME + delta);1971 writeFrameTypes(3 + clocalsSize, 4 + clocalsSize);1972 break;1973 case SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED:1974 stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED)1975 .putShort(delta);1976 writeFrameTypes(3 + clocalsSize, 4 + clocalsSize);1977 break;1978 case SAME_FRAME_EXTENDED:1979 stackMap.putByte(SAME_FRAME_EXTENDED).putShort(delta);1980 break;1981 case CHOP_FRAME:1982 stackMap.putByte(SAME_FRAME_EXTENDED + k).putShort(delta);1983 break;1984 case APPEND_FRAME:1985 stackMap.putByte(SAME_FRAME_EXTENDED + k).putShort(delta);1986 writeFrameTypes(3 + localsSize, 3 + clocalsSize);1987 break;1988 // case FULL_FRAME:1989 default:1990 stackMap.putByte(FULL_FRAME)1991 .putShort(delta)1992 .putShort(clocalsSize);1993 writeFrameTypes(3, 3 + clocalsSize);1994 stackMap.putShort(cstackSize);1995 writeFrameTypes(3 + clocalsSize, 3 + clocalsSize + cstackSize);1996 }1997 }1999 /**2000 * Writes some types of the current frame {@link #frame} into the2001 * StackMapTableAttribute. This method converts types from the format used2002 * in {@link Label} to the format used in StackMapTable attributes. In2003 * particular, it converts type table indexes to constant pool indexes.2004 *2005 * @param start index of the first type in {@link #frame} to write.2006 * @param end index of last type in {@link #frame} to write (exclusive).2007 */2008 private void writeFrameTypes(final int start, final int end){2009 for(int i = start; i < end; ++i)2010 {2011 int t = frame[i];2012 int d = t & Frame.DIM;2013 if(d == 0)2014 {2015 int v = t & Frame.BASE_VALUE;2016 switch(t & Frame.BASE_KIND)2017 {2018 case Frame.OBJECT:2019 stackMap.putByte(7)2020 .putShort(cw.newClass(cw.typeTable[v].strVal1));2021 break;2022 case Frame.UNINITIALIZED:2023 stackMap.putByte(8).putShort(cw.typeTable[v].intVal);2024 break;2025 default:2026 stackMap.putByte(v);2027 }2028 }2029 else2030 {2031 StringBuffer buf = new StringBuffer();2032 d >>= 28;2033 while(d-- > 0)2034 {2035 buf.append('[');2036 }2037 if((t & Frame.BASE_KIND) == Frame.OBJECT)2038 {2039 buf.append('L');2040 buf.append(cw.typeTable[t & Frame.BASE_VALUE].strVal1);2041 buf.append(';');2042 }2043 else2044 {2045 switch(t & 0xF)2046 {2047 case 1:2048 buf.append('I');2049 break;2050 case 2:2051 buf.append('F');2052 break;2053 case 3:2054 buf.append('D');2055 break;2056 case 9:2057 buf.append('Z');2058 break;2059 case 10:2060 buf.append('B');2061 break;2062 case 11:2063 buf.append('C');2064 break;2065 case 12:2066 buf.append('S');2067 break;2068 default:2069 buf.append('J');2070 }2071 }2072 stackMap.putByte(7).putShort(cw.newClass(buf.toString()));2073 }2074 }2075 }2077 private void writeFrameType(final Object type){2078 if(type instanceof String)2079 {2080 stackMap.putByte(7).putShort(cw.newClass((String) type));2081 }2082 else if(type instanceof Integer)2083 {2084 stackMap.putByte(((Integer) type).intValue());2085 }2086 else2087 {2088 stackMap.putByte(8).putShort(((Label) type).position);2089 }2090 }2092 // ------------------------------------------------------------------------2093 // Utility methods: dump bytecode array2094 // ------------------------------------------------------------------------2096 /**2097 * Returns the size of the bytecode of this method.2098 *2099 * @return the size of the bytecode of this method.2100 */2101 final int getSize(){2102 if(classReaderOffset != 0)2103 {2104 return 6 + classReaderLength;2105 }2106 if(resize)2107 {2108 // replaces the temporary jump opcodes introduced by Label.resolve.2109 resizeInstructions();2110 }2111 int size = 8;2112 if(code.length > 0)2113 {2114 cw.newUTF8("Code");2115 size += 18 + code.length + 8 * handlerCount;2116 if(localVar != null)2117 {2118 cw.newUTF8("LocalVariableTable");2119 size += 8 + localVar.length;2120 }2121 if(localVarType != null)2122 {2123 cw.newUTF8("LocalVariableTypeTable");2124 size += 8 + localVarType.length;2125 }2126 if(lineNumber != null)2127 {2128 cw.newUTF8("LineNumberTable");2129 size += 8 + lineNumber.length;2130 }2131 if(stackMap != null)2132 {2133 boolean zip = (cw.version & 0xFFFF) >= Opcodes.V1_6;2134 cw.newUTF8(zip ? "StackMapTable" : "StackMap");2135 size += 8 + stackMap.length;2136 }2137 if(cattrs != null)2138 {2139 size += cattrs.getSize(cw,2140 code.data,2141 code.length,2142 maxStack,2143 maxLocals);2144 }2145 }2146 if(exceptionCount > 0)2147 {2148 cw.newUTF8("Exceptions");2149 size += 8 + 2 * exceptionCount;2150 }2151 if((access & Opcodes.ACC_SYNTHETIC) != 02152 && (cw.version & 0xffff) < Opcodes.V1_5)2153 {2154 cw.newUTF8("Synthetic");2155 size += 6;2156 }2157 if((access & Opcodes.ACC_DEPRECATED) != 0)2158 {2159 cw.newUTF8("Deprecated");2160 size += 6;2161 }2162 if(signature != null)2163 {2164 cw.newUTF8("Signature");2165 cw.newUTF8(signature);2166 size += 8;2167 }2168 if(annd != null)2169 {2170 cw.newUTF8("AnnotationDefault");2171 size += 6 + annd.length;2172 }2173 if(anns != null)2174 {2175 cw.newUTF8("RuntimeVisibleAnnotations");2176 size += 8 + anns.getSize();2177 }2178 if(ianns != null)2179 {2180 cw.newUTF8("RuntimeInvisibleAnnotations");2181 size += 8 + ianns.getSize();2182 }2183 if(panns != null)2184 {2185 cw.newUTF8("RuntimeVisibleParameterAnnotations");2186 size += 7 + 2 * panns.length;2187 for(int i = panns.length - 1; i >= 0; --i)2188 {2189 size += panns[i] == null ? 0 : panns[i].getSize();2190 }2191 }2192 if(ipanns != null)2193 {2194 cw.newUTF8("RuntimeInvisibleParameterAnnotations");2195 size += 7 + 2 * ipanns.length;2196 for(int i = ipanns.length - 1; i >= 0; --i)2197 {2198 size += ipanns[i] == null ? 0 : ipanns[i].getSize();2199 }2200 }2201 if(attrs != null)2202 {2203 size += attrs.getSize(cw, null, 0, -1, -1);2204 }2205 return size;2206 }2208 /**2209 * Puts the bytecode of this method in the given byte vector.2210 *2211 * @param out the byte vector into which the bytecode of this method must be2212 * copied.2213 */2214 final void put(final ByteVector out){2215 out.putShort(access).putShort(name).putShort(desc);2216 if(classReaderOffset != 0)2217 {2218 out.putByteArray(cw.cr.b, classReaderOffset, classReaderLength);2219 return;2220 }2221 int attributeCount = 0;2222 if(code.length > 0)2223 {2224 ++attributeCount;2225 }2226 if(exceptionCount > 0)2227 {2228 ++attributeCount;2229 }2230 if((access & Opcodes.ACC_SYNTHETIC) != 02231 && (cw.version & 0xffff) < Opcodes.V1_5)2232 {2233 ++attributeCount;2234 }2235 if((access & Opcodes.ACC_DEPRECATED) != 0)2236 {2237 ++attributeCount;2238 }2239 if(signature != null)2240 {2241 ++attributeCount;2242 }2243 if(annd != null)2244 {2245 ++attributeCount;2246 }2247 if(anns != null)2248 {2249 ++attributeCount;2250 }2251 if(ianns != null)2252 {2253 ++attributeCount;2254 }2255 if(panns != null)2256 {2257 ++attributeCount;2258 }2259 if(ipanns != null)2260 {2261 ++attributeCount;2262 }2263 if(attrs != null)2264 {2265 attributeCount += attrs.getCount();2266 }2267 out.putShort(attributeCount);2268 if(code.length > 0)2269 {2270 int size = 12 + code.length + 8 * handlerCount;2271 if(localVar != null)2272 {2273 size += 8 + localVar.length;2274 }2275 if(localVarType != null)2276 {2277 size += 8 + localVarType.length;2278 }2279 if(lineNumber != null)2280 {2281 size += 8 + lineNumber.length;2282 }2283 if(stackMap != null)2284 {2285 size += 8 + stackMap.length;2286 }2287 if(cattrs != null)2288 {2289 size += cattrs.getSize(cw,2290 code.data,2291 code.length,2292 maxStack,2293 maxLocals);2294 }2295 out.putShort(cw.newUTF8("Code")).putInt(size);2296 out.putShort(maxStack).putShort(maxLocals);2297 out.putInt(code.length).putByteArray(code.data, 0, code.length);2298 out.putShort(handlerCount);2299 if(handlerCount > 0)2300 {2301 Handler h = firstHandler;2302 while(h != null)2303 {2304 out.putShort(h.start.position)2305 .putShort(h.end.position)2306 .putShort(h.handler.position)2307 .putShort(h.type);2308 h = h.next;2309 }2310 }2311 attributeCount = 0;2312 if(localVar != null)2313 {2314 ++attributeCount;2315 }2316 if(localVarType != null)2317 {2318 ++attributeCount;2319 }2320 if(lineNumber != null)2321 {2322 ++attributeCount;2323 }2324 if(stackMap != null)2325 {2326 ++attributeCount;2327 }2328 if(cattrs != null)2329 {2330 attributeCount += cattrs.getCount();2331 }2332 out.putShort(attributeCount);2333 if(localVar != null)2334 {2335 out.putShort(cw.newUTF8("LocalVariableTable"));2336 out.putInt(localVar.length + 2).putShort(localVarCount);2337 out.putByteArray(localVar.data, 0, localVar.length);2338 }2339 if(localVarType != null)2340 {2341 out.putShort(cw.newUTF8("LocalVariableTypeTable"));2342 out.putInt(localVarType.length + 2).putShort(localVarTypeCount);2343 out.putByteArray(localVarType.data, 0, localVarType.length);2344 }2345 if(lineNumber != null)2346 {2347 out.putShort(cw.newUTF8("LineNumberTable"));2348 out.putInt(lineNumber.length + 2).putShort(lineNumberCount);2349 out.putByteArray(lineNumber.data, 0, lineNumber.length);2350 }2351 if(stackMap != null)2352 {2353 boolean zip = (cw.version & 0xFFFF) >= Opcodes.V1_6;2354 out.putShort(cw.newUTF8(zip ? "StackMapTable" : "StackMap"));2355 out.putInt(stackMap.length + 2).putShort(frameCount);2356 out.putByteArray(stackMap.data, 0, stackMap.length);2357 }2358 if(cattrs != null)2359 {2360 cattrs.put(cw, code.data, code.length, maxLocals, maxStack, out);2361 }2362 }2363 if(exceptionCount > 0)2364 {2365 out.putShort(cw.newUTF8("Exceptions"))2366 .putInt(2 * exceptionCount + 2);2367 out.putShort(exceptionCount);2368 for(int i = 0; i < exceptionCount; ++i)2369 {2370 out.putShort(exceptions[i]);2371 }2372 }2373 if((access & Opcodes.ACC_SYNTHETIC) != 02374 && (cw.version & 0xffff) < Opcodes.V1_5)2375 {2376 out.putShort(cw.newUTF8("Synthetic")).putInt(0);2377 }2378 if((access & Opcodes.ACC_DEPRECATED) != 0)2379 {2380 out.putShort(cw.newUTF8("Deprecated")).putInt(0);2381 }2382 if(signature != null)2383 {2384 out.putShort(cw.newUTF8("Signature"))2385 .putInt(2)2386 .putShort(cw.newUTF8(signature));2387 }2388 if(annd != null)2389 {2390 out.putShort(cw.newUTF8("AnnotationDefault"));2391 out.putInt(annd.length);2392 out.putByteArray(annd.data, 0, annd.length);2393 }2394 if(anns != null)2395 {2396 out.putShort(cw.newUTF8("RuntimeVisibleAnnotations"));2397 anns.put(out);2398 }2399 if(ianns != null)2400 {2401 out.putShort(cw.newUTF8("RuntimeInvisibleAnnotations"));2402 ianns.put(out);2403 }2404 if(panns != null)2405 {2406 out.putShort(cw.newUTF8("RuntimeVisibleParameterAnnotations"));2407 AnnotationWriter.put(panns, out);2408 }2409 if(ipanns != null)2410 {2411 out.putShort(cw.newUTF8("RuntimeInvisibleParameterAnnotations"));2412 AnnotationWriter.put(ipanns, out);2413 }2414 if(attrs != null)2415 {2416 attrs.put(cw, null, 0, -1, -1, out);2417 }2418 }2420 // ------------------------------------------------------------------------2421 // Utility methods: instruction resizing (used to handle GOTO_W and JSR_W)2422 // ------------------------------------------------------------------------2424 /**2425 * Resizes and replaces the temporary instructions inserted by2426 * {@link Label#resolve} for wide forward jumps, while keeping jump offsets2427 * and instruction addresses consistent. This may require to resize other2428 * existing instructions, or even to introduce new instructions: for2429 * example, increasing the size of an instruction by 2 at the middle of a2430 * method can increases the offset of an IFEQ instruction from 32766 to2431 * 32768, in which case IFEQ 32766 must be replaced with IFNEQ 8 GOTO_W2432 * 32765. This, in turn, may require to increase the size of another jump2433 * instruction, and so on... All these operations are handled automatically2434 * by this method. <p> <i>This method must be called after all the method2435 * that is being built has been visited</i>. In particular, the2436 * {@link Label Label} objects used to construct the method are no longer2437 * valid after this method has been called.2438 */2439 private void resizeInstructions(){2440 byte[] b = code.data; // bytecode of the method2441 int u, v, label; // indexes in b2442 int i, j; // loop indexes2443 /*2444 * 1st step: As explained above, resizing an instruction may require to2445 * resize another one, which may require to resize yet another one, and2446 * so on. The first step of the algorithm consists in finding all the2447 * instructions that need to be resized, without modifying the code.2448 * This is done by the following "fix point" algorithm:2449 *2450 * Parse the code to find the jump instructions whose offset will need2451 * more than 2 bytes to be stored (the future offset is computed from2452 * the current offset and from the number of bytes that will be inserted2453 * or removed between the source and target instructions). For each such2454 * instruction, adds an entry in (a copy of) the indexes and sizes2455 * arrays (if this has not already been done in a previous iteration!).2456 *2457 * If at least one entry has been added during the previous step, go2458 * back to the beginning, otherwise stop.2459 *2460 * In fact the real algorithm is complicated by the fact that the size2461 * of TABLESWITCH and LOOKUPSWITCH instructions depends on their2462 * position in the bytecode (because of padding). In order to ensure the2463 * convergence of the algorithm, the number of bytes to be added or2464 * removed from these instructions is over estimated during the previous2465 * loop, and computed exactly only after the loop is finished (this2466 * requires another pass to parse the bytecode of the method).2467 */2468 int[] allIndexes = new int[0]; // copy of indexes2469 int[] allSizes = new int[0]; // copy of sizes2470 boolean[] resize; // instructions to be resized2471 int newOffset; // future offset of a jump instruction2473 resize = new boolean[code.length];2475 // 3 = loop again, 2 = loop ended, 1 = last pass, 0 = done2476 int state = 3;2477 do2478 {2479 if(state == 3)2480 {2481 state = 2;2482 }2483 u = 0;2484 while(u < b.length)2485 {2486 int opcode = b[u] & 0xFF; // opcode of current instruction2487 int insert = 0; // bytes to be added after this instruction2489 switch(ClassWriter.TYPE[opcode])2490 {2491 case ClassWriter.NOARG_INSN:2492 case ClassWriter.IMPLVAR_INSN:2493 u += 1;2494 break;2495 case ClassWriter.LABEL_INSN:2496 if(opcode > 201)2497 {2498 // converts temporary opcodes 202 to 217, 218 and2499 // 219 to IFEQ ... JSR (inclusive), IFNULL and2500 // IFNONNULL2501 opcode = opcode < 218 ? opcode - 49 : opcode - 20;2502 label = u + readUnsignedShort(b, u + 1);2503 }2504 else2505 {2506 label = u + readShort(b, u + 1);2507 }2508 newOffset = getNewOffset(allIndexes, allSizes, u, label);2509 if(newOffset < Short.MIN_VALUE2510 || newOffset > Short.MAX_VALUE)2511 {2512 if(!resize[u])2513 {2514 if(opcode == Opcodes.GOTO2515 || opcode == Opcodes.JSR)2516 {2517 // two additional bytes will be required to2518 // replace this GOTO or JSR instruction with2519 // a GOTO_W or a JSR_W2520 insert = 2;2521 }2522 else2523 {2524 // five additional bytes will be required to2525 // replace this IFxxx <l> instruction with2526 // IFNOTxxx <l'> GOTO_W <l>, where IFNOTxxx2527 // is the "opposite" opcode of IFxxx (i.e.,2528 // IFNE for IFEQ) and where <l'> designates2529 // the instruction just after the GOTO_W.2530 insert = 5;2531 }2532 resize[u] = true;2533 }2534 }2535 u += 3;2536 break;2537 case ClassWriter.LABELW_INSN:2538 u += 5;2539 break;2540 case ClassWriter.TABL_INSN:2541 if(state == 1)2542 {2543 // true number of bytes to be added (or removed)2544 // from this instruction = (future number of padding2545 // bytes - current number of padding byte) -2546 // previously over estimated variation =2547 // = ((3 - newOffset%4) - (3 - u%4)) - u%42548 // = (-newOffset%4 + u%4) - u%42549 // = -(newOffset & 3)2550 newOffset = getNewOffset(allIndexes, allSizes, 0, u);2551 insert = -(newOffset & 3);2552 }2553 else if(!resize[u])2554 {2555 // over estimation of the number of bytes to be2556 // added to this instruction = 3 - current number2557 // of padding bytes = 3 - (3 - u%4) = u%4 = u & 32558 insert = u & 3;2559 resize[u] = true;2560 }2561 // skips instruction2562 u = u + 4 - (u & 3);2563 u += 4 * (readInt(b, u + 8) - readInt(b, u + 4) + 1) + 12;2564 break;2565 case ClassWriter.LOOK_INSN:2566 if(state == 1)2567 {2568 // like TABL_INSN2569 newOffset = getNewOffset(allIndexes, allSizes, 0, u);2570 insert = -(newOffset & 3);2571 }2572 else if(!resize[u])2573 {2574 // like TABL_INSN2575 insert = u & 3;2576 resize[u] = true;2577 }2578 // skips instruction2579 u = u + 4 - (u & 3);2580 u += 8 * readInt(b, u + 4) + 8;2581 break;2582 case ClassWriter.WIDE_INSN:2583 opcode = b[u + 1] & 0xFF;2584 if(opcode == Opcodes.IINC)2585 {2586 u += 6;2587 }2588 else2589 {2590 u += 4;2591 }2592 break;2593 case ClassWriter.VAR_INSN:2594 case ClassWriter.SBYTE_INSN:2595 case ClassWriter.LDC_INSN:2596 u += 2;2597 break;2598 case ClassWriter.SHORT_INSN:2599 case ClassWriter.LDCW_INSN:2600 case ClassWriter.FIELDORMETH_INSN:2601 case ClassWriter.TYPE_INSN:2602 case ClassWriter.IINC_INSN:2603 u += 3;2604 break;2605 case ClassWriter.ITFMETH_INSN:2606 u += 5;2607 break;2608 // case ClassWriter.MANA_INSN:2609 default:2610 u += 4;2611 break;2612 }2613 if(insert != 0)2614 {2615 // adds a new (u, insert) entry in the allIndexes and2616 // allSizes arrays2617 int[] newIndexes = new int[allIndexes.length + 1];2618 int[] newSizes = new int[allSizes.length + 1];2619 System.arraycopy(allIndexes,2620 0,2621 newIndexes,2622 0,2623 allIndexes.length);2624 System.arraycopy(allSizes, 0, newSizes, 0, allSizes.length);2625 newIndexes[allIndexes.length] = u;2626 newSizes[allSizes.length] = insert;2627 allIndexes = newIndexes;2628 allSizes = newSizes;2629 if(insert > 0)2630 {2631 state = 3;2632 }2633 }2634 }2635 if(state < 3)2636 {2637 --state;2638 }2639 } while(state != 0);2641 // 2nd step:2642 // copies the bytecode of the method into a new bytevector, updates the2643 // offsets, and inserts (or removes) bytes as requested.2645 ByteVector newCode = new ByteVector(code.length);2647 u = 0;2648 while(u < code.length)2649 {2650 int opcode = b[u] & 0xFF;2651 switch(ClassWriter.TYPE[opcode])2652 {2653 case ClassWriter.NOARG_INSN:2654 case ClassWriter.IMPLVAR_INSN:2655 newCode.putByte(opcode);2656 u += 1;2657 break;2658 case ClassWriter.LABEL_INSN:2659 if(opcode > 201)2660 {2661 // changes temporary opcodes 202 to 217 (inclusive), 2182662 // and 219 to IFEQ ... JSR (inclusive), IFNULL and2663 // IFNONNULL2664 opcode = opcode < 218 ? opcode - 49 : opcode - 20;2665 label = u + readUnsignedShort(b, u + 1);2666 }2667 else2668 {2669 label = u + readShort(b, u + 1);2670 }2671 newOffset = getNewOffset(allIndexes, allSizes, u, label);2672 if(resize[u])2673 {2674 // replaces GOTO with GOTO_W, JSR with JSR_W and IFxxx2675 // <l> with IFNOTxxx <l'> GOTO_W <l>, where IFNOTxxx is2676 // the "opposite" opcode of IFxxx (i.e., IFNE for IFEQ)2677 // and where <l'> designates the instruction just after2678 // the GOTO_W.2679 if(opcode == Opcodes.GOTO)2680 {2681 newCode.putByte(200); // GOTO_W2682 }2683 else if(opcode == Opcodes.JSR)2684 {2685 newCode.putByte(201); // JSR_W2686 }2687 else2688 {2689 newCode.putByte(opcode <= 1662690 ? ((opcode + 1) ^ 1) - 12691 : opcode ^ 1);2692 newCode.putShort(8); // jump offset2693 newCode.putByte(200); // GOTO_W2694 // newOffset now computed from start of GOTO_W2695 newOffset -= 3;2696 }2697 newCode.putInt(newOffset);2698 }2699 else2700 {2701 newCode.putByte(opcode);2702 newCode.putShort(newOffset);2703 }2704 u += 3;2705 break;2706 case ClassWriter.LABELW_INSN:2707 label = u + readInt(b, u + 1);2708 newOffset = getNewOffset(allIndexes, allSizes, u, label);2709 newCode.putByte(opcode);2710 newCode.putInt(newOffset);2711 u += 5;2712 break;2713 case ClassWriter.TABL_INSN:2714 // skips 0 to 3 padding bytes2715 v = u;2716 u = u + 4 - (v & 3);2717 // reads and copies instruction2718 newCode.putByte(Opcodes.TABLESWITCH);2719 newCode.length += (4 - newCode.length % 4) % 4;2720 label = v + readInt(b, u);2721 u += 4;2722 newOffset = getNewOffset(allIndexes, allSizes, v, label);2723 newCode.putInt(newOffset);2724 j = readInt(b, u);2725 u += 4;2726 newCode.putInt(j);2727 j = readInt(b, u) - j + 1;2728 u += 4;2729 newCode.putInt(readInt(b, u - 4));2730 for(; j > 0; --j)2731 {2732 label = v + readInt(b, u);2733 u += 4;2734 newOffset = getNewOffset(allIndexes, allSizes, v, label);2735 newCode.putInt(newOffset);2736 }2737 break;2738 case ClassWriter.LOOK_INSN:2739 // skips 0 to 3 padding bytes2740 v = u;2741 u = u + 4 - (v & 3);2742 // reads and copies instruction2743 newCode.putByte(Opcodes.LOOKUPSWITCH);2744 newCode.length += (4 - newCode.length % 4) % 4;2745 label = v + readInt(b, u);2746 u += 4;2747 newOffset = getNewOffset(allIndexes, allSizes, v, label);2748 newCode.putInt(newOffset);2749 j = readInt(b, u);2750 u += 4;2751 newCode.putInt(j);2752 for(; j > 0; --j)2753 {2754 newCode.putInt(readInt(b, u));2755 u += 4;2756 label = v + readInt(b, u);2757 u += 4;2758 newOffset = getNewOffset(allIndexes, allSizes, v, label);2759 newCode.putInt(newOffset);2760 }2761 break;2762 case ClassWriter.WIDE_INSN:2763 opcode = b[u + 1] & 0xFF;2764 if(opcode == Opcodes.IINC)2765 {2766 newCode.putByteArray(b, u, 6);2767 u += 6;2768 }2769 else2770 {2771 newCode.putByteArray(b, u, 4);2772 u += 4;2773 }2774 break;2775 case ClassWriter.VAR_INSN:2776 case ClassWriter.SBYTE_INSN:2777 case ClassWriter.LDC_INSN:2778 newCode.putByteArray(b, u, 2);2779 u += 2;2780 break;2781 case ClassWriter.SHORT_INSN:2782 case ClassWriter.LDCW_INSN:2783 case ClassWriter.FIELDORMETH_INSN:2784 case ClassWriter.TYPE_INSN:2785 case ClassWriter.IINC_INSN:2786 newCode.putByteArray(b, u, 3);2787 u += 3;2788 break;2789 case ClassWriter.ITFMETH_INSN:2790 newCode.putByteArray(b, u, 5);2791 u += 5;2792 break;2793 // case MANA_INSN:2794 default:2795 newCode.putByteArray(b, u, 4);2796 u += 4;2797 break;2798 }2799 }2801 // recomputes the stack map frames2802 if(frameCount > 0)2803 {2804 if(compute == FRAMES)2805 {2806 frameCount = 0;2807 stackMap = null;2808 previousFrame = null;2809 frame = null;2810 Frame f = new Frame();2811 f.owner = labels;2812 Type[] args = Type.getArgumentTypes(descriptor);2813 f.initInputFrame(cw, access, args, maxLocals);2814 visitFrame(f);2815 Label l = labels;2816 while(l != null)2817 {2818 /*2819 * here we need the original label position. getNewOffset2820 * must therefore never have been called for this label.2821 */2822 u = l.position - 3;2823 if((l.status & Label.STORE) != 0 || (u >= 0 && resize[u]))2824 {2825 getNewOffset(allIndexes, allSizes, l);2826 // TODO update offsets in UNINITIALIZED values2827 visitFrame(l.frame);2828 }2829 l = l.successor;2830 }2831 }2832 else2833 {2834 /*2835 * Resizing an existing stack map frame table is really hard.2836 * Not only the table must be parsed to update the offets, but2837 * new frames may be needed for jump instructions that were2838 * inserted by this method. And updating the offsets or2839 * inserting frames can change the format of the following2840 * frames, in case of packed frames. In practice the whole table2841 * must be recomputed. For this the frames are marked as2842 * potentially invalid. This will cause the whole class to be2843 * reread and rewritten with the COMPUTE_FRAMES option (see the2844 * ClassWriter.toByteArray method). This is not very efficient2845 * but is much easier and requires much less code than any other2846 * method I can think of.2847 */2848 cw.invalidFrames = true;2849 }2850 }2851 // updates the exception handler block labels2852 Handler h = firstHandler;2853 while(h != null)2854 {2855 getNewOffset(allIndexes, allSizes, h.start);2856 getNewOffset(allIndexes, allSizes, h.end);2857 getNewOffset(allIndexes, allSizes, h.handler);2858 h = h.next;2859 }2860 // updates the instructions addresses in the2861 // local var and line number tables2862 for(i = 0; i < 2; ++i)2863 {2864 ByteVector bv = i == 0 ? localVar : localVarType;2865 if(bv != null)2866 {2867 b = bv.data;2868 u = 0;2869 while(u < bv.length)2870 {2871 label = readUnsignedShort(b, u);2872 newOffset = getNewOffset(allIndexes, allSizes, 0, label);2873 writeShort(b, u, newOffset);2874 label += readUnsignedShort(b, u + 2);2875 newOffset = getNewOffset(allIndexes, allSizes, 0, label)2876 - newOffset;2877 writeShort(b, u + 2, newOffset);2878 u += 10;2879 }2880 }2881 }2882 if(lineNumber != null)2883 {2884 b = lineNumber.data;2885 u = 0;2886 while(u < lineNumber.length)2887 {2888 writeShort(b, u, getNewOffset(allIndexes,2889 allSizes,2890 0,2891 readUnsignedShort(b, u)));2892 u += 4;2893 }2894 }2895 // updates the labels of the other attributes2896 Attribute attr = cattrs;2897 while(attr != null)2898 {2899 Label[] labels = attr.getLabels();2900 if(labels != null)2901 {2902 for(i = labels.length - 1; i >= 0; --i)2903 {2904 getNewOffset(allIndexes, allSizes, labels[i]);2905 }2906 }2907 attr = attr.next;2908 }2910 // replaces old bytecodes with new ones2911 code = newCode;2912 }2914 /**2915 * Reads an unsigned short value in the given byte array.2916 *2917 * @param b a byte array.2918 * @param index the start index of the value to be read.2919 * @return the read value.2920 */2921 static int readUnsignedShort(final byte[] b, final int index){2922 return ((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF);2923 }2925 /**2926 * Reads a signed short value in the given byte array.2927 *2928 * @param b a byte array.2929 * @param index the start index of the value to be read.2930 * @return the read value.2931 */2932 static short readShort(final byte[] b, final int index){2933 return (short) (((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF));2934 }2936 /**2937 * Reads a signed int value in the given byte array.2938 *2939 * @param b a byte array.2940 * @param index the start index of the value to be read.2941 * @return the read value.2942 */2943 static int readInt(final byte[] b, final int index){2944 return ((b[index] & 0xFF) << 24) | ((b[index + 1] & 0xFF) << 16)2945 | ((b[index + 2] & 0xFF) << 8) | (b[index + 3] & 0xFF);2946 }2948 /**2949 * Writes a short value in the given byte array.2950 *2951 * @param b a byte array.2952 * @param index where the first byte of the short value must be written.2953 * @param s the value to be written in the given byte array.2954 */2955 static void writeShort(final byte[] b, final int index, final int s){2956 b[index] = (byte) (s >>> 8);2957 b[index + 1] = (byte) s;2958 }2960 /**2961 * Computes the future value of a bytecode offset. <p> Note: it is possible2962 * to have several entries for the same instruction in the <tt>indexes</tt>2963 * and <tt>sizes</tt>: two entries (index=a,size=b) and (index=a,size=b')2964 * are equivalent to a single entry (index=a,size=b+b').2965 *2966 * @param indexes current positions of the instructions to be resized. Each2967 * instruction must be designated by the index of its <i>last</i>2968 * byte, plus one (or, in other words, by the index of the <i>first</i>2969 * byte of the <i>next</i> instruction).2970 * @param sizes the number of bytes to be <i>added</i> to the above2971 * instructions. More precisely, for each i < <tt>len</tt>,2972 * <tt>sizes</tt>[i] bytes will be added at the end of the2973 * instruction designated by <tt>indexes</tt>[i] or, if2974 * <tt>sizes</tt>[i] is negative, the <i>last</i> |<tt>sizes[i]</tt>|2975 * bytes of the instruction will be removed (the instruction size2976 * <i>must not</i> become negative or null).2977 * @param begin index of the first byte of the source instruction.2978 * @param end index of the first byte of the target instruction.2979 * @return the future value of the given bytecode offset.2980 */2981 static int getNewOffset(2982 final int[] indexes,2983 final int[] sizes,2984 final int begin,2985 final int end){2986 int offset = end - begin;2987 for(int i = 0; i < indexes.length; ++i)2988 {2989 if(begin < indexes[i] && indexes[i] <= end)2990 {2991 // forward jump2992 offset += sizes[i];2993 }2994 else if(end < indexes[i] && indexes[i] <= begin)2995 {2996 // backward jump2997 offset -= sizes[i];2998 }2999 }3000 return offset;3001 }3003 /**3004 * Updates the offset of the given label.3005 *3006 * @param indexes current positions of the instructions to be resized. Each3007 * instruction must be designated by the index of its <i>last</i>3008 * byte, plus one (or, in other words, by the index of the <i>first</i>3009 * byte of the <i>next</i> instruction).3010 * @param sizes the number of bytes to be <i>added</i> to the above3011 * instructions. More precisely, for each i < <tt>len</tt>,3012 * <tt>sizes</tt>[i] bytes will be added at the end of the3013 * instruction designated by <tt>indexes</tt>[i] or, if3014 * <tt>sizes</tt>[i] is negative, the <i>last</i> |<tt>sizes[i]</tt>|3015 * bytes of the instruction will be removed (the instruction size3016 * <i>must not</i> become negative or null).3017 * @param label the label whose offset must be updated.3018 */3019 static void getNewOffset(3020 final int[] indexes,3021 final int[] sizes,3022 final Label label){3023 if((label.status & Label.RESIZED) == 0)3024 {3025 label.position = getNewOffset(indexes, sizes, 0, label.position);3026 label.status |= Label.RESIZED;3027 }3028 }3029 }