View Javadoc
1   /*
2    * reserved comment block
3    * DO NOT REMOVE OR ALTER!
4    */
5   package com.sun.org.apache.bcel.internal.generic;
6   
7   /* ====================================================================
8    * The Apache Software License, Version 1.1
9    *
10   * Copyright (c) 2001 The Apache Software Foundation.  All rights
11   * reserved.
12   *
13   * Redistribution and use in source and binary forms, with or without
14   * modification, are permitted provided that the following conditions
15   * are met:
16   *
17   * 1. Redistributions of source code must retain the above copyright
18   *    notice, this list of conditions and the following disclaimer.
19   *
20   * 2. Redistributions in binary form must reproduce the above copyright
21   *    notice, this list of conditions and the following disclaimer in
22   *    the documentation and/or other materials provided with the
23   *    distribution.
24   *
25   * 3. The end-user documentation included with the redistribution,
26   *    if any, must include the following acknowledgment:
27   *       "This product includes software developed by the
28   *        Apache Software Foundation (http://www.apache.org/)."
29   *    Alternately, this acknowledgment may appear in the software itself,
30   *    if and wherever such third-party acknowledgments normally appear.
31   *
32   * 4. The names "Apache" and "Apache Software Foundation" and
33   *    "Apache BCEL" must not be used to endorse or promote products
34   *    derived from this software without prior written permission. For
35   *    written permission, please contact apache@apache.org.
36   *
37   * 5. Products derived from this software may not be called "Apache",
38   *    "Apache BCEL", nor may "Apache" appear in their name, without
39   *    prior written permission of the Apache Software Foundation.
40   *
41   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
42   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
43   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
44   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
45   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
46   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
47   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
48   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
49   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
50   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
51   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
52   * SUCH DAMAGE.
53   * ====================================================================
54   *
55   * This software consists of voluntary contributions made by many
56   * individuals on behalf of the Apache Software Foundation.  For more
57   * information on the Apache Software Foundation, please see
58   * <http://www.apache.org/>.
59   */
60  
61  import java.io.*;
62  import com.sun.org.apache.bcel.internal.util.ByteSequence;
63  
64  /**
65   * Abstract super class for branching instructions like GOTO, IFEQ, etc..
66   * Branch instructions may have a variable length, namely GOTO, JSR,
67   * LOOKUPSWITCH and TABLESWITCH.
68   *
69   * @see InstructionList
70   * @author  <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A>
71   */
72  public abstract class BranchInstruction extends Instruction implements InstructionTargeter {
73    protected int               index;    // Branch target relative to this instruction
74    protected InstructionHandle target;   // Target object in instruction list
75    protected int               position; // Byte code offset
76  
77    /**
78     * Empty constructor needed for the Class.newInstance() statement in
79     * Instruction.readInstruction(). Not to be used otherwise.
80     */
81    BranchInstruction() {}
82  
83    /** Common super constructor
84     * @param opcodee Instruction opcode
85     * @param target instruction to branch to
86     */
87    protected BranchInstruction(short opcode, InstructionHandle target) {
88      super(opcode, (short)3);
89      setTarget(target);
90    }
91  
92    /**
93     * Dump instruction as byte code to stream out.
94     * @param out Output stream
95     */
96    @Override
97    public void dump(DataOutputStream out) throws IOException {
98      out.writeByte(opcode);
99  
100     index = getTargetOffset();
101 
102     if(Math.abs(index) >= 32767) // too large for short
103       throw new ClassGenException("Branch target offset too large for short");
104 
105     out.writeShort(index); // May be negative, i.e., point backwards
106   }
107 
108   /**
109    * @param target branch target
110    * @return the offset to  `target' relative to this instruction
111    */
112   protected int getTargetOffset(InstructionHandle target) {
113     if(target == null)
114       throw new ClassGenException("Target of " + super.toString(true) +
115                                   " is invalid null handle");
116 
117     int t = target.getPosition();
118 
119     if(t < 0)
120       throw new ClassGenException("Invalid branch target position offset for " +
121                                   super.toString(true) + ":" + t + ":" + target);
122 
123     return t - position;
124   }
125 
126   /**
127    * @return the offset to this instruction's target
128    */
129   protected int getTargetOffset() { return getTargetOffset(target); }
130 
131   /**
132    * Called by InstructionList.setPositions when setting the position for every
133    * instruction. In the presence of variable length instructions `setPositions'
134    * performs multiple passes over the instruction list to calculate the
135    * correct (byte) positions and offsets by calling this function.
136    *
137    * @param offset additional offset caused by preceding (variable length) instructions
138    * @param max_offset the maximum offset that may be caused by these instructions
139    * @return additional offset caused by possible change of this instruction's length
140    */
141   protected int updatePosition(int offset, int max_offset) {
142     position += offset;
143     return 0;
144   }
145 
146   /**
147    * Long output format:
148    *
149    * &lt;position in byte code&gt;
150    * &lt;name of opcode&gt; "["&lt;opcode number&gt;"]"
151    * "("&lt;length of instruction&gt;")"
152    * "&lt;"&lt;target instruction&gt;"&gt;" "@"&lt;branch target offset&gt;
153    *
154    * @param verbose long/short format switch
155    * @return mnemonic for instruction
156    */
157   @Override
158   public String toString(boolean verbose) {
159     String s = super.toString(verbose);
160     String t = "null";
161 
162     if(verbose) {
163       if(target != null) {
164         if(target.getInstruction() == this)
165           t = "<points to itself>";
166         else if(target.getInstruction() == null)
167           t = "<null instruction!!!?>";
168         else
169           t = target.getInstruction().toString(false); // Avoid circles
170       }
171     } else {
172       if(target != null) {
173         index = getTargetOffset();
174         t = "" + (index + position);
175       }
176     }
177 
178     return s + " -> " + t;
179   }
180 
181   /**
182    * Read needed data (e.g. index) from file. Conversion to a InstructionHandle
183    * is done in InstructionList(byte[]).
184    *
185    * @param bytes input stream
186    * @param wide wide prefix?
187    * @see InstructionList
188    */
189   @Override
190   protected void initFromFile(ByteSequence bytes, boolean wide) throws IOException
191   {
192     length = 3;
193     index  = bytes.readShort();
194   }
195 
196   /**
197    * @return target offset in byte code
198    */
199   public final int getIndex() { return index; }
200 
201   /**
202    * @return target of branch instruction
203    */
204   public InstructionHandle getTarget() { return target; }
205 
206   /**
207    * Set branch target
208    * @param target branch target
209    */
210   public final void setTarget(InstructionHandle target) {
211     notifyTargetChanging(this.target, this);
212     this.target = target;
213     notifyTargetChanged(this.target, this);
214   }
215 
216   /**
217    * Used by BranchInstruction, LocalVariableGen, CodeExceptionGen.
218    * Must be called before the target is actually changed in the
219    * InstructionTargeter.
220    */
221   static void notifyTargetChanging(InstructionHandle old_ih,
222                                  InstructionTargeter t) {
223     if(old_ih != null) {
224       old_ih.removeTargeter(t);
225     }
226   }
227 
228   /**
229    * Used by BranchInstruction, LocalVariableGen, CodeExceptionGen.
230    * Must be called after the target is actually changed in the
231    * InstructionTargeter.
232    */
233   static void notifyTargetChanged(InstructionHandle new_ih,
234                                  InstructionTargeter t) {
235     if(new_ih != null) {
236       new_ih.addTargeter(t);
237     }
238   }
239 
240   /**
241    * @param old_ih old target
242    * @param new_ih new target
243    */
244   @Override
245   public void updateTarget(InstructionHandle old_ih, InstructionHandle new_ih) {
246     if(target == old_ih)
247       setTarget(new_ih);
248     else
249       throw new ClassGenException("Not targeting " + old_ih + ", but " + target);
250   }
251 
252   /**
253    * @return true, if ih is target of this instruction
254    */
255   @Override
256   public boolean containsTarget(InstructionHandle ih) {
257     return (target == ih);
258   }
259 
260   /**
261    * Inform target that it's not targeted anymore.
262    */
263   @Override
264   void dispose() {
265     setTarget(null);
266     index=-1;
267     position=-1;
268   }
269 }