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      * Dump 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, i.e., point backwards
112     }
113 
114     /**
115      * @return target offset in byte code
116      */
117     public final int getIndex() {
118         return index;
119     }
120 
121     /**
122      * @return the position
123      * @since 6.0
124      */
125     protected int getPosition() {
126         return position;
127     }
128 
129     /**
130      * @return target of branch instruction
131      */
132     public InstructionHandle getTarget() {
133         return target;
134     }
135 
136     /**
137      * @return the offset to this instruction's target
138      */
139     protected int getTargetOffset() {
140         return getTargetOffset(target);
141     }
142 
143     /**
144      * @param target branch target
145      * @return the offset to 'target' relative to this instruction
146      */
147     protected int getTargetOffset(final InstructionHandle target) {
148         if (target == null) {
149             throw new ClassGenException("Target of " + super.toString(true) + " is invalid null handle");
150         }
151         final int t = target.getPosition();
152         if (t < 0) {
153             throw new ClassGenException("Invalid branch target position offset for " + super.toString(true) + ":" + t + ":" + target);
154         }
155         return t - position;
156     }
157 
158     /**
159      * Reads needed data (for example index) from file. Conversion to a InstructionHandle is done in InstructionList(byte[]).
160      *
161      * @param bytes input stream
162      * @param wide wide prefix?
163      * @see InstructionList
164      */
165     @Override
166     protected void initFromFile(final ByteSequence bytes, final boolean wide) throws IOException {
167         super.setLength(3);
168         index = bytes.readShort();
169     }
170 
171     /**
172      * @param index the index to set
173      * @since 6.0
174      */
175     protected void setIndex(final int index) {
176         this.index = index;
177     }
178 
179     /**
180      * @param position the position to set
181      * @since 6.0
182      */
183     protected void setPosition(final int position) {
184         this.position = position;
185     }
186 
187     /**
188      * Sets branch target
189      *
190      * @param target branch target
191      */
192     public void setTarget(final InstructionHandle target) {
193         notifyTarget(this.target, target, this);
194         this.target = target;
195     }
196 
197     /**
198      * Long output format:
199      *
200      * &lt;position in byte code&gt; &lt;name of opcode&gt; "["&lt;opcode number&gt;"]" "("&lt;length of instruction&gt;")"
201      * "&lt;"&lt;target instruction&gt;"&gt;" "@"&lt;branch target offset&gt;
202      *
203      * @param verbose long/short format switch
204      * @return mnemonic for instruction
205      */
206     @Override
207     public String toString(final boolean verbose) {
208         final String s = super.toString(verbose);
209         String t = "null";
210         if (target != null) {
211             if (verbose) {
212                 if (target.getInstruction() == this) {
213                     t = "<points to itself>";
214                 } else if (target.getInstruction() == null) {
215                     t = "<null instruction!!!?>";
216                 } else {
217                     // I'm more interested in the address of the target then
218                     // the instruction located there.
219                     // t = target.getInstruction().toString(false); // Avoid circles
220                     t = "" + target.getPosition();
221                 }
222             } else {
223                 index = target.getPosition();
224                 // index = getTargetOffset(); crashes if positions haven't been set
225                 // t = "" + (index + position);
226                 t = "" + index;
227             }
228         }
229         return s + " -> " + t;
230     }
231 
232     /**
233      * Called by InstructionList.setPositions when setting the position for every instruction. In the presence of variable
234      * length instructions 'setPositions' performs multiple passes over the instruction list to calculate the correct (byte)
235      * positions and offsets by calling this function.
236      *
237      * @param offset additional offset caused by preceding (variable length) instructions
238      * @param maxOffset the maximum offset that may be caused by these instructions
239      * @return additional offset caused by possible change of this instruction's length
240      */
241     protected int updatePosition(final int offset, final int maxOffset) {
242         position += offset;
243         return 0;
244     }
245 
246     /**
247      * @param oldIh old target
248      * @param newIh new target
249      */
250     @Override
251     public void updateTarget(final InstructionHandle oldIh, final InstructionHandle newIh) {
252         if (target != oldIh) {
253             throw new ClassGenException("Not targeting " + oldIh + ", but " + target);
254         }
255         setTarget(newIh);
256     }
257 
258 }