001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 *  Unless required by applicable law or agreed to in writing, software
012 *  distributed under the License is distributed on an "AS IS" BASIS,
013 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 *  See the License for the specific language governing permissions and
015 *  limitations under the License.
016 */
017package org.apache.bcel.generic;
018
019import java.io.DataOutputStream;
020import java.io.IOException;
021
022import org.apache.bcel.util.ByteSequence;
023
024/**
025 * Abstract super class for branching instructions like GOTO, IFEQ, etc.. Branch instructions may have a variable
026 * length, namely GOTO, JSR, LOOKUPSWITCH and TABLESWITCH.
027 *
028 * @see InstructionList
029 */
030public abstract class BranchInstruction extends Instruction implements InstructionTargeter {
031
032    /**
033     * Used by BranchInstruction, LocalVariableGen, CodeExceptionGen, LineNumberGen
034     */
035    static void notifyTarget(final InstructionHandle oldIh, final InstructionHandle newIh, final InstructionTargeter t) {
036        if (oldIh != null) {
037            oldIh.removeTargeter(t);
038        }
039        if (newIh != null) {
040            newIh.addTargeter(t);
041        }
042    }
043
044    /**
045     * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
046     */
047    @Deprecated
048    protected int index; // Branch target relative to this instruction
049
050    /**
051     * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
052     */
053    @Deprecated
054    protected InstructionHandle target; // Target object in instruction list
055
056    /**
057     * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
058     */
059    @Deprecated
060    protected int position; // Byte code offset
061
062    /**
063     * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise.
064     */
065    BranchInstruction() {
066    }
067
068    /**
069     * Common super constructor
070     *
071     * @param opcode Instruction opcode
072     * @param target instruction to branch to
073     */
074    protected BranchInstruction(final short opcode, final InstructionHandle target) {
075        super(opcode, (short) 3);
076        setTarget(target);
077    }
078
079    /**
080     * @return true, if ih is target of this instruction
081     */
082    @Override
083    public boolean containsTarget(final InstructionHandle ih) {
084        return target == ih;
085    }
086
087    /**
088     * Inform target that it's not targeted anymore.
089     */
090    @Override
091    void dispose() {
092        setTarget(null);
093        index = -1;
094        position = -1;
095    }
096
097    /**
098     * Dump instruction as byte code to stream out.
099     *
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}