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