Mercurial > lasercutter
comparison 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 |
comparison
equal
deleted
inserted
replaced
9:35cf337adfcf | 10:ef7dbbd6452c |
---|---|
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; | |
31 | |
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{ | |
41 | |
42 /** | |
43 * Pseudo access flag used to denote constructors. | |
44 */ | |
45 final static int ACC_CONSTRUCTOR = 262144; | |
46 | |
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) | |
52 | |
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) | |
58 | |
59 /** | |
60 * Reserved for future use | |
61 */ | |
62 final static int RESERVED = 128; | |
63 | |
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 | |
69 | |
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) | |
76 | |
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 | |
82 | |
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 | |
89 | |
90 /** | |
91 * Full frame | |
92 */ | |
93 final static int FULL_FRAME = 255; // ff | |
94 | |
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; | |
103 | |
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; | |
111 | |
112 /** | |
113 * Indicates that nothing must be automatically computed. | |
114 * | |
115 * @see #compute | |
116 */ | |
117 private final static int NOTHING = 2; | |
118 | |
119 /** | |
120 * Next method writer (see {@link ClassWriter#firstMethod firstMethod}). | |
121 */ | |
122 MethodWriter next; | |
123 | |
124 /** | |
125 * The class writer to which this method must be added. | |
126 */ | |
127 ClassWriter cw; | |
128 | |
129 /** | |
130 * Access flags of this method. | |
131 */ | |
132 private int access; | |
133 | |
134 /** | |
135 * The index of the constant pool item that contains the name of this | |
136 * method. | |
137 */ | |
138 private int name; | |
139 | |
140 /** | |
141 * The index of the constant pool item that contains the descriptor of this | |
142 * method. | |
143 */ | |
144 private int desc; | |
145 | |
146 /** | |
147 * The descriptor of this method. | |
148 */ | |
149 private String descriptor; | |
150 | |
151 /** | |
152 * The signature of this method. | |
153 */ | |
154 String signature; | |
155 | |
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; | |
163 | |
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; | |
171 | |
172 /** | |
173 * Number of exceptions that can be thrown by this method. | |
174 */ | |
175 int exceptionCount; | |
176 | |
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; | |
183 | |
184 /** | |
185 * The annotation default attribute of this method. May be <tt>null</tt>. | |
186 */ | |
187 private ByteVector annd; | |
188 | |
189 /** | |
190 * The runtime visible annotations of this method. May be <tt>null</tt>. | |
191 */ | |
192 private AnnotationWriter anns; | |
193 | |
194 /** | |
195 * The runtime invisible annotations of this method. May be <tt>null</tt>. | |
196 */ | |
197 private AnnotationWriter ianns; | |
198 | |
199 /** | |
200 * The runtime visible parameter annotations of this method. May be | |
201 * <tt>null</tt>. | |
202 */ | |
203 private AnnotationWriter[] panns; | |
204 | |
205 /** | |
206 * The runtime invisible parameter annotations of this method. May be | |
207 * <tt>null</tt>. | |
208 */ | |
209 private AnnotationWriter[] ipanns; | |
210 | |
211 /** | |
212 * The non standard attributes of the method. | |
213 */ | |
214 private Attribute attrs; | |
215 | |
216 /** | |
217 * The bytecode of this method. | |
218 */ | |
219 private ByteVector code = new ByteVector(); | |
220 | |
221 /** | |
222 * Maximum stack size of this method. | |
223 */ | |
224 private int maxStack; | |
225 | |
226 /** | |
227 * Maximum number of local variables for this method. | |
228 */ | |
229 private int maxLocals; | |
230 | |
231 /** | |
232 * Number of stack map frames in the StackMapTable attribute. | |
233 */ | |
234 private int frameCount; | |
235 | |
236 /** | |
237 * The StackMapTable attribute. | |
238 */ | |
239 private ByteVector stackMap; | |
240 | |
241 /** | |
242 * The offset of the last frame that was written in the StackMapTable | |
243 * attribute. | |
244 */ | |
245 private int previousFrameOffset; | |
246 | |
247 /** | |
248 * The last frame that was written in the StackMapTable attribute. | |
249 * | |
250 * @see #frame | |
251 */ | |
252 private int[] previousFrame; | |
253 | |
254 /** | |
255 * Index of the next element to be added in {@link #frame}. | |
256 */ | |
257 private int frameIndex; | |
258 | |
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; | |
269 | |
270 /** | |
271 * Number of elements in the exception handler list. | |
272 */ | |
273 private int handlerCount; | |
274 | |
275 /** | |
276 * The first element in the exception handler list. | |
277 */ | |
278 private Handler firstHandler; | |
279 | |
280 /** | |
281 * The last element in the exception handler list. | |
282 */ | |
283 private Handler lastHandler; | |
284 | |
285 /** | |
286 * Number of entries in the LocalVariableTable attribute. | |
287 */ | |
288 private int localVarCount; | |
289 | |
290 /** | |
291 * The LocalVariableTable attribute. | |
292 */ | |
293 private ByteVector localVar; | |
294 | |
295 /** | |
296 * Number of entries in the LocalVariableTypeTable attribute. | |
297 */ | |
298 private int localVarTypeCount; | |
299 | |
300 /** | |
301 * The LocalVariableTypeTable attribute. | |
302 */ | |
303 private ByteVector localVarType; | |
304 | |
305 /** | |
306 * Number of entries in the LineNumberTable attribute. | |
307 */ | |
308 private int lineNumberCount; | |
309 | |
310 /** | |
311 * The LineNumberTable attribute. | |
312 */ | |
313 private ByteVector lineNumber; | |
314 | |
315 /** | |
316 * The non standard attributes of the method's code. | |
317 */ | |
318 private Attribute cattrs; | |
319 | |
320 /** | |
321 * Indicates if some jump instructions are too small and need to be resized. | |
322 */ | |
323 private boolean resize; | |
324 | |
325 /** | |
326 * Indicates if the instructions contain at least one JSR instruction. | |
327 */ | |
328 private boolean jsr; | |
329 | |
330 // ------------------------------------------------------------------------ | |
331 | |
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 */ | |
341 | |
342 /** | |
343 * Indicates what must be automatically computed. | |
344 * | |
345 * @see FRAMES | |
346 * @see MAXS | |
347 * @see NOTHING | |
348 */ | |
349 private int compute; | |
350 | |
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; | |
358 | |
359 /** | |
360 * The previous basic block. | |
361 */ | |
362 private Label previousBlock; | |
363 | |
364 /** | |
365 * The current basic block. | |
366 */ | |
367 private Label currentBlock; | |
368 | |
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; | |
377 | |
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; | |
386 | |
387 // ------------------------------------------------------------------------ | |
388 // Constructor | |
389 // ------------------------------------------------------------------------ | |
390 | |
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 } | |
459 | |
460 // ------------------------------------------------------------------------ | |
461 // Implementation of the MethodVisitor interface | |
462 // ------------------------------------------------------------------------ | |
463 | |
464 public AnnotationVisitor visitAnnotationDefault(){ | |
465 annd = new ByteVector(); | |
466 return new AnnotationWriter(cw, false, annd, null, 0); | |
467 } | |
468 | |
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 } | |
488 | |
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 } | |
517 | |
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 } | |
530 | |
531 public void visitCode(){ | |
532 } | |
533 | |
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 } | |
544 | |
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 } | |
598 | |
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 } | |
650 | |
651 previousFrameOffset = code.length; | |
652 ++frameCount; | |
653 } | |
654 } | |
655 | |
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 } | |
685 | |
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 } | |
716 | |
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 } | |
795 | |
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 } | |
820 | |
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 } | |
866 | |
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 } | |
933 | |
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) | |
1002 { | |
1003 nextInsn.status |= Label.TARGET; | |
1004 } | |
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 | |
1010 } | |
1011 label.put(this, code, code.length - 1, true); | |
1012 } | |
1013 else | |
1014 { | |
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); | |
1023 } | |
1024 if(currentBlock != null) | |
1025 { | |
1026 if(nextInsn != null) | |
1027 { | |
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); | |
1033 } | |
1034 if(opcode == Opcodes.GOTO) | |
1035 { | |
1036 noSuccessor(); | |
1037 } | |
1038 } | |
1039 } | |
1040 | |
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) | |
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 block | |
1056 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 block | |
1064 currentBlock = label; | |
1065 if(label.frame == null) | |
1066 { | |
1067 label.frame = new Frame(); | |
1068 label.frame.owner = label; | |
1069 } | |
1070 // updates the basic block list | |
1071 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 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) | |
1099 { | |
1100 previousBlock.successor = label; | |
1101 } | |
1102 previousBlock = label; | |
1103 } | |
1104 } | |
1105 | |
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 else | |
1116 { | |
1117 int size; | |
1118 // computes the stack size variation | |
1119 if(i.type == ClassWriter.LONG || i.type == ClassWriter.DOUBLE) | |
1120 { | |
1121 size = stackSize + 2; | |
1122 } | |
1123 else | |
1124 { | |
1125 size = stackSize + 1; | |
1126 } | |
1127 // updates current and max stack sizes | |
1128 if(size > maxStackSize) | |
1129 { | |
1130 maxStackSize = size; | |
1131 } | |
1132 stackSize = size; | |
1133 } | |
1134 } | |
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) | |
1138 { | |
1139 code.put12(20 /* LDC2_W */, index); | |
1140 } | |
1141 else if(index >= 256) | |
1142 { | |
1143 code.put12(19 /* LDC_W */, index); | |
1144 } | |
1145 else | |
1146 { | |
1147 code.put11(Opcodes.LDC, index); | |
1148 } | |
1149 } | |
1150 | |
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 locals | |
1162 int n = var + 1; | |
1163 if(n > maxLocals) | |
1164 { | |
1165 maxLocals = n; | |
1166 } | |
1167 } | |
1168 // adds the instruction to the bytecode of the method | |
1169 if((var > 255) || (increment > 127) || (increment < -128)) | |
1170 { | |
1171 code.putByte(196 /* WIDE */) | |
1172 .put12(Opcodes.IINC, var) | |
1173 .putShort(increment); | |
1174 } | |
1175 else | |
1176 { | |
1177 code.putByte(Opcodes.IINC).put11(var, increment); | |
1178 } | |
1179 } | |
1180 | |
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) | |
1193 { | |
1194 labels[i].put(this, code, source, true); | |
1195 } | |
1196 // updates currentBlock | |
1197 visitSwitchInsn(dflt, labels); | |
1198 } | |
1199 | |
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) | |
1211 { | |
1212 code.putInt(keys[i]); | |
1213 labels[i].put(this, code, source, true); | |
1214 } | |
1215 // updates currentBlock | |
1216 visitSwitchInsn(dflt, labels); | |
1217 } | |
1218 | |
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 successors | |
1227 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 else | |
1236 { | |
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) | |
1242 { | |
1243 addSuccessor(stackSize, labels[i]); | |
1244 } | |
1245 } | |
1246 // ends current block | |
1247 noSuccessor(); | |
1248 } | |
1249 } | |
1250 | |
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 else | |
1261 { | |
1262 // updates current stack size (max stack size unchanged because | |
1263 // stack size variation always negative or null) | |
1264 stackSize += 1 - dims; | |
1265 } | |
1266 } | |
1267 // adds the instruction to the bytecode of the method | |
1268 code.put12(Opcodes.MULTIANEWARRAY, i.index).putByte(dims); | |
1269 } | |
1270 | |
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 else | |
1288 { | |
1289 lastHandler.next = h; | |
1290 } | |
1291 lastHandler = h; | |
1292 } | |
1293 | |
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 locals | |
1327 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 } | |
1335 | |
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 } | |
1345 | |
1346 public void visitMaxs(final int maxStack, final int maxLocals){ | |
1347 if(compute == FRAMES) | |
1348 { | |
1349 // completes the control flow graph with exception handler blocks | |
1350 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 == 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) | |
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 label | |
1374 l = l.successor; | |
1375 } | |
1376 handler = handler.next; | |
1377 } | |
1378 | |
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); | |
1384 | |
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) | |
1394 { | |
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) | |
1402 { | |
1403 l.status |= Label.STORE; | |
1404 } | |
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) | |
1410 { | |
1411 max = blockMax; | |
1412 } | |
1413 // updates the successors of the current basic block | |
1414 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 list | |
1423 n.next = changed; | |
1424 changed = n; | |
1425 } | |
1426 e = e.next; | |
1427 } | |
1428 } | |
1429 this.maxStack = max; | |
1430 | |
1431 // visits all the frames that must be stored in the stack map | |
1432 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 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) | |
1448 { | |
1449 // replaces instructions with NOP ... NOP ATHROW | |
1450 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 block | |
1456 startFrame(start, 0, 1); | |
1457 frame[frameIndex++] = Frame.OBJECT | |
1458 | 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 blocks | |
1468 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 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; | |
1489 } | |
1490 else | |
1491 { | |
1492 b.next = l.successors; | |
1493 l.successors = b; | |
1494 } | |
1495 // goes to the next label | |
1496 l = l.successor; | |
1497 } | |
1498 handler = handler.next; | |
1499 } | |
1500 | |
1501 if(jsr) | |
1502 { | |
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) | |
1518 { | |
1519 if((l.status & Label.JSR) != 0) | |
1520 { | |
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) | |
1525 { | |
1526 // ...assigns it a new id and finds its basic blocks | |
1527 id = id << 1; | |
1528 findSubroutine(subroutine, id); | |
1529 } | |
1530 } | |
1531 l = l.successor; | |
1532 } | |
1533 // second step: finds the successors of RET blocks | |
1534 findSubroutineSuccessors(0x1000, new Label[10], 0); | |
1535 } | |
1536 | |
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) | |
1550 { | |
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) | |
1559 { | |
1560 max = blockMax; | |
1561 } | |
1562 // analyses the successors of the block | |
1563 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 : start | |
1577 + b.info; | |
1578 // ...and pushes it onto the stack | |
1579 l.status |= Label.PUSHED; | |
1580 l.next = stack; | |
1581 stack = l; | |
1582 } | |
1583 b = b.next; | |
1584 } | |
1585 } | |
1586 this.maxStack = max; | |
1587 } | |
1588 else | |
1589 { | |
1590 this.maxStack = maxStack; | |
1591 this.maxLocals = maxLocals; | |
1592 } | |
1593 } | |
1594 | |
1595 public void visitEnd(){ | |
1596 } | |
1597 | |
1598 // ------------------------------------------------------------------------ | |
1599 // Utility methods: control flow analysis algorithm | |
1600 // ------------------------------------------------------------------------ | |
1601 | |
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 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) | |
1616 { | |
1617 char car = desc.charAt(c++); | |
1618 if(car == ')') | |
1619 { | |
1620 car = desc.charAt(c); | |
1621 return n << 2 | |
1622 | (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 else | |
1647 { | |
1648 n += 1; | |
1649 } | |
1650 } | |
1651 } | |
1652 | |
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 block | |
1665 b.next = currentBlock.successors; | |
1666 currentBlock.successors = b; | |
1667 } | |
1668 | |
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) | |
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 else | |
1684 { | |
1685 currentBlock.outputStackMax = maxStackSize; | |
1686 } | |
1687 currentBlock = null; | |
1688 } | |
1689 | |
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. | |
1696 * | |
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) | |
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 targets | |
1709 Edge e = block.successors; | |
1710 while(e != null) | |
1711 { | |
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) | |
1716 { | |
1717 findSubroutine(e.successor, id); | |
1718 } | |
1719 e = e.next; | |
1720 } | |
1721 } | |
1722 | |
1723 /** | |
1724 * Finds the successors of the RET blocks of the specified subroutine, and | |
1725 * 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 the | |
1745 // 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 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); | |
1754 } | |
1755 } | |
1756 else if((l.status & Label.RET) != 0) | |
1757 { | |
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) | |
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 } | |
1788 | |
1789 // ------------------------------------------------------------------------ | |
1790 // Utility methods: stack map frames | |
1791 // ------------------------------------------------------------------------ | |
1792 | |
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 after | |
1806 // 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 else | |
1815 { | |
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 after | |
1825 // 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 content | |
1836 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 } | |
1857 | |
1858 /** | |
1859 * Starts the visit of a stack map frame. | |
1860 * | |
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) | |
1869 { | |
1870 frame = new int[n]; | |
1871 } | |
1872 frame[0] = offset; | |
1873 frame[1] = nLocal; | |
1874 frame[2] = nStack; | |
1875 frameIndex = 3; | |
1876 } | |
1877 | |
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) | |
1886 { | |
1887 stackMap = new ByteVector(); | |
1888 } | |
1889 writeFrame(); | |
1890 ++frameCount; | |
1891 } | |
1892 previousFrame = frame; | |
1893 frame = null; | |
1894 } | |
1895 | |
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) | |
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 else | |
1920 { | |
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 < 63 | |
1947 ? SAME_LOCALS_1_STACK_ITEM_FRAME | |
1948 : SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED; | |
1949 } | |
1950 if(type != FULL_FRAME) | |
1951 { | |
1952 // verify if locals are the same | |
1953 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 } | |
1998 | |
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. | |
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 else | |
2030 { | |
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 else | |
2044 { | |
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 } | |
2076 | |
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 else | |
2087 { | |
2088 stackMap.putByte(8).putShort(((Label) type).position); | |
2089 } | |
2090 } | |
2091 | |
2092 // ------------------------------------------------------------------------ | |
2093 // Utility methods: dump bytecode array | |
2094 // ------------------------------------------------------------------------ | |
2095 | |
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) != 0 | |
2152 && (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 } | |
2207 | |
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 be | |
2212 * 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) != 0 | |
2231 && (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) != 0 | |
2374 && (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 } | |
2419 | |
2420 // ------------------------------------------------------------------------ | |
2421 // Utility methods: instruction resizing (used to handle GOTO_W and JSR_W) | |
2422 // ------------------------------------------------------------------------ | |
2423 | |
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: | |
2449 * | |
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!). | |
2456 * | |
2457 * If at least one entry has been added during the previous step, go | |
2458 * back to the beginning, otherwise stop. | |
2459 * | |
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 | |
2472 | |
2473 resize = new boolean[code.length]; | |
2474 | |
2475 // 3 = loop again, 2 = loop ended, 1 = last pass, 0 = done | |
2476 int state = 3; | |
2477 do | |
2478 { | |
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 instruction | |
2487 int insert = 0; // bytes to be added after this instruction | |
2488 | |
2489 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 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); | |
2503 } | |
2504 else | |
2505 { | |
2506 label = u + readShort(b, u + 1); | |
2507 } | |
2508 newOffset = getNewOffset(allIndexes, allSizes, u, label); | |
2509 if(newOffset < Short.MIN_VALUE | |
2510 || newOffset > Short.MAX_VALUE) | |
2511 { | |
2512 if(!resize[u]) | |
2513 { | |
2514 if(opcode == Opcodes.GOTO | |
2515 || opcode == Opcodes.JSR) | |
2516 { | |
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; | |
2521 } | |
2522 else | |
2523 { | |
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; | |
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 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); | |
2552 } | |
2553 else if(!resize[u]) | |
2554 { | |
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; | |
2560 } | |
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) | |
2567 { | |
2568 // like TABL_INSN | |
2569 newOffset = getNewOffset(allIndexes, allSizes, 0, u); | |
2570 insert = -(newOffset & 3); | |
2571 } | |
2572 else if(!resize[u]) | |
2573 { | |
2574 // like TABL_INSN | |
2575 insert = u & 3; | |
2576 resize[u] = true; | |
2577 } | |
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) | |
2585 { | |
2586 u += 6; | |
2587 } | |
2588 else | |
2589 { | |
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 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) | |
2630 { | |
2631 state = 3; | |
2632 } | |
2633 } | |
2634 } | |
2635 if(state < 3) | |
2636 { | |
2637 --state; | |
2638 } | |
2639 } while(state != 0); | |
2640 | |
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. | |
2644 | |
2645 ByteVector newCode = new ByteVector(code.length); | |
2646 | |
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), 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); | |
2666 } | |
2667 else | |
2668 { | |
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 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) | |
2680 { | |
2681 newCode.putByte(200); // GOTO_W | |
2682 } | |
2683 else if(opcode == Opcodes.JSR) | |
2684 { | |
2685 newCode.putByte(201); // JSR_W | |
2686 } | |
2687 else | |
2688 { | |
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; | |
2696 } | |
2697 newCode.putInt(newOffset); | |
2698 } | |
2699 else | |
2700 { | |
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 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) | |
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 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) | |
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 else | |
2770 { | |
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 } | |
2800 | |
2801 // recomputes the stack map frames | |
2802 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. 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])) | |
2824 { | |
2825 getNewOffset(allIndexes, allSizes, l); | |
2826 // TODO update offsets in UNINITIALIZED values | |
2827 visitFrame(l.frame); | |
2828 } | |
2829 l = l.successor; | |
2830 } | |
2831 } | |
2832 else | |
2833 { | |
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; | |
2849 } | |
2850 } | |
2851 // updates the exception handler block labels | |
2852 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 the | |
2861 // local var and line number tables | |
2862 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 attributes | |
2896 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 } | |
2909 | |
2910 // replaces old bytecodes with new ones | |
2911 code = newCode; | |
2912 } | |
2913 | |
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 } | |
2924 | |
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 } | |
2935 | |
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 } | |
2947 | |
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 } | |
2959 | |
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'). | |
2965 * | |
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) | |
2988 { | |
2989 if(begin < indexes[i] && indexes[i] <= end) | |
2990 { | |
2991 // forward jump | |
2992 offset += sizes[i]; | |
2993 } | |
2994 else if(end < indexes[i] && indexes[i] <= begin) | |
2995 { | |
2996 // backward jump | |
2997 offset -= sizes[i]; | |
2998 } | |
2999 } | |
3000 return offset; | |
3001 } | |
3002 | |
3003 /** | |
3004 * Updates the offset of the given label. | |
3005 * | |
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) | |
3024 { | |
3025 label.position = getNewOffset(indexes, sizes, 0, label.position); | |
3026 label.status |= Label.RESIZED; | |
3027 } | |
3028 } | |
3029 } |