View Javadoc
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  package org.apache.bcel.generic;
18  
19  import java.io.DataOutputStream;
20  import java.io.IOException;
21  
22  import org.apache.bcel.util.ByteSequence;
23  
24  /**
25   * Abstract super class for branching instructions like GOTO, IFEQ, etc.. Branch instructions may have a variable
26   * length, namely GOTO, JSR, LOOKUPSWITCH and TABLESWITCH.
27   *
28   * @see InstructionList
29   */
30  public abstract class BranchInstruction extends Instruction implements InstructionTargeter {
31  
32      /**
33       * Used by BranchInstruction, LocalVariableGen, CodeExceptionGen, LineNumberGen
34       */
35      static void notifyTarget(final InstructionHandle oldIh, final InstructionHandle newIh, final InstructionTargeter t) {
36          if (oldIh != null) {
37              oldIh.removeTargeter(t);
38          }
39          if (newIh != null) {
40              newIh.addTargeter(t);
41          }
42      }
43  
44      /**
45       * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
46       */
47      @Deprecated
48      protected int index; // Branch target relative to this instruction
49  
50      /**
51       * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
52       */
53      @Deprecated
54      protected InstructionHandle target; // Target object in instruction list
55  
56      /**
57       * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
58       */
59      @Deprecated
60      protected int position; // Byte code offset
61  
62      /**
63       * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise.
64       */
65      BranchInstruction() {
66      }
67  
68      /**
69       * Common super constructor
70       *
71       * @param opcode Instruction opcode
72       * @param target instruction to branch to
73       */
74      protected BranchInstruction(final short opcode, final InstructionHandle target) {
75          super(opcode, (short) 3);
76          setTarget(target);
77      }
78  
79      /**
80       * @return true, if ih is target of this instruction
81       */
82      @Override
83      public boolean containsTarget(final InstructionHandle ih) {
84          return target == ih;
85      }
86  
87      /**
88       * Inform target that it's not targeted anymore.
89       */
90      @Override
91      void dispose() {
92          setTarget(null);
93          index = -1;
94          position = -1;
95      }
96  
97      /**
98       * Dump instruction as byte code to stream out.
99       *
100      * @param out Output stream
101      */
102     @Override
103     public void dump(final DataOutputStream out) throws IOException {
104         out.writeByte(super.getOpcode());
105         index = getTargetOffset();
106         if (!isValidShort(index)) {
107             throw new ClassGenException("Branch target offset too large for short: " + index);
108         }
109         out.writeShort(index); // May be negative, i.e., point backwards
110     }
111 
112     /**
113      * @return target offset in byte code
114      */
115     public final int getIndex() {
116         return index;
117     }
118 
119     /**
120      * @return the position
121      * @since 6.0
122      */
123     protected int getPosition() {
124         return position;
125     }
126 
127     /**
128      * @return target of branch instruction
129      */
130     public InstructionHandle getTarget() {
131         return target;
132     }
133 
134     /**
135      * @return the offset to this instruction's target
136      */
137     protected int getTargetOffset() {
138         return getTargetOffset(target);
139     }
140 
141     /**
142      * @param target branch target
143      * @return the offset to 'target' relative to this instruction
144      */
145     protected int getTargetOffset(final InstructionHandle target) {
146         if (target == null) {
147             throw new ClassGenException("Target of " + super.toString(true) + " is invalid null handle");
148         }
149         final int t = target.getPosition();
150         if (t < 0) {
151             throw new ClassGenException("Invalid branch target position offset for " + super.toString(true) + ":" + t + ":" + target);
152         }
153         return t - position;
154     }
155 
156     /**
157      * Read needed data (e.g. index) from file. Conversion to a InstructionHandle is done in InstructionList(byte[]).
158      *
159      * @param bytes input stream
160      * @param wide wide prefix?
161      * @see InstructionList
162      */
163     @Override
164     protected void initFromFile(final ByteSequence bytes, final boolean wide) throws IOException {
165         super.setLength(3);
166         index = bytes.readShort();
167     }
168 
169     /**
170      * @param index the index to set
171      * @since 6.0
172      */
173     protected void setIndex(final int index) {
174         this.index = index;
175     }
176 
177     /**
178      * @param position the position to set
179      * @since 6.0
180      */
181     protected void setPosition(final int position) {
182         this.position = position;
183     }
184 
185     /**
186      * Sets branch target
187      *
188      * @param target branch target
189      */
190     public void setTarget(final InstructionHandle target) {
191         notifyTarget(this.target, target, this);
192         this.target = target;
193     }
194 
195     /**
196      * Long output format:
197      *
198      * &lt;position in byte code&gt; &lt;name of opcode&gt; "["&lt;opcode number&gt;"]" "("&lt;length of instruction&gt;")"
199      * "&lt;"&lt;target instruction&gt;"&gt;" "@"&lt;branch target offset&gt;
200      *
201      * @param verbose long/short format switch
202      * @return mnemonic for instruction
203      */
204     @Override
205     public String toString(final boolean verbose) {
206         final String s = super.toString(verbose);
207         String t = "null";
208         if (target != null) {
209             if (verbose) {
210                 if (target.getInstruction() == this) {
211                     t = "<points to itself>";
212                 } else if (target.getInstruction() == null) {
213                     t = "<null instruction!!!?>";
214                 } else {
215                     // I'm more interested in the address of the target then
216                     // the instruction located there.
217                     // t = target.getInstruction().toString(false); // Avoid circles
218                     t = "" + target.getPosition();
219                 }
220             } else {
221                 index = target.getPosition();
222                 // index = getTargetOffset(); crashes if positions haven't been set
223                 // t = "" + (index + position);
224                 t = "" + index;
225             }
226         }
227         return s + " -> " + t;
228     }
229 
230     /**
231      * Called by InstructionList.setPositions when setting the position for every instruction. In the presence of variable
232      * length instructions 'setPositions' performs multiple passes over the instruction list to calculate the correct (byte)
233      * positions and offsets by calling this function.
234      *
235      * @param offset additional offset caused by preceding (variable length) instructions
236      * @param maxOffset the maximum offset that may be caused by these instructions
237      * @return additional offset caused by possible change of this instruction's length
238      */
239     protected int updatePosition(final int offset, final int maxOffset) {
240         position += offset;
241         return 0;
242     }
243 
244     /**
245      * @param oldIh old target
246      * @param newIh new target
247      */
248     @Override
249     public void updateTarget(final InstructionHandle oldIh, final InstructionHandle newIh) {
250         if (target != oldIh) {
251             throw new ClassGenException("Not targeting " + oldIh + ", but " + target);
252         }
253         setTarget(newIh);
254     }
255 
256 }