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 framework
3 * Copyright (c) 2000-2005 INRIA, France Telecom
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of the copyright holders nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * 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, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
28 * THE POSSIBILITY OF SUCH DAMAGE.
29 */
30 package clojure.asm;
32 /**
33 * A {@link MethodVisitor} that generates methods in bytecode form. Each visit
34 * method of this class appends the bytecode corresponding to the visited
35 * instruction to a byte vector, in the order these methods are called.
36 *
37 * @author Eric Bruneton
38 * @author Eugene Kuleshov
39 */
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 and
49 * 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 and
55 * number of stack items is 1
56 */
57 final static int SAME_LOCALS_1_STACK_ITEM_FRAME = 64; // to 127 (40-7f)
59 /**
60 * Reserved for future use
61 */
62 final static int RESERVED = 128;
64 /**
65 * Frame has exactly the same locals as the previous stack map frame and
66 * number of stack items is 1. Offset is bigger then 63;
67 */
68 final static int SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED = 247; // f7
70 /**
71 * Frame where current locals are the same as the locals in the previous
72 * frame, except that the k last locals are absent. The value of k is given
73 * 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 and
79 * number of stack items is zero. Offset is bigger then 63;
80 */
81 final static int SAME_FRAME_EXTENDED = 251; // fb
83 /**
84 * Frame where current locals are the same as the locals in the previous
85 * frame, except that k additional locals are defined. The value of k is
86 * given by the formula frame_type-251.
87 */
88 final static int APPEND_FRAME = 252; // to 254 // fc-fe
90 /**
91 * Full frame
92 */
93 final static int FULL_FRAME = 255; // ff
95 /**
96 * Indicates that the stack map frames must be recomputed from scratch. In
97 * this case the maximum stack size and number of local variables is also
98 * recomputed from scratch.
99 *
100 * @see #compute
101 */
102 private final static int FRAMES = 0;
104 /**
105 * Indicates that the maximum stack size and number of local variables must
106 * be automatically computed.
107 *
108 * @see #compute
109 */
110 private final static int MAXS = 1;
112 /**
113 * Indicates that nothing must be automatically computed.
114 *
115 * @see #compute
116 */
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 this
136 * method.
137 */
138 private int name;
140 /**
141 * The index of the constant pool item that contains the descriptor of this
142 * 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 from
158 * the ClassReader associated to this writer in <code>cw.cr</code>. More
159 * precisely, this field gives the index of the first byte to copied from
160 * <code>cw.cr.b</code>.
161 */
162 int classReaderOffset;
164 /**
165 * If not zero, indicates that the code of this method must be copied from
166 * the ClassReader associated to this writer in <code>cw.cr</code>. More
167 * precisely, this field gives the number of bytes to copied from
168 * <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, this
179 * array contains the indexes of the constant pool items that contain the
180 * 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 be
201 * <tt>null</tt>.
202 */
203 private AnnotationWriter[] panns;
205 /**
206 * The runtime invisible parameter annotations of this method. May be
207 * <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 StackMapTable
243 * attribute.
244 */
245 private int previousFrameOffset;
247 /**
248 * The last frame that was written in the StackMapTable attribute.
249 *
250 * @see #frame
251 */
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 the
261 * instruction to which the frame corresponds, the second element is the
262 * number of locals and the third one is the number of stack elements. The
263 * local variables start at index 3 and are followed by the operand stack
264 * values. In summary frame[0] = offset, frame[1] = nLocal, frame[2] =
265 * nStack, frame[3] = nLocal. All types are encoded as integers, with the
266 * 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 the
334 * maximum stack size). A control flow graph contains one node per "basic
335 * block", and one edge per "jump" from one basic block to another. Each
336 * node (i.e., each basic block) is represented by the Label object that
337 * corresponds to the first instruction of this basic block. Each node also
338 * stores the list of its successors in the graph, as a linked list of Edge
339 * objects.
340 */
342 /**
343 * Indicates what must be automatically computed.
344 *
345 * @see FRAMES
346 * @see MAXS
347 * @see NOTHING
348 */
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 their
354 * {@link Label#successor} field, in the order they are visited by
355 * {@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 size
371 * is relative to the beginning of the current basic block, i.e., the true
372 * stack size after the last visited instruction is equal to the
373 * {@link Label#inputStackTop beginStackSize} of the current basic block
374 * 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 equal
382 * to the {@link Label#inputStackTop beginStackSize} of the current basic
383 * block plus <tt>stackSize</tt>.
384 */
385 private int maxStackSize;
387 // ------------------------------------------------------------------------
388 // Constructor
389 // ------------------------------------------------------------------------
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 be
400 * <tt>null</tt>.
401 * @param computeMaxs <tt>true</tt> if the maximum stack size and number
402 * of local variables must be automatically computed.
403 * @param computeFrames <tt>true</tt> if the stack map tables must be
404 * 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 else
420 {
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 maxLocals
447 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 block
454 labels = new Label();
455 labels.status |= Label.PUSHED;
456 visitLabel(labels);
457 }
458 }
460 // ------------------------------------------------------------------------
461 // Implementation of the MethodVisitor interface
462 // ------------------------------------------------------------------------
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 count
474 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 else
482 {
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 count
495 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 else
507 {
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 else
525 {
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.OBJECT
553 | cw.addType((String) local[i]);
554 }
555 else if(local[i] instanceof Integer)
556 {
557 frame[frameIndex++] = ((Integer) local[i]).intValue();
558 }
559 else
560 {
561 frame[frameIndex++] = Frame.UNINITIALIZED
562 | 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.OBJECT
571 | cw.addType((String) stack[i]);
572 }
573 else if(stack[i] instanceof Integer)
574 {
575 frame[frameIndex++] = ((Integer) stack[i]).intValue();
576 }
577 else
578 {
579 frame[frameIndex++] = Frame.UNINITIALIZED
580 | cw.addUninitializedType("",
581 ((Label) stack[i]).position);
582 }
583 }
584 endFrame();
585 }
586 else
587 {
588 int delta;
589 if(stackMap == null)
590 {
591 stackMap = new ByteVector();
592 delta = code.length;
593 }
594 else
595 {
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 else
633 {
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 else
643 {
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 method
658 code.putByte(opcode);
659 // update currentBlock
660 // Label currentBlock = this.currentBlock;
661 if(currentBlock != null)
662 {
663 if(compute == FRAMES)
664 {
665 currentBlock.frame.execute(opcode, 0, null, null);
666 }
667 else
668 {
669 // updates current and max stack sizes
670 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 NEWARRAY
697 // (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 method
707 if(opcode == Opcodes.SIPUSH)
708 {
709 code.put12(opcode, operand);
710 }
711 else
712 { // BIPUSH or NEWARRAY
713 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 else
726 {
727 // updates current and max stack sizes
728 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 use
733 // (see {@link #findSubroutineSuccessors})
734 currentBlock.inputStackTop = stackSize;
735 noSuccessor();
736 }
737 else
738 { // xLOAD or xSTORE
739 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 locals
751 int n;
752 if(opcode == Opcodes.LLOAD || opcode == Opcodes.DLOAD
753 || opcode == Opcodes.LSTORE || opcode == Opcodes.DSTORE)
754 {
755 n = var + 2;
756 }
757 else
758 {
759 n = var + 1;
760 }
761 if(n > maxLocals)
762 {
763 maxLocals = n;
764 }
765 }
766 // adds the instruction to the bytecode of the method
767 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 else
776 {
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 else
787 {
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 == NEW
808 // (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 method
818 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 else
835 {
836 int size;
837 // computes the stack size variation
838 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 sizes
856 if(size > maxStackSize)
857 {
858 maxStackSize = size;
859 }
860 stackSize = size;
861 }
862 }
863 // adds the instruction to the bytecode of the method
864 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 else
883 {
884 /*
885 * computes the stack size variation. In order not to recompute
886 * several times this variation for the same Item, we use the
887 * intVal field of this item to store this variation, once it
888 * has been computed. More precisely this intVal field stores
889 * the sizes of the arguments and of the return value
890 * 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 order
898 // not to recompute them in the future
899 i.intVal = argSize;
900 }
901 int size;
902 if(opcode == Opcodes.INVOKESTATIC)
903 {
904 size = stackSize - (argSize >> 2) + (argSize & 0x03) + 1;
905 }
906 else
907 {
908 size = stackSize - (argSize >> 2) + (argSize & 0x03);
909 }
910 // updates current and max stack sizes
911 if(size > maxStackSize)
912 {
913 maxStackSize = size;
914 }
915 stackSize = size;
916 }
917 }
918 // adds the instruction to the bytecode of the method
919 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 else
929 {
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 instruction
943 label.getFirst().status |= Label.TARGET;
944 // adds 'label' as a successor of this basic block
945 addSuccessor(Edge.NORMAL, label);
946 if(opcode != Opcodes.GOTO)
947 {
948 // creates a Label for the next basic block
949 nextInsn = new Label();
950 }
951 }
952 else
953 {
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 block
960 nextInsn = new Label();
961 /*
962 * note that, by construction in this method, a JSR block
963 * 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 else
969 {
970 // updates current stack size (max stack size unchanged
971 // because stack size variation always negative in this
972 // case)
973 stackSize += Frame.SIZE[opcode];
974 addSuccessor(stackSize, label);
975 }
976 }
977 }
978 // adds the instruction to the bytecode of the method
979 if((label.status & Label.RESOLVED) != 0
980 && label.position - code.length < Short.MIN_VALUE)
981 {
982 /*
983 * case of a backward jump with an offset < -32768. In this case we
984 * automatically replace GOTO with GOTO_W, JSR with JSR_W and IFxxx
985 * <l> with IFNOTxxx <l'> GOTO_W <l>, where IFNOTxxx is the
986 * "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_W
992 }
993 else if(opcode == Opcodes.JSR)
994 {
995 code.putByte(201); // JSR_W
996 }
997 else
998 {
999 // if the IF instruction is transformed into IFNOT GOTO_W the
1000 // next instruction becomes the target of the IFNOT instruction
1001 if(nextInsn != null)
1003 nextInsn.status |= Label.TARGET;
1005 code.putByte(opcode <= 166
1006 ? ((opcode + 1) ^ 1) - 1
1007 : opcode ^ 1);
1008 code.putShort(8); // jump offset
1009 code.putByte(200); // GOTO_W
1011 label.put(this, code, code.length - 1, true);
1013 else
1015 /*
1016 * case of a backward jump with an offset >= -32768, or of a forward
1017 * jump with, of course, an unknown offset. In these cases we store
1018 * the offset in 2 bytes (which will be increased in
1019 * resizeInstructions, if needed).
1020 */
1021 code.putByte(opcode);
1022 label.put(this, code, code.length - 1, false);
1024 if(currentBlock != null)
1026 if(nextInsn != null)
1028 // if the jump instruction is not a GOTO, the next instruction
1029 // is also a successor of this instruction. Calling visitLabel
1030 // adds the label of this next instruction as a successor of the
1031 // current block, and starts a new basic block
1032 visitLabel(nextInsn);
1034 if(opcode == Opcodes.GOTO)
1036 noSuccessor();
1041 public void visitLabel(final Label label){
1042 // resolves previous forward references to label, if any
1043 resize |= label.resolve(this, code.length, code.data);
1044 // updates currentBlock
1045 if((label.status & Label.DEBUG) != 0)
1047 return;
1049 if(compute == FRAMES)
1051 if(currentBlock != null)
1053 if(label.position == currentBlock.position)
1055 // successive labels, do not start a new basic block
1056 currentBlock.status |= (label.status & Label.TARGET);
1057 label.frame = currentBlock.frame;
1058 return;
1060 // ends current block (with one new successor)
1061 addSuccessor(Edge.NORMAL, label);
1063 // begins a new current block
1064 currentBlock = label;
1065 if(label.frame == null)
1067 label.frame = new Frame();
1068 label.frame.owner = label;
1070 // updates the basic block list
1071 if(previousBlock != null)
1073 if(label.position == previousBlock.position)
1075 previousBlock.status |= (label.status & Label.TARGET);
1076 label.frame = previousBlock.frame;
1077 currentBlock = previousBlock;
1078 return;
1080 previousBlock.successor = label;
1082 previousBlock = label;
1084 else if(compute == MAXS)
1086 if(currentBlock != null)
1088 // ends current block (with one new successor)
1089 currentBlock.outputStackMax = maxStackSize;
1090 addSuccessor(stackSize, label);
1092 // begins a new current block
1093 currentBlock = label;
1094 // resets the relative current and max stack sizes
1095 stackSize = 0;
1096 maxStackSize = 0;
1097 // updates the basic block list
1098 if(previousBlock != null)
1100 previousBlock.successor = label;
1102 previousBlock = label;
1106 public void visitLdcInsn(final Object cst){
1107 Item i = cw.newConstItem(cst);
1108 // Label currentBlock = this.currentBlock;
1109 if(currentBlock != null)
1111 if(compute == FRAMES)
1113 currentBlock.frame.execute(Opcodes.LDC, 0, cw, i);
1115 else
1117 int size;
1118 // computes the stack size variation
1119 if(i.type == ClassWriter.LONG || i.type == ClassWriter.DOUBLE)
1121 size = stackSize + 2;
1123 else
1125 size = stackSize + 1;
1127 // updates current and max stack sizes
1128 if(size > maxStackSize)
1130 maxStackSize = size;
1132 stackSize = size;
1135 // adds the instruction to the bytecode of the method
1136 int index = i.index;
1137 if(i.type == ClassWriter.LONG || i.type == ClassWriter.DOUBLE)
1139 code.put12(20 /* LDC2_W */, index);
1141 else if(index >= 256)
1143 code.put12(19 /* LDC_W */, index);
1145 else
1147 code.put11(Opcodes.LDC, index);
1151 public void visitIincInsn(final int var, final int increment){
1152 if(currentBlock != null)
1154 if(compute == FRAMES)
1156 currentBlock.frame.execute(Opcodes.IINC, var, null, null);
1159 if(compute != NOTHING)
1161 // updates max locals
1162 int n = var + 1;
1163 if(n > maxLocals)
1165 maxLocals = n;
1168 // adds the instruction to the bytecode of the method
1169 if((var > 255) || (increment > 127) || (increment < -128))
1171 code.putByte(196 /* WIDE */)
1172 .put12(Opcodes.IINC, var)
1173 .putShort(increment);
1175 else
1177 code.putByte(Opcodes.IINC).put11(var, increment);
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 method
1187 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)
1194 labels[i].put(this, code, source, true);
1196 // updates currentBlock
1197 visitSwitchInsn(dflt, labels);
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 method
1205 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)
1212 code.putInt(keys[i]);
1213 labels[i].put(this, code, source, true);
1215 // updates currentBlock
1216 visitSwitchInsn(dflt, labels);
1219 private void visitSwitchInsn(final Label dflt, final Label[] labels){
1220 // Label currentBlock = this.currentBlock;
1221 if(currentBlock != null)
1223 if(compute == FRAMES)
1225 currentBlock.frame.execute(Opcodes.LOOKUPSWITCH, 0, null, null);
1226 // adds current block successors
1227 addSuccessor(Edge.NORMAL, dflt);
1228 dflt.getFirst().status |= Label.TARGET;
1229 for(int i = 0; i < labels.length; ++i)
1231 addSuccessor(Edge.NORMAL, labels[i]);
1232 labels[i].getFirst().status |= Label.TARGET;
1235 else
1237 // updates current stack size (max stack size unchanged)
1238 --stackSize;
1239 // adds current block successors
1240 addSuccessor(stackSize, dflt);
1241 for(int i = 0; i < labels.length; ++i)
1243 addSuccessor(stackSize, labels[i]);
1246 // ends current block
1247 noSuccessor();
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)
1256 if(compute == FRAMES)
1258 currentBlock.frame.execute(Opcodes.MULTIANEWARRAY, dims, cw, i);
1260 else
1262 // updates current stack size (max stack size unchanged because
1263 // stack size variation always negative or null)
1264 stackSize += 1 - dims;
1267 // adds the instruction to the bytecode of the method
1268 code.put12(Opcodes.MULTIANEWARRAY, i.index).putByte(dims);
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)
1285 firstHandler = h;
1287 else
1289 lastHandler.next = h;
1291 lastHandler = h;
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)
1303 if(localVarType == null)
1305 localVarType = new ByteVector();
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);
1314 if(localVar == null)
1316 localVar = new ByteVector();
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)
1326 // updates max locals
1327 char c = desc.charAt(0);
1328 int n = index + (c == 'J' || c == 'D' ? 2 : 1);
1329 if(n > maxLocals)
1331 maxLocals = n;
1336 public void visitLineNumber(final int line, final Label start){
1337 if(lineNumber == null)
1339 lineNumber = new ByteVector();
1341 ++lineNumberCount;
1342 lineNumber.putShort(start.position);
1343 lineNumber.putShort(line);
1346 public void visitMaxs(final int maxStack, final int maxLocals){
1347 if(compute == FRAMES)
1349 // completes the control flow graph with exception handler blocks
1350 Handler handler = firstHandler;
1351 while(handler != null)
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 == null
1358 ? "java/lang/Throwable"
1359 : handler.desc;
1360 int kind = Frame.OBJECT | cw.addType(t);
1361 // h is an exception handler
1362 h.status |= Label.TARGET;
1363 // adds 'h' as a successor of labels between 'start' and 'end'
1364 while(l != e)
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 label
1374 l = l.successor;
1376 handler = handler.next;
1379 // creates and visits the first (implicit) frame
1380 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 changed
1388 * basic blocks, choose one, mark it as unchanged, and update its
1389 * successors (which can be changed in the process).
1390 */
1391 int max = 0;
1392 Label changed = labels;
1393 while(changed != null)
1395 // removes a basic block from the list of changed basic blocks
1396 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 map
1401 if((l.status & Label.TARGET) != 0)
1403 l.status |= Label.STORE;
1405 // all visited labels are reacheable, by definition
1406 l.status |= Label.REACHABLE;
1407 // updates the (absolute) maximum stack size
1408 int blockMax = f.inputStack.length + l.outputStackMax;
1409 if(blockMax > max)
1411 max = blockMax;
1413 // updates the successors of the current basic block
1414 Edge e = l.successors;
1415 while(e != null)
1417 Label n = e.successor.getFirst();
1418 boolean change = f.merge(cw, n.frame, e.info);
1419 if(change && n.next == null)
1421 // if n has changed and is not already in the 'changed'
1422 // list, adds it to this list
1423 n.next = changed;
1424 changed = n;
1426 e = e.next;
1429 this.maxStack = max;
1431 // visits all the frames that must be stored in the stack map
1432 Label l = labels;
1433 while(l != null)
1435 f = l.frame;
1436 if((l.status & Label.STORE) != 0)
1438 visitFrame(f);
1440 if((l.status & Label.REACHABLE) == 0)
1442 // finds start and end of dead basic block
1443 Label k = l.successor;
1444 int start = l.position;
1445 int end = (k == null ? code.length : k.position) - 1;
1446 // if non empty basic block
1447 if(end >= start)
1449 // replaces instructions with NOP ... NOP ATHROW
1450 for(int i = start; i < end; ++i)
1452 code.data[i] = Opcodes.NOP;
1454 code.data[end] = (byte) Opcodes.ATHROW;
1455 // emits a frame for this unreachable block
1456 startFrame(start, 0, 1);
1457 frame[frameIndex++] = Frame.OBJECT
1458 | cw.addType("java/lang/Throwable");
1459 endFrame();
1462 l = l.successor;
1465 else if(compute == MAXS)
1467 // completes the control flow graph with exception handler blocks
1468 Handler handler = firstHandler;
1469 while(handler != null)
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)
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)
1484 // if l is a JSR block, adds b after the first two edges
1485 // to preserve the hypothesis about JSR block successors
1486 // order (see {@link #visitJumpInsn})
1487 b.next = l.successors.next.next;
1488 l.successors.next.next = b;
1490 else
1492 b.next = l.successors;
1493 l.successors = b;
1495 // goes to the next label
1496 l = l.successor;
1498 handler = handler.next;
1501 if(jsr)
1503 // completes the control flow graph with the RET successors
1504 /*
1505 * first step: finds the subroutines. This step determines, for
1506 * each basic block, to which subroutine(s) it belongs, and
1507 * stores this set as a bit set in the {@link Label#status}
1508 * field. Subroutines are numbered with powers of two, from
1509 * 0x1000 to 0x80000000 (so there must be at most 20 subroutines
1510 * in a method).
1511 */
1512 // finds the basic blocks that belong to the "main" subroutine
1513 int id = 0x1000;
1514 findSubroutine(labels, id);
1515 // finds the basic blocks that belong to the real subroutines
1516 Label l = labels;
1517 while(l != null)
1519 if((l.status & Label.JSR) != 0)
1521 // the subroutine is defined by l's TARGET, not by l
1522 Label subroutine = l.successors.next.successor;
1523 // if this subroutine does not have an id yet...
1524 if((subroutine.status & ~0xFFF) == 0)
1526 // ...assigns it a new id and finds its basic blocks
1527 id = id << 1;
1528 findSubroutine(subroutine, id);
1531 l = l.successor;
1533 // second step: finds the successors of RET blocks
1534 findSubroutineSuccessors(0x1000, new Label[10], 0);
1537 /*
1538 * control flow analysis algorithm: while the block stack is not
1539 * empty, pop a block from this stack, update the max stack size,
1540 * compute the true (non relative) begin stack size of the
1541 * successors of this block, and push these successors onto the
1542 * stack (unless they have already been pushed onto the stack).
1543 * Note: by hypothesis, the {@link Label#inputStackTop} of the
1544 * blocks in the block stack are the true (non relative) beginning
1545 * stack sizes of these blocks.
1546 */
1547 int max = 0;
1548 Label stack = labels;
1549 while(stack != null)
1551 // pops a block from the stack
1552 Label l = stack;
1553 stack = stack.next;
1554 // computes the true (non relative) max stack size of this block
1555 int start = l.inputStackTop;
1556 int blockMax = start + l.outputStackMax;
1557 // updates the global max stack size
1558 if(blockMax > max)
1560 max = blockMax;
1562 // analyses the successors of the block
1563 Edge b = l.successors;
1564 if((l.status & Label.JSR) != 0)
1566 // ignores the first edge of JSR blocks (virtual successor)
1567 b = b.next;
1569 while(b != null)
1571 l = b.successor;
1572 // if this successor has not already been pushed...
1573 if((l.status & Label.PUSHED) == 0)
1575 // computes its true beginning stack size...
1576 l.inputStackTop = b.info == Edge.EXCEPTION ? 1 : start
1577 + b.info;
1578 // ...and pushes it onto the stack
1579 l.status |= Label.PUSHED;
1580 l.next = stack;
1581 stack = l;
1583 b = b.next;
1586 this.maxStack = max;
1588 else
1590 this.maxStack = maxStack;
1591 this.maxLocals = maxLocals;
1595 public void visitEnd(){
1598 // ------------------------------------------------------------------------
1599 // Utility methods: control flow analysis algorithm
1600 // ------------------------------------------------------------------------
1602 /**
1603 * Computes the size of the arguments and of the return value of a method.
1605 * @param desc the descriptor of a method.
1606 * @return the size of the arguments of the method (plus one for the
1607 * implicit this argument), argSize, and the size of its return
1608 * value, retSize, packed into a single int i =
1609 * <tt>(argSize << 2) | retSize</tt> (argSize is therefore equal
1610 * 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)
1617 char car = desc.charAt(c++);
1618 if(car == ')')
1620 car = desc.charAt(c);
1621 return n << 2
1622 | (car == 'V' ? 0 : (car == 'D' || car == 'J' ? 2 : 1));
1624 else if(car == 'L')
1626 while(desc.charAt(c++) != ';')
1629 n += 1;
1631 else if(car == '[')
1633 while((car = desc.charAt(c)) == '[')
1635 ++c;
1637 if(car == 'D' || car == 'J')
1639 n -= 1;
1642 else if(car == 'D' || car == 'J')
1644 n += 2;
1646 else
1648 n += 1;
1653 /**
1654 * Adds a successor to the {@link #currentBlock currentBlock} block.
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 block
1665 b.next = currentBlock.successors;
1666 currentBlock.successors = b;
1669 /**
1670 * Ends the current basic block. This method must be used in the case where
1671 * the current basic block does not have any successor.
1672 */
1673 private void noSuccessor(){
1674 if(compute == FRAMES)
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;
1683 else
1685 currentBlock.outputStackMax = maxStackSize;
1687 currentBlock = null;
1690 /**
1691 * Finds the basic blocks that belong to a given subroutine, and marks these
1692 * blocks as belonging to this subroutine (by using {@link Label#status} as
1693 * a bit set (see {@link #visitMaxs}). This recursive method follows the
1694 * control flow graph to find all the blocks that are reachable from the
1695 * given block WITHOUT following any JSR target.
1697 * @param block a block that belongs to the subroutine
1698 * @param id the id of this subroutine
1699 */
1700 private void findSubroutine(final Label block, final int id){
1701 // if 'block' is already marked as belonging to subroutine 'id', returns
1702 if((block.status & id) != 0)
1704 return;
1706 // marks 'block' as belonging to subroutine 'id'
1707 block.status |= id;
1708 // calls this method recursively on each successor, except JSR targets
1709 Edge e = block.successors;
1710 while(e != null)
1712 // if 'block' is a JSR block, then 'block.successors.next' leads
1713 // to the JSR target (see {@link #visitJumpInsn}) and must therefore
1714 // not be followed
1715 if((block.status & Label.JSR) == 0 || e != block.successors.next)
1717 findSubroutine(e.successor, id);
1719 e = e.next;
1723 /**
1724 * Finds the successors of the RET blocks of the specified subroutine, and
1725 * of any nested subroutine it calls.
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)
1739 // for those that belong to subroutine 'id'...
1740 if((l.status & id) != 0)
1742 if((l.status & Label.JSR) != 0)
1744 // finds the subroutine to which 'l' leads by following the
1745 // second edge of l.successors (see {@link #visitJumpInsn})
1746 int nId = l.successors.next.successor.status & ~0xFFF;
1747 if(nId != id)
1749 // calls this method recursively with l pushed onto the
1750 // JSRs stack to find the successors of the RET blocks
1751 // of this nested subroutine 'nId'
1752 JSRs[nJSRs] = l;
1753 findSubroutineSuccessors(nId, JSRs, nJSRs + 1);
1756 else if((l.status & Label.RET) != 0)
1758 /*
1759 * finds the JSR block in the JSRs stack that corresponds to
1760 * this RET block, and updates the successors of this RET
1761 * block accordingly. This corresponding JSR is the one that
1762 * leads to the subroutine to which the RET block belongs.
1763 * But the RET block can belong to several subroutines (if a
1764 * nested subroutine returns to its parent subroutine
1765 * implicitely, without a RET). So, in fact, the JSR that
1766 * corresponds to this RET is the first block in the JSRs
1767 * stack, starting from the bottom of the stack, that leads
1768 * to a subroutine to which the RET block belongs.
1769 */
1770 for(int i = 0; i < nJSRs; ++i)
1772 int JSRstatus = JSRs[i].successors.next.successor.status;
1773 if(((JSRstatus & ~0xFFF) & (l.status & ~0xFFF)) != 0)
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;
1785 l = l.successor;
1789 // ------------------------------------------------------------------------
1790 // Utility methods: stack map frames
1791 // ------------------------------------------------------------------------
1793 /**
1794 * Visits a frame that has been computed from scratch.
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 after
1806 // a LONG or a DOUBLE, and all trailing TOP types)
1807 for(i = 0; i < locals.length; ++i)
1809 t = locals[i];
1810 if(t == Frame.TOP)
1812 ++nTop;
1814 else
1816 nLocal += nTop + 1;
1817 nTop = 0;
1819 if(t == Frame.LONG || t == Frame.DOUBLE)
1821 ++i;
1824 // computes the stack size (ignores TOP types that are just after
1825 // a LONG or a DOUBLE)
1826 for(i = 0; i < stacks.length; ++i)
1828 t = stacks[i];
1829 ++nStack;
1830 if(t == Frame.LONG || t == Frame.DOUBLE)
1832 ++i;
1835 // visits the frame and its content
1836 startFrame(f.owner.position, nLocal, nStack);
1837 for(i = 0; nLocal > 0; ++i, --nLocal)
1839 t = locals[i];
1840 frame[frameIndex++] = t;
1841 if(t == Frame.LONG || t == Frame.DOUBLE)
1843 ++i;
1846 for(i = 0; i < stacks.length; ++i)
1848 t = stacks[i];
1849 frame[frameIndex++] = t;
1850 if(t == Frame.LONG || t == Frame.DOUBLE)
1852 ++i;
1855 endFrame();
1858 /**
1859 * Starts the visit of a stack map frame.
1861 * @param offset the offset of the instruction to which the frame
1862 * 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)
1870 frame = new int[n];
1872 frame[0] = offset;
1873 frame[1] = nLocal;
1874 frame[2] = nStack;
1875 frameIndex = 3;
1878 /**
1879 * Checks if the visit of the current frame {@link #frame} is finished, and
1880 * if yes, write it in the StackMapTable attribute.
1881 */
1882 private void endFrame(){
1883 if(previousFrame != null)
1884 { // do not write the first frame
1885 if(stackMap == null)
1887 stackMap = new ByteVector();
1889 writeFrame();
1890 ++frameCount;
1892 previousFrame = frame;
1893 frame = null;
1896 /**
1897 * Compress and writes the current frame {@link #frame} in the StackMapTable
1898 * attribute.
1899 */
1900 private void writeFrame(){
1901 int clocalsSize = frame[1];
1902 int cstackSize = frame[2];
1903 if((cw.version & 0xFFFF) < Opcodes.V1_6)
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;
1911 int localsSize = previousFrame[1];
1912 int type = FULL_FRAME;
1913 int k = 0;
1914 int delta;
1915 if(frameCount == 0)
1917 delta = frame[0];
1919 else
1921 delta = frame[0] - previousFrame[0] - 1;
1923 if(cstackSize == 0)
1925 k = clocalsSize - localsSize;
1926 switch(k)
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;
1944 else if(clocalsSize == localsSize && cstackSize == 1)
1946 type = delta < 63
1947 ? SAME_LOCALS_1_STACK_ITEM_FRAME
1948 : SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED;
1950 if(type != FULL_FRAME)
1952 // verify if locals are the same
1953 int l = 3;
1954 for(int j = 0; j < localsSize; j++)
1956 if(frame[l] != previousFrame[l])
1958 type = FULL_FRAME;
1959 break;
1961 l++;
1964 switch(type)
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);
1999 /**
2000 * Writes some types of the current frame {@link #frame} into the
2001 * StackMapTableAttribute. This method converts types from the format used
2002 * in {@link Label} to the format used in StackMapTable attributes. In
2003 * particular, it converts type table indexes to constant pool indexes.
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)
2011 int t = frame[i];
2012 int d = t & Frame.DIM;
2013 if(d == 0)
2015 int v = t & Frame.BASE_VALUE;
2016 switch(t & Frame.BASE_KIND)
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);
2029 else
2031 StringBuffer buf = new StringBuffer();
2032 d >>= 28;
2033 while(d-- > 0)
2035 buf.append('[');
2037 if((t & Frame.BASE_KIND) == Frame.OBJECT)
2039 buf.append('L');
2040 buf.append(cw.typeTable[t & Frame.BASE_VALUE].strVal1);
2041 buf.append(';');
2043 else
2045 switch(t & 0xF)
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');
2072 stackMap.putByte(7).putShort(cw.newClass(buf.toString()));
2077 private void writeFrameType(final Object type){
2078 if(type instanceof String)
2080 stackMap.putByte(7).putShort(cw.newClass((String) type));
2082 else if(type instanceof Integer)
2084 stackMap.putByte(((Integer) type).intValue());
2086 else
2088 stackMap.putByte(8).putShort(((Label) type).position);
2092 // ------------------------------------------------------------------------
2093 // Utility methods: dump bytecode array
2094 // ------------------------------------------------------------------------
2096 /**
2097 * Returns the size of the bytecode of this method.
2099 * @return the size of the bytecode of this method.
2100 */
2101 final int getSize(){
2102 if(classReaderOffset != 0)
2104 return 6 + classReaderLength;
2106 if(resize)
2108 // replaces the temporary jump opcodes introduced by Label.resolve.
2109 resizeInstructions();
2111 int size = 8;
2112 if(code.length > 0)
2114 cw.newUTF8("Code");
2115 size += 18 + code.length + 8 * handlerCount;
2116 if(localVar != null)
2118 cw.newUTF8("LocalVariableTable");
2119 size += 8 + localVar.length;
2121 if(localVarType != null)
2123 cw.newUTF8("LocalVariableTypeTable");
2124 size += 8 + localVarType.length;
2126 if(lineNumber != null)
2128 cw.newUTF8("LineNumberTable");
2129 size += 8 + lineNumber.length;
2131 if(stackMap != null)
2133 boolean zip = (cw.version & 0xFFFF) >= Opcodes.V1_6;
2134 cw.newUTF8(zip ? "StackMapTable" : "StackMap");
2135 size += 8 + stackMap.length;
2137 if(cattrs != null)
2139 size += cattrs.getSize(cw,
2140 code.data,
2141 code.length,
2142 maxStack,
2143 maxLocals);
2146 if(exceptionCount > 0)
2148 cw.newUTF8("Exceptions");
2149 size += 8 + 2 * exceptionCount;
2151 if((access & Opcodes.ACC_SYNTHETIC) != 0
2152 && (cw.version & 0xffff) < Opcodes.V1_5)
2154 cw.newUTF8("Synthetic");
2155 size += 6;
2157 if((access & Opcodes.ACC_DEPRECATED) != 0)
2159 cw.newUTF8("Deprecated");
2160 size += 6;
2162 if(signature != null)
2164 cw.newUTF8("Signature");
2165 cw.newUTF8(signature);
2166 size += 8;
2168 if(annd != null)
2170 cw.newUTF8("AnnotationDefault");
2171 size += 6 + annd.length;
2173 if(anns != null)
2175 cw.newUTF8("RuntimeVisibleAnnotations");
2176 size += 8 + anns.getSize();
2178 if(ianns != null)
2180 cw.newUTF8("RuntimeInvisibleAnnotations");
2181 size += 8 + ianns.getSize();
2183 if(panns != null)
2185 cw.newUTF8("RuntimeVisibleParameterAnnotations");
2186 size += 7 + 2 * panns.length;
2187 for(int i = panns.length - 1; i >= 0; --i)
2189 size += panns[i] == null ? 0 : panns[i].getSize();
2192 if(ipanns != null)
2194 cw.newUTF8("RuntimeInvisibleParameterAnnotations");
2195 size += 7 + 2 * ipanns.length;
2196 for(int i = ipanns.length - 1; i >= 0; --i)
2198 size += ipanns[i] == null ? 0 : ipanns[i].getSize();
2201 if(attrs != null)
2203 size += attrs.getSize(cw, null, 0, -1, -1);
2205 return size;
2208 /**
2209 * Puts the bytecode of this method in the given byte vector.
2211 * @param out the byte vector into which the bytecode of this method must be
2212 * copied.
2213 */
2214 final void put(final ByteVector out){
2215 out.putShort(access).putShort(name).putShort(desc);
2216 if(classReaderOffset != 0)
2218 out.putByteArray(cw.cr.b, classReaderOffset, classReaderLength);
2219 return;
2221 int attributeCount = 0;
2222 if(code.length > 0)
2224 ++attributeCount;
2226 if(exceptionCount > 0)
2228 ++attributeCount;
2230 if((access & Opcodes.ACC_SYNTHETIC) != 0
2231 && (cw.version & 0xffff) < Opcodes.V1_5)
2233 ++attributeCount;
2235 if((access & Opcodes.ACC_DEPRECATED) != 0)
2237 ++attributeCount;
2239 if(signature != null)
2241 ++attributeCount;
2243 if(annd != null)
2245 ++attributeCount;
2247 if(anns != null)
2249 ++attributeCount;
2251 if(ianns != null)
2253 ++attributeCount;
2255 if(panns != null)
2257 ++attributeCount;
2259 if(ipanns != null)
2261 ++attributeCount;
2263 if(attrs != null)
2265 attributeCount += attrs.getCount();
2267 out.putShort(attributeCount);
2268 if(code.length > 0)
2270 int size = 12 + code.length + 8 * handlerCount;
2271 if(localVar != null)
2273 size += 8 + localVar.length;
2275 if(localVarType != null)
2277 size += 8 + localVarType.length;
2279 if(lineNumber != null)
2281 size += 8 + lineNumber.length;
2283 if(stackMap != null)
2285 size += 8 + stackMap.length;
2287 if(cattrs != null)
2289 size += cattrs.getSize(cw,
2290 code.data,
2291 code.length,
2292 maxStack,
2293 maxLocals);
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)
2301 Handler h = firstHandler;
2302 while(h != null)
2304 out.putShort(h.start.position)
2305 .putShort(h.end.position)
2306 .putShort(h.handler.position)
2307 .putShort(h.type);
2308 h = h.next;
2311 attributeCount = 0;
2312 if(localVar != null)
2314 ++attributeCount;
2316 if(localVarType != null)
2318 ++attributeCount;
2320 if(lineNumber != null)
2322 ++attributeCount;
2324 if(stackMap != null)
2326 ++attributeCount;
2328 if(cattrs != null)
2330 attributeCount += cattrs.getCount();
2332 out.putShort(attributeCount);
2333 if(localVar != null)
2335 out.putShort(cw.newUTF8("LocalVariableTable"));
2336 out.putInt(localVar.length + 2).putShort(localVarCount);
2337 out.putByteArray(localVar.data, 0, localVar.length);
2339 if(localVarType != null)
2341 out.putShort(cw.newUTF8("LocalVariableTypeTable"));
2342 out.putInt(localVarType.length + 2).putShort(localVarTypeCount);
2343 out.putByteArray(localVarType.data, 0, localVarType.length);
2345 if(lineNumber != null)
2347 out.putShort(cw.newUTF8("LineNumberTable"));
2348 out.putInt(lineNumber.length + 2).putShort(lineNumberCount);
2349 out.putByteArray(lineNumber.data, 0, lineNumber.length);
2351 if(stackMap != null)
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);
2358 if(cattrs != null)
2360 cattrs.put(cw, code.data, code.length, maxLocals, maxStack, out);
2363 if(exceptionCount > 0)
2365 out.putShort(cw.newUTF8("Exceptions"))
2366 .putInt(2 * exceptionCount + 2);
2367 out.putShort(exceptionCount);
2368 for(int i = 0; i < exceptionCount; ++i)
2370 out.putShort(exceptions[i]);
2373 if((access & Opcodes.ACC_SYNTHETIC) != 0
2374 && (cw.version & 0xffff) < Opcodes.V1_5)
2376 out.putShort(cw.newUTF8("Synthetic")).putInt(0);
2378 if((access & Opcodes.ACC_DEPRECATED) != 0)
2380 out.putShort(cw.newUTF8("Deprecated")).putInt(0);
2382 if(signature != null)
2384 out.putShort(cw.newUTF8("Signature"))
2385 .putInt(2)
2386 .putShort(cw.newUTF8(signature));
2388 if(annd != null)
2390 out.putShort(cw.newUTF8("AnnotationDefault"));
2391 out.putInt(annd.length);
2392 out.putByteArray(annd.data, 0, annd.length);
2394 if(anns != null)
2396 out.putShort(cw.newUTF8("RuntimeVisibleAnnotations"));
2397 anns.put(out);
2399 if(ianns != null)
2401 out.putShort(cw.newUTF8("RuntimeInvisibleAnnotations"));
2402 ianns.put(out);
2404 if(panns != null)
2406 out.putShort(cw.newUTF8("RuntimeVisibleParameterAnnotations"));
2407 AnnotationWriter.put(panns, out);
2409 if(ipanns != null)
2411 out.putShort(cw.newUTF8("RuntimeInvisibleParameterAnnotations"));
2412 AnnotationWriter.put(ipanns, out);
2414 if(attrs != null)
2416 attrs.put(cw, null, 0, -1, -1, out);
2420 // ------------------------------------------------------------------------
2421 // Utility methods: instruction resizing (used to handle GOTO_W and JSR_W)
2422 // ------------------------------------------------------------------------
2424 /**
2425 * Resizes and replaces the temporary instructions inserted by
2426 * {@link Label#resolve} for wide forward jumps, while keeping jump offsets
2427 * and instruction addresses consistent. This may require to resize other
2428 * existing instructions, or even to introduce new instructions: for
2429 * example, increasing the size of an instruction by 2 at the middle of a
2430 * method can increases the offset of an IFEQ instruction from 32766 to
2431 * 32768, in which case IFEQ 32766 must be replaced with IFNEQ 8 GOTO_W
2432 * 32765. This, in turn, may require to increase the size of another jump
2433 * instruction, and so on... All these operations are handled automatically
2434 * by this method. <p> <i>This method must be called after all the method
2435 * that is being built has been visited</i>. In particular, the
2436 * {@link Label Label} objects used to construct the method are no longer
2437 * valid after this method has been called.
2438 */
2439 private void resizeInstructions(){
2440 byte[] b = code.data; // bytecode of the method
2441 int u, v, label; // indexes in b
2442 int i, j; // loop indexes
2443 /*
2444 * 1st step: As explained above, resizing an instruction may require to
2445 * resize another one, which may require to resize yet another one, and
2446 * so on. The first step of the algorithm consists in finding all the
2447 * instructions that need to be resized, without modifying the code.
2448 * This is done by the following "fix point" algorithm:
2450 * Parse the code to find the jump instructions whose offset will need
2451 * more than 2 bytes to be stored (the future offset is computed from
2452 * the current offset and from the number of bytes that will be inserted
2453 * or removed between the source and target instructions). For each such
2454 * instruction, adds an entry in (a copy of) the indexes and sizes
2455 * arrays (if this has not already been done in a previous iteration!).
2457 * If at least one entry has been added during the previous step, go
2458 * back to the beginning, otherwise stop.
2460 * In fact the real algorithm is complicated by the fact that the size
2461 * of TABLESWITCH and LOOKUPSWITCH instructions depends on their
2462 * position in the bytecode (because of padding). In order to ensure the
2463 * convergence of the algorithm, the number of bytes to be added or
2464 * removed from these instructions is over estimated during the previous
2465 * loop, and computed exactly only after the loop is finished (this
2466 * requires another pass to parse the bytecode of the method).
2467 */
2468 int[] allIndexes = new int[0]; // copy of indexes
2469 int[] allSizes = new int[0]; // copy of sizes
2470 boolean[] resize; // instructions to be resized
2471 int newOffset; // future offset of a jump instruction
2473 resize = new boolean[code.length];
2475 // 3 = loop again, 2 = loop ended, 1 = last pass, 0 = done
2476 int state = 3;
2477 do
2479 if(state == 3)
2481 state = 2;
2483 u = 0;
2484 while(u < b.length)
2486 int opcode = b[u] & 0xFF; // opcode of current instruction
2487 int insert = 0; // bytes to be added after this instruction
2489 switch(ClassWriter.TYPE[opcode])
2491 case ClassWriter.NOARG_INSN:
2492 case ClassWriter.IMPLVAR_INSN:
2493 u += 1;
2494 break;
2495 case ClassWriter.LABEL_INSN:
2496 if(opcode > 201)
2498 // converts temporary opcodes 202 to 217, 218 and
2499 // 219 to IFEQ ... JSR (inclusive), IFNULL and
2500 // IFNONNULL
2501 opcode = opcode < 218 ? opcode - 49 : opcode - 20;
2502 label = u + readUnsignedShort(b, u + 1);
2504 else
2506 label = u + readShort(b, u + 1);
2508 newOffset = getNewOffset(allIndexes, allSizes, u, label);
2509 if(newOffset < Short.MIN_VALUE
2510 || newOffset > Short.MAX_VALUE)
2512 if(!resize[u])
2514 if(opcode == Opcodes.GOTO
2515 || opcode == Opcodes.JSR)
2517 // two additional bytes will be required to
2518 // replace this GOTO or JSR instruction with
2519 // a GOTO_W or a JSR_W
2520 insert = 2;
2522 else
2524 // five additional bytes will be required to
2525 // replace this IFxxx <l> instruction with
2526 // IFNOTxxx <l'> GOTO_W <l>, where IFNOTxxx
2527 // is the "opposite" opcode of IFxxx (i.e.,
2528 // IFNE for IFEQ) and where <l'> designates
2529 // the instruction just after the GOTO_W.
2530 insert = 5;
2532 resize[u] = true;
2535 u += 3;
2536 break;
2537 case ClassWriter.LABELW_INSN:
2538 u += 5;
2539 break;
2540 case ClassWriter.TABL_INSN:
2541 if(state == 1)
2543 // true number of bytes to be added (or removed)
2544 // from this instruction = (future number of padding
2545 // bytes - current number of padding byte) -
2546 // previously over estimated variation =
2547 // = ((3 - newOffset%4) - (3 - u%4)) - u%4
2548 // = (-newOffset%4 + u%4) - u%4
2549 // = -(newOffset & 3)
2550 newOffset = getNewOffset(allIndexes, allSizes, 0, u);
2551 insert = -(newOffset & 3);
2553 else if(!resize[u])
2555 // over estimation of the number of bytes to be
2556 // added to this instruction = 3 - current number
2557 // of padding bytes = 3 - (3 - u%4) = u%4 = u & 3
2558 insert = u & 3;
2559 resize[u] = true;
2561 // skips instruction
2562 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)
2568 // like TABL_INSN
2569 newOffset = getNewOffset(allIndexes, allSizes, 0, u);
2570 insert = -(newOffset & 3);
2572 else if(!resize[u])
2574 // like TABL_INSN
2575 insert = u & 3;
2576 resize[u] = true;
2578 // skips instruction
2579 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)
2586 u += 6;
2588 else
2590 u += 4;
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;
2613 if(insert != 0)
2615 // adds a new (u, insert) entry in the allIndexes and
2616 // allSizes arrays
2617 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)
2631 state = 3;
2635 if(state < 3)
2637 --state;
2639 } while(state != 0);
2641 // 2nd step:
2642 // copies the bytecode of the method into a new bytevector, updates the
2643 // offsets, and inserts (or removes) bytes as requested.
2645 ByteVector newCode = new ByteVector(code.length);
2647 u = 0;
2648 while(u < code.length)
2650 int opcode = b[u] & 0xFF;
2651 switch(ClassWriter.TYPE[opcode])
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)
2661 // changes temporary opcodes 202 to 217 (inclusive), 218
2662 // and 219 to IFEQ ... JSR (inclusive), IFNULL and
2663 // IFNONNULL
2664 opcode = opcode < 218 ? opcode - 49 : opcode - 20;
2665 label = u + readUnsignedShort(b, u + 1);
2667 else
2669 label = u + readShort(b, u + 1);
2671 newOffset = getNewOffset(allIndexes, allSizes, u, label);
2672 if(resize[u])
2674 // replaces GOTO with GOTO_W, JSR with JSR_W and IFxxx
2675 // <l> with IFNOTxxx <l'> GOTO_W <l>, where IFNOTxxx is
2676 // the "opposite" opcode of IFxxx (i.e., IFNE for IFEQ)
2677 // and where <l'> designates the instruction just after
2678 // the GOTO_W.
2679 if(opcode == Opcodes.GOTO)
2681 newCode.putByte(200); // GOTO_W
2683 else if(opcode == Opcodes.JSR)
2685 newCode.putByte(201); // JSR_W
2687 else
2689 newCode.putByte(opcode <= 166
2690 ? ((opcode + 1) ^ 1) - 1
2691 : opcode ^ 1);
2692 newCode.putShort(8); // jump offset
2693 newCode.putByte(200); // GOTO_W
2694 // newOffset now computed from start of GOTO_W
2695 newOffset -= 3;
2697 newCode.putInt(newOffset);
2699 else
2701 newCode.putByte(opcode);
2702 newCode.putShort(newOffset);
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 bytes
2715 v = u;
2716 u = u + 4 - (v & 3);
2717 // reads and copies instruction
2718 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)
2732 label = v + readInt(b, u);
2733 u += 4;
2734 newOffset = getNewOffset(allIndexes, allSizes, v, label);
2735 newCode.putInt(newOffset);
2737 break;
2738 case ClassWriter.LOOK_INSN:
2739 // skips 0 to 3 padding bytes
2740 v = u;
2741 u = u + 4 - (v & 3);
2742 // reads and copies instruction
2743 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)
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);
2761 break;
2762 case ClassWriter.WIDE_INSN:
2763 opcode = b[u + 1] & 0xFF;
2764 if(opcode == Opcodes.IINC)
2766 newCode.putByteArray(b, u, 6);
2767 u += 6;
2769 else
2771 newCode.putByteArray(b, u, 4);
2772 u += 4;
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;
2801 // recomputes the stack map frames
2802 if(frameCount > 0)
2804 if(compute == FRAMES)
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)
2818 /*
2819 * here we need the original label position. getNewOffset
2820 * 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]))
2825 getNewOffset(allIndexes, allSizes, l);
2826 // TODO update offsets in UNINITIALIZED values
2827 visitFrame(l.frame);
2829 l = l.successor;
2832 else
2834 /*
2835 * Resizing an existing stack map frame table is really hard.
2836 * Not only the table must be parsed to update the offets, but
2837 * new frames may be needed for jump instructions that were
2838 * inserted by this method. And updating the offsets or
2839 * inserting frames can change the format of the following
2840 * frames, in case of packed frames. In practice the whole table
2841 * must be recomputed. For this the frames are marked as
2842 * potentially invalid. This will cause the whole class to be
2843 * reread and rewritten with the COMPUTE_FRAMES option (see the
2844 * ClassWriter.toByteArray method). This is not very efficient
2845 * but is much easier and requires much less code than any other
2846 * method I can think of.
2847 */
2848 cw.invalidFrames = true;
2851 // updates the exception handler block labels
2852 Handler h = firstHandler;
2853 while(h != null)
2855 getNewOffset(allIndexes, allSizes, h.start);
2856 getNewOffset(allIndexes, allSizes, h.end);
2857 getNewOffset(allIndexes, allSizes, h.handler);
2858 h = h.next;
2860 // updates the instructions addresses in the
2861 // local var and line number tables
2862 for(i = 0; i < 2; ++i)
2864 ByteVector bv = i == 0 ? localVar : localVarType;
2865 if(bv != null)
2867 b = bv.data;
2868 u = 0;
2869 while(u < bv.length)
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;
2882 if(lineNumber != null)
2884 b = lineNumber.data;
2885 u = 0;
2886 while(u < lineNumber.length)
2888 writeShort(b, u, getNewOffset(allIndexes,
2889 allSizes,
2890 0,
2891 readUnsignedShort(b, u)));
2892 u += 4;
2895 // updates the labels of the other attributes
2896 Attribute attr = cattrs;
2897 while(attr != null)
2899 Label[] labels = attr.getLabels();
2900 if(labels != null)
2902 for(i = labels.length - 1; i >= 0; --i)
2904 getNewOffset(allIndexes, allSizes, labels[i]);
2907 attr = attr.next;
2910 // replaces old bytecodes with new ones
2911 code = newCode;
2914 /**
2915 * Reads an unsigned short value in the given byte array.
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);
2925 /**
2926 * Reads a signed short value in the given byte array.
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));
2936 /**
2937 * Reads a signed int value in the given byte array.
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);
2948 /**
2949 * Writes a short value in the given byte array.
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;
2960 /**
2961 * Computes the future value of a bytecode offset. <p> Note: it is possible
2962 * 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').
2966 * @param indexes current positions of the instructions to be resized. Each
2967 * 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 above
2971 * instructions. More precisely, for each i < <tt>len</tt>,
2972 * <tt>sizes</tt>[i] bytes will be added at the end of the
2973 * instruction designated by <tt>indexes</tt>[i] or, if
2974 * <tt>sizes</tt>[i] is negative, the <i>last</i> |<tt>sizes[i]</tt>|
2975 * bytes of the instruction will be removed (the instruction size
2976 * <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)
2989 if(begin < indexes[i] && indexes[i] <= end)
2991 // forward jump
2992 offset += sizes[i];
2994 else if(end < indexes[i] && indexes[i] <= begin)
2996 // backward jump
2997 offset -= sizes[i];
3000 return offset;
3003 /**
3004 * Updates the offset of the given label.
3006 * @param indexes current positions of the instructions to be resized. Each
3007 * 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 above
3011 * instructions. More precisely, for each i < <tt>len</tt>,
3012 * <tt>sizes</tt>[i] bytes will be added at the end of the
3013 * instruction designated by <tt>indexes</tt>[i] or, if
3014 * <tt>sizes</tt>[i] is negative, the <i>last</i> |<tt>sizes[i]</tt>|
3015 * bytes of the instruction will be removed (the instruction size
3016 * <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)
3025 label.position = getNewOffset(indexes, sizes, 0, label.position);
3026 label.status |= Label.RESIZED;