View Javadoc

1   /*
2    * Copyright  2000-2004 The Apache Software Foundation
3    *
4    *  Licensed under the Apache License, Version 2.0 (the "License"); 
5    *  you may not use this file except in compliance with the License.
6    *  You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *  Unless required by applicable law or agreed to in writing, software
11   *  distributed under the License is distributed on an "AS IS" BASIS,
12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *  See the License for the specific language governing permissions and
14   *  limitations under the License. 
15   *
16   */
17  package org.apache.bcel.generic;
18  
19  import java.io.DataOutputStream;
20  import java.io.IOException;
21  import org.apache.bcel.util.ByteSequence;
22  
23  /*** 
24   * Abstract super class for branching instructions like GOTO, IFEQ, etc..
25   * Branch instructions may have a variable length, namely GOTO, JSR, 
26   * LOOKUPSWITCH and TABLESWITCH.
27   *
28   * @see InstructionList
29   * @version $Id: BranchInstruction.java 386056 2006-03-15 11:31:56Z tcurdt $
30   * @author  <A HREF="mailto:m.dahm@gmx.de">M. Dahm</A>
31   */
32  public abstract class BranchInstruction extends Instruction implements InstructionTargeter {
33  
34      protected int index; // Branch target relative to this instruction
35      protected InstructionHandle target; // Target object in instruction list
36      protected int position; // Byte code offset
37  
38  
39      /***
40       * Empty constructor needed for the Class.newInstance() statement in
41       * Instruction.readInstruction(). Not to be used otherwise.
42       */
43      BranchInstruction() {
44      }
45  
46  
47      /*** Common super constructor
48       * @param opcode Instruction opcode
49       * @param target instruction to branch to
50       */
51      protected BranchInstruction(short opcode, InstructionHandle target) {
52          super(opcode, (short) 3);
53          setTarget(target);
54      }
55  
56  
57      /***
58       * Dump instruction as byte code to stream out.
59       * @param out Output stream
60       */
61      public void dump( DataOutputStream out ) throws IOException {
62          out.writeByte(opcode);
63          index = getTargetOffset();
64          if (Math.abs(index) >= 32767) {
65              throw new ClassGenException("Branch target offset too large for short");
66          }
67          out.writeShort(index); // May be negative, i.e., point backwards
68      }
69  
70  
71      /***
72       * @param _target branch target
73       * @return the offset to  `target' relative to this instruction
74       */
75      protected int getTargetOffset( InstructionHandle _target ) {
76          if (_target == null) {
77              throw new ClassGenException("Target of " + super.toString(true)
78                      + " is invalid null handle");
79          }
80          int t = _target.getPosition();
81          if (t < 0) {
82              throw new ClassGenException("Invalid branch target position offset for "
83                      + super.toString(true) + ":" + t + ":" + _target);
84          }
85          return t - position;
86      }
87  
88  
89      /***
90       * @return the offset to this instruction's target
91       */
92      protected int getTargetOffset() {
93          return getTargetOffset(target);
94      }
95  
96  
97      /***
98       * Called by InstructionList.setPositions when setting the position for every
99       * instruction. In the presence of variable length instructions `setPositions'
100      * performs multiple passes over the instruction list to calculate the
101      * correct (byte) positions and offsets by calling this function.
102      *
103      * @param offset additional offset caused by preceding (variable length) instructions
104      * @param max_offset the maximum offset that may be caused by these instructions
105      * @return additional offset caused by possible change of this instruction's length
106      */
107     protected int updatePosition( int offset, int max_offset ) {
108         position += offset;
109         return 0;
110     }
111 
112 
113     /***
114      * Long output format:
115      *
116      * &lt;position in byte code&gt;
117      * &lt;name of opcode&gt; "["&lt;opcode number&gt;"]" 
118      * "("&lt;length of instruction&gt;")"
119      * "&lt;"&lt;target instruction&gt;"&gt;" "@"&lt;branch target offset&gt;
120      *
121      * @param verbose long/short format switch
122      * @return mnemonic for instruction
123      */
124     public String toString( boolean verbose ) {
125         String s = super.toString(verbose);
126         String t = "null";
127         if (verbose) {
128             if (target != null) {
129                 if (target.getInstruction() == this) {
130                     t = "<points to itself>";
131                 } else if (target.getInstruction() == null) {
132                     t = "<null instruction!!!?>";
133                 } else {
134                     t = target.getInstruction().toString(false); // Avoid circles
135                 }
136             }
137         } else {
138             if (target != null) {
139                 index = getTargetOffset();
140                 t = "" + (index + position);
141             }
142         }
143         return s + " -> " + t;
144     }
145 
146 
147     /***
148      * Read needed data (e.g. index) from file. Conversion to a InstructionHandle
149      * is done in InstructionList(byte[]).
150      *
151      * @param bytes input stream
152      * @param wide wide prefix?
153      * @see InstructionList
154      */
155     protected void initFromFile( ByteSequence bytes, boolean wide ) throws IOException {
156         length = 3;
157         index = bytes.readShort();
158     }
159 
160 
161     /***
162      * @return target offset in byte code
163      */
164     public final int getIndex() {
165         return index;
166     }
167 
168 
169     /***
170      * @return target of branch instruction
171      */
172     public InstructionHandle getTarget() {
173         return target;
174     }
175 
176 
177     /***
178      * Set branch target
179      * @param target branch target
180      */
181     public void setTarget( InstructionHandle target ) {
182         notifyTarget(this.target, target, this);
183         this.target = target;
184     }
185 
186 
187     /***
188      * Used by BranchInstruction, LocalVariableGen, CodeExceptionGen
189      */
190     static final void notifyTarget( InstructionHandle old_ih, InstructionHandle new_ih,
191             InstructionTargeter t ) {
192         if (old_ih != null) {
193             old_ih.removeTargeter(t);
194         }
195         if (new_ih != null) {
196             new_ih.addTargeter(t);
197         }
198     }
199 
200 
201     /***
202      * @param old_ih old target
203      * @param new_ih new target
204      */
205     public void updateTarget( InstructionHandle old_ih, InstructionHandle new_ih ) {
206         if (target == old_ih) {
207             setTarget(new_ih);
208         } else {
209             throw new ClassGenException("Not targeting " + old_ih + ", but " + target);
210         }
211     }
212 
213 
214     /***
215      * @return true, if ih is target of this instruction
216      */
217     public boolean containsTarget( InstructionHandle ih ) {
218         return (target == ih);
219     }
220 
221 
222     /***
223      * Inform target that it's not targeted anymore.
224      */
225     void dispose() {
226         setTarget(null);
227         index = -1;
228         position = -1;
229     }
230 }