1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 */
18 package org.apache.bcel.generic;
19
20 import java.io.ByteArrayOutputStream;
21 import java.io.DataOutputStream;
22 import java.io.IOException;
23 import java.io.Serializable;
24 import java.util.ArrayList;
25 import java.util.HashMap;
26 import java.util.Iterator;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.NoSuchElementException;
30
31 import org.apache.bcel.Constants;
32 import org.apache.bcel.classfile.Constant;
33 import org.apache.bcel.util.ByteSequence;
34
35 /**
36 * This class is a container for a list of <a
37 * href="Instruction.html">Instruction</a> objects. Instructions can
38 * be appended, inserted, moved, deleted, etc.. Instructions are being
39 * wrapped into <a
40 * href="InstructionHandle.html">InstructionHandles</a> objects that
41 * are returned upon append/insert operations. They give the user
42 * (read only) access to the list structure, such that it can be traversed and
43 * manipulated in a controlled way.
44 *
45 * A list is finally dumped to a byte code array with <a
46 * href="#getByteCode()">getByteCode</a>.
47 *
48 * @version $Id: InstructionList.java 1152077 2011-07-29 02:29:42Z dbrosius $
49 * @author <A HREF="mailto:m.dahm@gmx.de">M. Dahm</A>
50 * @see Instruction
51 * @see InstructionHandle
52 * @see BranchHandle
53 */
54 public class InstructionList implements Serializable {
55
56 private static final long serialVersionUID = 2651389055345707857L;
57 private InstructionHandle start = null, end = null;
58 private int length = 0; // number of elements in list
59 private int[] byte_positions; // byte code offsets corresponding to instructions
60
61
62 /**
63 * Create (empty) instruction list.
64 */
65 public InstructionList() {
66 }
67
68
69 /**
70 * Create instruction list containing one instruction.
71 * @param i initial instruction
72 */
73 public InstructionList(Instruction i) {
74 append(i);
75 }
76
77
78 /**
79 * Create instruction list containing one instruction.
80 * @param i initial instruction
81 */
82 public InstructionList(BranchInstruction i) {
83 append(i);
84 }
85
86
87 /**
88 * Initialize list with (nonnull) compound instruction. Consumes argument
89 * list, i.e., it becomes empty.
90 *
91 * @param c compound instruction (list)
92 */
93 public InstructionList(CompoundInstruction c) {
94 append(c.getInstructionList());
95 }
96
97
98 /**
99 * Test for empty list.
100 */
101 public boolean isEmpty() {
102 return start == null;
103 } // && end == null
104
105
106 /**
107 * Find the target instruction (handle) that corresponds to the given target
108 * position (byte code offset).
109 *
110 * @param ihs array of instruction handles, i.e. il.getInstructionHandles()
111 * @param pos array of positions corresponding to ihs, i.e. il.getInstructionPositions()
112 * @param count length of arrays
113 * @param target target position to search for
114 * @return target position's instruction handle if available
115 */
116 public static InstructionHandle findHandle( InstructionHandle[] ihs, int[] pos, int count,
117 int target ) {
118 int l = 0, r = count - 1;
119 /* Do a binary search since the pos array is orderd.
120 */
121 do {
122 int i = (l + r) / 2;
123 int j = pos[i];
124 if (j == target) {
125 return ihs[i];
126 } else if (target < j) {
127 r = i - 1;
128 } else {
129 l = i + 1;
130 }
131 } while (l <= r);
132 return null;
133 }
134
135
136 /**
137 * Get instruction handle for instruction at byte code position pos.
138 * This only works properly, if the list is freshly initialized from a byte array or
139 * setPositions() has been called before this method.
140 *
141 * @param pos byte code position to search for
142 * @return target position's instruction handle if available
143 */
144 public InstructionHandle findHandle( int pos ) {
145 int[] positions = byte_positions;
146 InstructionHandle ih = start;
147 for (int i = 0; i < length; i++) {
148 if(positions[i] == pos) {
149 return ih;
150 }
151 ih = ih.next;
152 }
153 return null;
154 }
155
156
157 /**
158 * Initialize instruction list from byte array.
159 *
160 * @param code byte array containing the instructions
161 */
162 public InstructionList(byte[] code) {
163 ByteSequence bytes = new ByteSequence(code);
164 InstructionHandle[] ihs = new InstructionHandle[code.length];
165 int[] pos = new int[code.length]; // Can't be more than that
166 int count = 0; // Contains actual length
167 /* Pass 1: Create an object for each byte code and append them
168 * to the list.
169 */
170 try {
171 while (bytes.available() > 0) {
172 // Remember byte offset and associate it with the instruction
173 int off = bytes.getIndex();
174 pos[count] = off;
175 /* Read one instruction from the byte stream, the byte position is set
176 * accordingly.
177 */
178 Instruction i = Instruction.readInstruction(bytes);
179 InstructionHandle ih;
180 if (i instanceof BranchInstruction) {
181 ih = append((BranchInstruction) i);
182 } else {
183 ih = append(i);
184 }
185 ih.setPosition(off);
186 ihs[count] = ih;
187 count++;
188 }
189 } catch (IOException e) {
190 throw new ClassGenException(e.toString(), e);
191 }
192 byte_positions = new int[count]; // Trim to proper size
193 System.arraycopy(pos, 0, byte_positions, 0, count);
194 /* Pass 2: Look for BranchInstruction and update their targets, i.e.,
195 * convert offsets to instruction handles.
196 */
197 for (int i = 0; i < count; i++) {
198 if (ihs[i] instanceof BranchHandle) {
199 BranchInstruction bi = (BranchInstruction) ihs[i].instruction;
200 int target = bi.position + bi.getIndex(); /* Byte code position:
201 * relative -> absolute. */
202 // Search for target position
203 InstructionHandle ih = findHandle(ihs, pos, count, target);
204 if (ih == null) {
205 throw new ClassGenException("Couldn't find target for branch: " + bi);
206 }
207 bi.setTarget(ih); // Update target
208 // If it is a Select instruction, update all branch targets
209 if (bi instanceof Select) { // Either LOOKUPSWITCH or TABLESWITCH
210 Select s = (Select) bi;
211 int[] indices = s.getIndices();
212 for (int j = 0; j < indices.length; j++) {
213 target = bi.position + indices[j];
214 ih = findHandle(ihs, pos, count, target);
215 if (ih == null) {
216 throw new ClassGenException("Couldn't find target for switch: " + bi);
217 }
218 s.setTarget(j, ih); // Update target
219 }
220 }
221 }
222 }
223 }
224
225
226 /**
227 * Append another list after instruction (handle) ih contained in this list.
228 * Consumes argument list, i.e., it becomes empty.
229 *
230 * @param ih where to append the instruction list
231 * @param il Instruction list to append to this one
232 * @return instruction handle pointing to the <B>first</B> appended instruction
233 */
234 public InstructionHandle append( InstructionHandle ih, InstructionList il ) {
235 if (il == null) {
236 throw new ClassGenException("Appending null InstructionList");
237 }
238 if (il.isEmpty()) {
239 return ih;
240 }
241 InstructionHandle next = ih.next, ret = il.start;
242 ih.next = il.start;
243 il.start.prev = ih;
244 il.end.next = next;
245 if (next != null) {
246 next.prev = il.end;
247 } else {
248 end = il.end; // Update end ...
249 }
250 length += il.length; // Update length
251 il.clear();
252 return ret;
253 }
254
255
256 /**
257 * Append another list after instruction i contained in this list.
258 * Consumes argument list, i.e., it becomes empty.
259 *
260 * @param i where to append the instruction list
261 * @param il Instruction list to append to this one
262 * @return instruction handle pointing to the <B>first</B> appended instruction
263 */
264 public InstructionHandle append( Instruction i, InstructionList il ) {
265 InstructionHandle ih;
266 if ((ih = findInstruction2(i)) == null) {
267 throw new ClassGenException("Instruction " + i + " is not contained in this list.");
268 }
269 return append(ih, il);
270 }
271
272
273 /**
274 * Append another list to this one.
275 * Consumes argument list, i.e., it becomes empty.
276 *
277 * @param il list to append to end of this list
278 * @return instruction handle of the <B>first</B> appended instruction
279 */
280 public InstructionHandle append( InstructionList il ) {
281 if (il == null) {
282 throw new ClassGenException("Appending null InstructionList");
283 }
284 if (il.isEmpty()) {
285 return null;
286 }
287 if (isEmpty()) {
288 start = il.start;
289 end = il.end;
290 length = il.length;
291 il.clear();
292 return start;
293 } else {
294 return append(end, il); // was end.instruction
295 }
296 }
297
298
299 /**
300 * Append an instruction to the end of this list.
301 *
302 * @param ih instruction to append
303 */
304 private void append( InstructionHandle ih ) {
305 if (isEmpty()) {
306 start = end = ih;
307 ih.next = ih.prev = null;
308 } else {
309 end.next = ih;
310 ih.prev = end;
311 ih.next = null;
312 end = ih;
313 }
314 length++; // Update length
315 }
316
317
318 /**
319 * Append an instruction to the end of this list.
320 *
321 * @param i instruction to append
322 * @return instruction handle of the appended instruction
323 */
324 public InstructionHandle append( Instruction i ) {
325 InstructionHandle ih = InstructionHandle.getInstructionHandle(i);
326 append(ih);
327 return ih;
328 }
329
330
331 /**
332 * Append a branch instruction to the end of this list.
333 *
334 * @param i branch instruction to append
335 * @return branch instruction handle of the appended instruction
336 */
337 public BranchHandle append( BranchInstruction i ) {
338 BranchHandle ih = BranchHandle.getBranchHandle(i);
339 append(ih);
340 return ih;
341 }
342
343
344 /**
345 * Append a single instruction j after another instruction i, which
346 * must be in this list of course!
347 *
348 * @param i Instruction in list
349 * @param j Instruction to append after i in list
350 * @return instruction handle of the first appended instruction
351 */
352 public InstructionHandle append( Instruction i, Instruction j ) {
353 return append(i, new InstructionList(j));
354 }
355
356
357 /**
358 * Append a compound instruction, after instruction i.
359 *
360 * @param i Instruction in list
361 * @param c The composite instruction (containing an InstructionList)
362 * @return instruction handle of the first appended instruction
363 */
364 public InstructionHandle append( Instruction i, CompoundInstruction c ) {
365 return append(i, c.getInstructionList());
366 }
367
368
369 /**
370 * Append a compound instruction.
371 *
372 * @param c The composite instruction (containing an InstructionList)
373 * @return instruction handle of the first appended instruction
374 */
375 public InstructionHandle append( CompoundInstruction c ) {
376 return append(c.getInstructionList());
377 }
378
379
380 /**
381 * Append a compound instruction.
382 *
383 * @param ih where to append the instruction list
384 * @param c The composite instruction (containing an InstructionList)
385 * @return instruction handle of the first appended instruction
386 */
387 public InstructionHandle append( InstructionHandle ih, CompoundInstruction c ) {
388 return append(ih, c.getInstructionList());
389 }
390
391
392 /**
393 * Append an instruction after instruction (handle) ih contained in this list.
394 *
395 * @param ih where to append the instruction list
396 * @param i Instruction to append
397 * @return instruction handle pointing to the <B>first</B> appended instruction
398 */
399 public InstructionHandle append( InstructionHandle ih, Instruction i ) {
400 return append(ih, new InstructionList(i));
401 }
402
403
404 /**
405 * Append an instruction after instruction (handle) ih contained in this list.
406 *
407 * @param ih where to append the instruction list
408 * @param i Instruction to append
409 * @return instruction handle pointing to the <B>first</B> appended instruction
410 */
411 public BranchHandle append( InstructionHandle ih, BranchInstruction i ) {
412 BranchHandle bh = BranchHandle.getBranchHandle(i);
413 InstructionList il = new InstructionList();
414 il.append(bh);
415 append(ih, il);
416 return bh;
417 }
418
419
420 /**
421 * Insert another list before Instruction handle ih contained in this list.
422 * Consumes argument list, i.e., it becomes empty.
423 *
424 * @param ih where to append the instruction list
425 * @param il Instruction list to insert
426 * @return instruction handle of the first inserted instruction
427 */
428 public InstructionHandle insert( InstructionHandle ih, InstructionList il ) {
429 if (il == null) {
430 throw new ClassGenException("Inserting null InstructionList");
431 }
432 if (il.isEmpty()) {
433 return ih;
434 }
435 InstructionHandle prev = ih.prev, ret = il.start;
436 ih.prev = il.end;
437 il.end.next = ih;
438 il.start.prev = prev;
439 if (prev != null) {
440 prev.next = il.start;
441 } else {
442 start = il.start; // Update start ...
443 }
444 length += il.length; // Update length
445 il.clear();
446 return ret;
447 }
448
449
450 /**
451 * Insert another list.
452 *
453 * @param il list to insert before start of this list
454 * @return instruction handle of the first inserted instruction
455 */
456 public InstructionHandle insert( InstructionList il ) {
457 if (isEmpty()) {
458 append(il); // Code is identical for this case
459 return start;
460 } else {
461 return insert(start, il);
462 }
463 }
464
465
466 /**
467 * Insert an instruction at start of this list.
468 *
469 * @param ih instruction to insert
470 */
471 private void insert( InstructionHandle ih ) {
472 if (isEmpty()) {
473 start = end = ih;
474 ih.next = ih.prev = null;
475 } else {
476 start.prev = ih;
477 ih.next = start;
478 ih.prev = null;
479 start = ih;
480 }
481 length++;
482 }
483
484
485 /**
486 * Insert another list before Instruction i contained in this list.
487 * Consumes argument list, i.e., it becomes empty.
488 *
489 * @param i where to append the instruction list
490 * @param il Instruction list to insert
491 * @return instruction handle pointing to the first inserted instruction,
492 * i.e., il.getStart()
493 */
494 public InstructionHandle insert( Instruction i, InstructionList il ) {
495 InstructionHandle ih;
496 if ((ih = findInstruction1(i)) == null) {
497 throw new ClassGenException("Instruction " + i + " is not contained in this list.");
498 }
499 return insert(ih, il);
500 }
501
502
503 /**
504 * Insert an instruction at start of this list.
505 *
506 * @param i instruction to insert
507 * @return instruction handle of the inserted instruction
508 */
509 public InstructionHandle insert( Instruction i ) {
510 InstructionHandle ih = InstructionHandle.getInstructionHandle(i);
511 insert(ih);
512 return ih;
513 }
514
515
516 /**
517 * Insert a branch instruction at start of this list.
518 *
519 * @param i branch instruction to insert
520 * @return branch instruction handle of the appended instruction
521 */
522 public BranchHandle insert( BranchInstruction i ) {
523 BranchHandle ih = BranchHandle.getBranchHandle(i);
524 insert(ih);
525 return ih;
526 }
527
528
529 /**
530 * Insert a single instruction j before another instruction i, which
531 * must be in this list of course!
532 *
533 * @param i Instruction in list
534 * @param j Instruction to insert before i in list
535 * @return instruction handle of the first inserted instruction
536 */
537 public InstructionHandle insert( Instruction i, Instruction j ) {
538 return insert(i, new InstructionList(j));
539 }
540
541
542 /**
543 * Insert a compound instruction before instruction i.
544 *
545 * @param i Instruction in list
546 * @param c The composite instruction (containing an InstructionList)
547 * @return instruction handle of the first inserted instruction
548 */
549 public InstructionHandle insert( Instruction i, CompoundInstruction c ) {
550 return insert(i, c.getInstructionList());
551 }
552
553
554 /**
555 * Insert a compound instruction.
556 *
557 * @param c The composite instruction (containing an InstructionList)
558 * @return instruction handle of the first inserted instruction
559 */
560 public InstructionHandle insert( CompoundInstruction c ) {
561 return insert(c.getInstructionList());
562 }
563
564
565 /**
566 * Insert an instruction before instruction (handle) ih contained in this list.
567 *
568 * @param ih where to insert to the instruction list
569 * @param i Instruction to insert
570 * @return instruction handle of the first inserted instruction
571 */
572 public InstructionHandle insert( InstructionHandle ih, Instruction i ) {
573 return insert(ih, new InstructionList(i));
574 }
575
576
577 /**
578 * Insert a compound instruction.
579 *
580 * @param ih where to insert the instruction list
581 * @param c The composite instruction (containing an InstructionList)
582 * @return instruction handle of the first inserted instruction
583 */
584 public InstructionHandle insert( InstructionHandle ih, CompoundInstruction c ) {
585 return insert(ih, c.getInstructionList());
586 }
587
588
589 /**
590 * Insert an instruction before instruction (handle) ih contained in this list.
591 *
592 * @param ih where to insert to the instruction list
593 * @param i Instruction to insert
594 * @return instruction handle of the first inserted instruction
595 */
596 public BranchHandle insert( InstructionHandle ih, BranchInstruction i ) {
597 BranchHandle bh = BranchHandle.getBranchHandle(i);
598 InstructionList il = new InstructionList();
599 il.append(bh);
600 insert(ih, il);
601 return bh;
602 }
603
604
605 /**
606 * Take all instructions (handles) from "start" to "end" and append them after the
607 * new location "target". Of course, "end" must be after "start" and target must
608 * not be located withing this range. If you want to move something to the start of
609 * the list use null as value for target.<br>
610 * Any instruction targeters pointing to handles within the block, keep their targets.
611 *
612 * @param start of moved block
613 * @param end of moved block
614 * @param target of moved block
615 */
616 public void move( InstructionHandle start, InstructionHandle end, InstructionHandle target ) {
617 // Step 1: Check constraints
618 if ((start == null) || (end == null)) {
619 throw new ClassGenException("Invalid null handle: From " + start + " to " + end);
620 }
621 if ((target == start) || (target == end)) {
622 throw new ClassGenException("Invalid range: From " + start + " to " + end
623 + " contains target " + target);
624 }
625 for (InstructionHandle ih = start; ih != end.next; ih = ih.next) {
626 if (ih == null) {
627 throw new ClassGenException("Invalid range: From " + start + " to " + end);
628 } else if (ih == target) {
629 throw new ClassGenException("Invalid range: From " + start + " to " + end
630 + " contains target " + target);
631 }
632 }
633 // Step 2: Temporarily remove the given instructions from the list
634 InstructionHandle prev = start.prev, next = end.next;
635 if (prev != null) {
636 prev.next = next;
637 } else {
638 this.start = next;
639 }
640 if (next != null) {
641 next.prev = prev;
642 } else {
643 this.end = prev;
644 }
645 start.prev = end.next = null;
646 // Step 3: append after target
647 if (target == null) { // append to start of list
648 if (this.start != null) {
649 this.start.prev = end;
650 }
651 end.next = this.start;
652 this.start = start;
653 } else {
654 next = target.next;
655 target.next = start;
656 start.prev = target;
657 end.next = next;
658 if (next != null) {
659 next.prev = end;
660 } else {
661 this.end = end;
662 }
663 }
664 }
665
666
667 /**
668 * Move a single instruction (handle) to a new location.
669 *
670 * @param ih moved instruction
671 * @param target new location of moved instruction
672 */
673 public void move( InstructionHandle ih, InstructionHandle target ) {
674 move(ih, ih, target);
675 }
676
677
678 /**
679 * Remove from instruction `prev' to instruction `next' both contained
680 * in this list. Throws TargetLostException when one of the removed instruction handles
681 * is still being targeted.
682 *
683 * @param prev where to start deleting (predecessor, exclusive)
684 * @param next where to end deleting (successor, exclusive)
685 */
686 private void remove( InstructionHandle prev, InstructionHandle next )
687 throws TargetLostException {
688 InstructionHandle first, last; // First and last deleted instruction
689 if ((prev == null) && (next == null)) {
690 first = start;
691 last = end;
692 start = end = null;
693 } else {
694 if (prev == null) { // At start of list
695 first = start;
696 start = next;
697 } else {
698 first = prev.next;
699 prev.next = next;
700 }
701 if (next == null) { // At end of list
702 last = end;
703 end = prev;
704 } else {
705 last = next.prev;
706 next.prev = prev;
707 }
708 }
709 first.prev = null; // Completely separated from rest of list
710 last.next = null;
711 List<InstructionHandle> target_vec = new ArrayList<InstructionHandle>();
712 for (InstructionHandle ih = first; ih != null; ih = ih.next) {
713 ih.getInstruction().dispose(); // e.g. BranchInstructions release their targets
714 }
715 StringBuilder buf = new StringBuilder("{ ");
716 for (InstructionHandle ih = first; ih != null; ih = next) {
717 next = ih.next;
718 length--;
719 if (ih.hasTargeters()) { // Still got targeters?
720 target_vec.add(ih);
721 buf.append(ih.toString(true) + " ");
722 ih.next = ih.prev = null;
723 } else {
724 ih.dispose();
725 }
726 }
727 buf.append("}");
728 if (!target_vec.isEmpty()) {
729 InstructionHandle[] targeted = new InstructionHandle[target_vec.size()];
730 target_vec.toArray(targeted);
731 throw new TargetLostException(targeted, buf.toString());
732 }
733 }
734
735
736 /**
737 * Remove instruction from this list. The corresponding Instruction
738 * handles must not be reused!
739 *
740 * @param ih instruction (handle) to remove
741 */
742 public void delete( InstructionHandle ih ) throws TargetLostException {
743 remove(ih.prev, ih.next);
744 }
745
746
747 /**
748 * Remove instruction from this list. The corresponding Instruction
749 * handles must not be reused!
750 *
751 * @param i instruction to remove
752 */
753 public void delete( Instruction i ) throws TargetLostException {
754 InstructionHandle ih;
755 if ((ih = findInstruction1(i)) == null) {
756 throw new ClassGenException("Instruction " + i + " is not contained in this list.");
757 }
758 delete(ih);
759 }
760
761
762 /**
763 * Remove instructions from instruction `from' to instruction `to' contained
764 * in this list. The user must ensure that `from' is an instruction before
765 * `to', or risk havoc. The corresponding Instruction handles must not be reused!
766 *
767 * @param from where to start deleting (inclusive)
768 * @param to where to end deleting (inclusive)
769 */
770 public void delete( InstructionHandle from, InstructionHandle to ) throws TargetLostException {
771 remove(from.prev, to.next);
772 }
773
774
775 /**
776 * Remove instructions from instruction `from' to instruction `to' contained
777 * in this list. The user must ensure that `from' is an instruction before
778 * `to', or risk havoc. The corresponding Instruction handles must not be reused!
779 *
780 * @param from where to start deleting (inclusive)
781 * @param to where to end deleting (inclusive)
782 */
783 public void delete( Instruction from, Instruction to ) throws TargetLostException {
784 InstructionHandle from_ih, to_ih;
785 if ((from_ih = findInstruction1(from)) == null) {
786 throw new ClassGenException("Instruction " + from + " is not contained in this list.");
787 }
788 if ((to_ih = findInstruction2(to)) == null) {
789 throw new ClassGenException("Instruction " + to + " is not contained in this list.");
790 }
791 delete(from_ih, to_ih);
792 }
793
794
795 /**
796 * Search for given Instruction reference, start at beginning of list.
797 *
798 * @param i instruction to search for
799 * @return instruction found on success, null otherwise
800 */
801 private InstructionHandle findInstruction1( Instruction i ) {
802 for (InstructionHandle ih = start; ih != null; ih = ih.next) {
803 if (ih.instruction == i) {
804 return ih;
805 }
806 }
807 return null;
808 }
809
810
811 /**
812 * Search for given Instruction reference, start at end of list
813 *
814 * @param i instruction to search for
815 * @return instruction found on success, null otherwise
816 */
817 private InstructionHandle findInstruction2( Instruction i ) {
818 for (InstructionHandle ih = end; ih != null; ih = ih.prev) {
819 if (ih.instruction == i) {
820 return ih;
821 }
822 }
823 return null;
824 }
825
826
827 public boolean contains( InstructionHandle i ) {
828 if (i == null) {
829 return false;
830 }
831 for (InstructionHandle ih = start; ih != null; ih = ih.next) {
832 if (ih == i) {
833 return true;
834 }
835 }
836 return false;
837 }
838
839
840 public boolean contains( Instruction i ) {
841 return findInstruction1(i) != null;
842 }
843
844
845 public void setPositions() {
846 setPositions(false);
847 }
848
849
850 /**
851 * Give all instructions their position number (offset in byte stream), i.e.,
852 * make the list ready to be dumped.
853 *
854 * @param check Perform sanity checks, e.g. if all targeted instructions really belong
855 * to this list
856 */
857 public void setPositions( boolean check ) {
858 int max_additional_bytes = 0, additional_bytes = 0;
859 int index = 0, count = 0;
860 int[] pos = new int[length];
861 /* Pass 0: Sanity checks
862 */
863 if (check) {
864 for (InstructionHandle ih = start; ih != null; ih = ih.next) {
865 Instruction i = ih.instruction;
866 if (i instanceof BranchInstruction) { // target instruction within list?
867 Instruction inst = ((BranchInstruction) i).getTarget().instruction;
868 if (!contains(inst)) {
869 throw new ClassGenException("Branch target of "
870 + Constants.OPCODE_NAMES[i.opcode] + ":" + inst
871 + " not in instruction list");
872 }
873 if (i instanceof Select) {
874 InstructionHandle[] targets = ((Select) i).getTargets();
875 for (int j = 0; j < targets.length; j++) {
876 inst = targets[j].instruction;
877 if (!contains(inst)) {
878 throw new ClassGenException("Branch target of "
879 + Constants.OPCODE_NAMES[i.opcode] + ":" + inst
880 + " not in instruction list");
881 }
882 }
883 }
884 if (!(ih instanceof BranchHandle)) {
885 throw new ClassGenException("Branch instruction "
886 + Constants.OPCODE_NAMES[i.opcode] + ":" + inst
887 + " not contained in BranchHandle.");
888 }
889 }
890 }
891 }
892 /* Pass 1: Set position numbers and sum up the maximum number of bytes an
893 * instruction may be shifted.
894 */
895 for (InstructionHandle ih = start; ih != null; ih = ih.next) {
896 Instruction i = ih.instruction;
897 ih.setPosition(index);
898 pos[count++] = index;
899 /* Get an estimate about how many additional bytes may be added, because
900 * BranchInstructions may have variable length depending on the target
901 * offset (short vs. int) or alignment issues (TABLESWITCH and
902 * LOOKUPSWITCH).
903 */
904 switch (i.getOpcode()) {
905 case Constants.JSR:
906 case Constants.GOTO:
907 max_additional_bytes += 2;
908 break;
909 case Constants.TABLESWITCH:
910 case Constants.LOOKUPSWITCH:
911 max_additional_bytes += 3;
912 break;
913 }
914 index += i.getLength();
915 }
916 /* Pass 2: Expand the variable-length (Branch)Instructions depending on
917 * the target offset (short or int) and ensure that branch targets are
918 * within this list.
919 */
920 for (InstructionHandle ih = start; ih != null; ih = ih.next) {
921 additional_bytes += ih.updatePosition(additional_bytes, max_additional_bytes);
922 }
923 /* Pass 3: Update position numbers (which may have changed due to the
924 * preceding expansions), like pass 1.
925 */
926 index = count = 0;
927 for (InstructionHandle ih = start; ih != null; ih = ih.next) {
928 Instruction i = ih.instruction;
929 ih.setPosition(index);
930 pos[count++] = index;
931 index += i.getLength();
932 }
933 byte_positions = new int[count]; // Trim to proper size
934 System.arraycopy(pos, 0, byte_positions, 0, count);
935 }
936
937
938 /**
939 * When everything is finished, use this method to convert the instruction
940 * list into an array of bytes.
941 *
942 * @return the byte code ready to be dumped
943 */
944 public byte[] getByteCode() {
945 // Update position indices of instructions
946 setPositions();
947 ByteArrayOutputStream b = new ByteArrayOutputStream();
948 DataOutputStream out = new DataOutputStream(b);
949 try {
950 for (InstructionHandle ih = start; ih != null; ih = ih.next) {
951 Instruction i = ih.instruction;
952 i.dump(out); // Traverse list
953 }
954 } catch (IOException e) {
955 System.err.println(e);
956 return new byte[0];
957 }
958 return b.toByteArray();
959 }
960
961
962 /**
963 * @return an array of instructions without target information for branch instructions.
964 */
965 public Instruction[] getInstructions() {
966 ByteSequence bytes = new ByteSequence(getByteCode());
967 List<Instruction> instructions = new ArrayList<Instruction>();
968 try {
969 while (bytes.available() > 0) {
970 instructions.add(Instruction.readInstruction(bytes));
971 }
972 } catch (IOException e) {
973 throw new ClassGenException(e.toString(), e);
974 }
975 return instructions.toArray(new Instruction[instructions.size()]);
976 }
977
978
979 @Override
980 public String toString() {
981 return toString(true);
982 }
983
984
985 /**
986 * @param verbose toggle output format
987 * @return String containing all instructions in this list.
988 */
989 public String toString( boolean verbose ) {
990 StringBuilder buf = new StringBuilder();
991 for (InstructionHandle ih = start; ih != null; ih = ih.next) {
992 buf.append(ih.toString(verbose)).append("\n");
993 }
994 return buf.toString();
995 }
996
997
998 /**
999 * @return iterator that lists all instructions (handles)
1000 */
1001 public Iterator<InstructionHandle> iterator() {
1002 return new Iterator<InstructionHandle>() {
1003
1004 private InstructionHandle ih = start;
1005
1006
1007 public InstructionHandle next() throws NoSuchElementException {
1008 if (ih == null)
1009 throw new NoSuchElementException();
1010 InstructionHandle i = ih;
1011 ih = ih.next;
1012 return i;
1013 }
1014
1015
1016 public void remove() {
1017 throw new UnsupportedOperationException();
1018 }
1019
1020
1021 public boolean hasNext() {
1022 return ih != null;
1023 }
1024 };
1025 }
1026
1027
1028 /**
1029 * @return array containing all instructions (handles)
1030 */
1031 public InstructionHandle[] getInstructionHandles() {
1032 InstructionHandle[] ihs = new InstructionHandle[length];
1033 InstructionHandle ih = start;
1034 for (int i = 0; i < length; i++) {
1035 ihs[i] = ih;
1036 ih = ih.next;
1037 }
1038 return ihs;
1039 }
1040
1041
1042 /**
1043 * Get positions (offsets) of all instructions in the list. This relies on that
1044 * the list has been freshly created from an byte code array, or that setPositions()
1045 * has been called. Otherwise this may be inaccurate.
1046 *
1047 * @return array containing all instruction's offset in byte code
1048 */
1049 public int[] getInstructionPositions() {
1050 return byte_positions;
1051 }
1052
1053
1054 /**
1055 * @return complete, i.e., deep copy of this list
1056 */
1057 public InstructionList copy() {
1058 Map<InstructionHandle, InstructionHandle> map = new HashMap<InstructionHandle, InstructionHandle>();
1059 InstructionList il = new InstructionList();
1060 /* Pass 1: Make copies of all instructions, append them to the new list
1061 * and associate old instruction references with the new ones, i.e.,
1062 * a 1:1 mapping.
1063 */
1064 for (InstructionHandle ih = start; ih != null; ih = ih.next) {
1065 Instruction i = ih.instruction;
1066 Instruction c = i.copy(); // Use clone for shallow copy
1067 if (c instanceof BranchInstruction) {
1068 map.put(ih, il.append((BranchInstruction) c));
1069 } else {
1070 map.put(ih, il.append(c));
1071 }
1072 }
1073 /* Pass 2: Update branch targets.
1074 */
1075 InstructionHandle ih = start;
1076 InstructionHandle ch = il.start;
1077 while (ih != null) {
1078 Instruction i = ih.instruction;
1079 Instruction c = ch.instruction;
1080 if (i instanceof BranchInstruction) {
1081 BranchInstruction bi = (BranchInstruction) i;
1082 BranchInstruction bc = (BranchInstruction) c;
1083 InstructionHandle itarget = bi.getTarget(); // old target
1084 // New target is in hash map
1085 bc.setTarget(map.get(itarget));
1086 if (bi instanceof Select) { // Either LOOKUPSWITCH or TABLESWITCH
1087 InstructionHandle[] itargets = ((Select) bi).getTargets();
1088 InstructionHandle[] ctargets = ((Select) bc).getTargets();
1089 for (int j = 0; j < itargets.length; j++) { // Update all targets
1090 ctargets[j] = map.get(itargets[j]);
1091 }
1092 }
1093 }
1094 ih = ih.next;
1095 ch = ch.next;
1096 }
1097 return il;
1098 }
1099
1100
1101 /** Replace all references to the old constant pool with references to the new
1102 * constant pool
1103 */
1104 public void replaceConstantPool( ConstantPoolGen old_cp, ConstantPoolGen new_cp ) {
1105 for (InstructionHandle ih = start; ih != null; ih = ih.next) {
1106 Instruction i = ih.instruction;
1107 if (i instanceof CPInstruction) {
1108 CPInstruction ci = (CPInstruction) i;
1109 Constant c = old_cp.getConstant(ci.getIndex());
1110 ci.setIndex(new_cp.addConstant(c, old_cp));
1111 }
1112 }
1113 }
1114
1115
1116 private void clear() {
1117 start = end = null;
1118 length = 0;
1119 }
1120
1121
1122 /**
1123 * Delete contents of list. Provides besser memory utilization,
1124 * because the system then may reuse the instruction handles. This
1125 * method is typically called right after
1126 * <href="MethodGen.html#getMethod()">MethodGen.getMethod()</a>.
1127 */
1128 public void dispose() {
1129 // Traverse in reverse order, because ih.next is overwritten
1130 for (InstructionHandle ih = end; ih != null; ih = ih.prev) {
1131 /* Causes BranchInstructions to release target and targeters, because it
1132 * calls dispose() on the contained instruction.
1133 */
1134 ih.dispose();
1135 }
1136 clear();
1137 }
1138
1139
1140 /**
1141 * @return start of list
1142 */
1143 public InstructionHandle getStart() {
1144 return start;
1145 }
1146
1147
1148 /**
1149 * @return end of list
1150 */
1151 public InstructionHandle getEnd() {
1152 return end;
1153 }
1154
1155
1156 /**
1157 * @return length of list (Number of instructions, not bytes)
1158 */
1159 public int getLength() {
1160 return length;
1161 }
1162
1163
1164 /**
1165 * @return length of list (Number of instructions, not bytes)
1166 */
1167 public int size() {
1168 return length;
1169 }
1170
1171
1172 /**
1173 * Redirect all references from old_target to new_target, i.e., update targets
1174 * of branch instructions.
1175 *
1176 * @param old_target the old target instruction handle
1177 * @param new_target the new target instruction handle
1178 */
1179 public void redirectBranches( InstructionHandle old_target, InstructionHandle new_target ) {
1180 for (InstructionHandle ih = start; ih != null; ih = ih.next) {
1181 Instruction i = ih.getInstruction();
1182 if (i instanceof BranchInstruction) {
1183 BranchInstruction b = (BranchInstruction) i;
1184 InstructionHandle target = b.getTarget();
1185 if (target == old_target) {
1186 b.setTarget(new_target);
1187 }
1188 if (b instanceof Select) { // Either LOOKUPSWITCH or TABLESWITCH
1189 InstructionHandle[] targets = ((Select) b).getTargets();
1190 for (int j = 0; j < targets.length; j++) {
1191 if (targets[j] == old_target) {
1192 ((Select) b).setTarget(j, new_target);
1193 }
1194 }
1195 }
1196 }
1197 }
1198 }
1199
1200
1201 /**
1202 * Redirect all references of local variables from old_target to new_target.
1203 *
1204 * @param lg array of local variables
1205 * @param old_target the old target instruction handle
1206 * @param new_target the new target instruction handle
1207 * @see MethodGen
1208 */
1209 public void redirectLocalVariables( LocalVariableGen[] lg, InstructionHandle old_target,
1210 InstructionHandle new_target ) {
1211 for (int i = 0; i < lg.length; i++) {
1212 InstructionHandle start = lg[i].getStart();
1213 InstructionHandle end = lg[i].getEnd();
1214 if (start == old_target) {
1215 lg[i].setStart(new_target);
1216 }
1217 if (end == old_target) {
1218 lg[i].setEnd(new_target);
1219 }
1220 }
1221 }
1222
1223
1224 /**
1225 * Redirect all references of exception handlers from old_target to new_target.
1226 *
1227 * @param exceptions array of exception handlers
1228 * @param old_target the old target instruction handle
1229 * @param new_target the new target instruction handle
1230 * @see MethodGen
1231 */
1232 public void redirectExceptionHandlers( CodeExceptionGen[] exceptions,
1233 InstructionHandle old_target, InstructionHandle new_target ) {
1234 for (int i = 0; i < exceptions.length; i++) {
1235 if (exceptions[i].getStartPC() == old_target) {
1236 exceptions[i].setStartPC(new_target);
1237 }
1238 if (exceptions[i].getEndPC() == old_target) {
1239 exceptions[i].setEndPC(new_target);
1240 }
1241 if (exceptions[i].getHandlerPC() == old_target) {
1242 exceptions[i].setHandlerPC(new_target);
1243 }
1244 }
1245 }
1246
1247 private List<InstructionListObserver> observers;
1248
1249
1250 /** Add observer for this object.
1251 */
1252 public void addObserver( InstructionListObserver o ) {
1253 if (observers == null) {
1254 observers = new ArrayList<InstructionListObserver>();
1255 }
1256 observers.add(o);
1257 }
1258
1259
1260 /** Remove observer for this object.
1261 */
1262 public void removeObserver( InstructionListObserver o ) {
1263 if (observers != null) {
1264 observers.remove(o);
1265 }
1266 }
1267
1268
1269 /** Call notify() method on all observers. This method is not called
1270 * automatically whenever the state has changed, but has to be
1271 * called by the user after he has finished editing the object.
1272 */
1273 public void update() {
1274 if (observers != null) {
1275 for (InstructionListObserver observer : observers) {
1276 observer.notify(this);
1277 }
1278 }
1279 }
1280 }