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