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 *
017 */
018package org.apache.bcel.generic;
019
020import java.io.DataOutputStream;
021import java.io.IOException;
022
023import org.apache.bcel.util.ByteSequence;
024
025/**
026 * Abstract super class for branching instructions like GOTO, IFEQ, etc.. Branch instructions may have a variable
027 * length, namely GOTO, JSR, LOOKUPSWITCH and TABLESWITCH.
028 *
029 * @see InstructionList
030 */
031public abstract class BranchInstruction extends Instruction implements InstructionTargeter {
032
033    /**
034     * Used by BranchInstruction, LocalVariableGen, CodeExceptionGen, LineNumberGen
035     */
036    static void notifyTarget(final InstructionHandle old_ih, final InstructionHandle new_ih, final InstructionTargeter t) {
037        if (old_ih != null) {
038            old_ih.removeTargeter(t);
039        }
040        if (new_ih != null) {
041            new_ih.addTargeter(t);
042        }
043    }
044
045    /**
046     * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
047     */
048    @Deprecated
049    protected int index; // Branch target relative to this instruction
050
051    /**
052     * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
053     */
054    @Deprecated
055    protected InstructionHandle target; // Target object in instruction list
056
057    /**
058     * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
059     */
060    @Deprecated
061    protected int position; // Byte code offset
062
063    /**
064     * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise.
065     */
066    BranchInstruction() {
067    }
068
069    /**
070     * Common super constructor
071     *
072     * @param opcode Instruction opcode
073     * @param target instruction to branch to
074     */
075    protected BranchInstruction(final short opcode, final InstructionHandle target) {
076        super(opcode, (short) 3);
077        setTarget(target);
078    }
079
080    /**
081     * @return true, if ih is target of this instruction
082     */
083    @Override
084    public boolean containsTarget(final InstructionHandle ih) {
085        return target == ih;
086    }
087
088    /**
089     * Inform target that it's not targeted anymore.
090     */
091    @Override
092    void dispose() {
093        setTarget(null);
094        index = -1;
095        position = -1;
096    }
097
098    /**
099     * Dump instruction as byte code to stream out.
100     *
101     * @param out Output stream
102     */
103    @Override
104    public void dump(final DataOutputStream out) throws IOException {
105        out.writeByte(super.getOpcode());
106        index = getTargetOffset();
107        if (!isValidShort(index)) {
108            throw new ClassGenException("Branch target offset too large for short: " + index);
109        }
110        out.writeShort(index); // May be negative, i.e., point backwards
111    }
112
113    /**
114     * @return target offset in byte code
115     */
116    public final int getIndex() {
117        return index;
118    }
119
120    /**
121     * @return the position
122     * @since 6.0
123     */
124    protected int getPosition() {
125        return position;
126    }
127
128    /**
129     * @return target of branch instruction
130     */
131    public InstructionHandle getTarget() {
132        return target;
133    }
134
135    /**
136     * @return the offset to this instruction's target
137     */
138    protected int getTargetOffset() {
139        return getTargetOffset(target);
140    }
141
142    /**
143     * @param target branch target
144     * @return the offset to `target' relative to this instruction
145     */
146    protected int getTargetOffset(final InstructionHandle target) {
147        if (target == null) {
148            throw new ClassGenException("Target of " + super.toString(true) + " is invalid null handle");
149        }
150        final int t = target.getPosition();
151        if (t < 0) {
152            throw new ClassGenException("Invalid branch target position offset for " + super.toString(true) + ":" + t + ":" + target);
153        }
154        return t - position;
155    }
156
157    /**
158     * Read needed data (e.g. index) from file. Conversion to a InstructionHandle is done in InstructionList(byte[]).
159     *
160     * @param bytes input stream
161     * @param wide wide prefix?
162     * @see InstructionList
163     */
164    @Override
165    protected void initFromFile(final ByteSequence bytes, final boolean wide) throws IOException {
166        super.setLength(3);
167        index = bytes.readShort();
168    }
169
170    /**
171     * @param index the index to set
172     * @since 6.0
173     */
174    protected void setIndex(final int index) {
175        this.index = index;
176    }
177
178    /**
179     * @param position the position to set
180     * @since 6.0
181     */
182    protected void setPosition(final int position) {
183        this.position = position;
184    }
185
186    /**
187     * Set branch target
188     *
189     * @param target branch target
190     */
191    public void setTarget(final InstructionHandle target) {
192        notifyTarget(this.target, target, this);
193        this.target = target;
194    }
195
196    /**
197     * Long output format:
198     *
199     * &lt;position in byte code&gt; &lt;name of opcode&gt; "["&lt;opcode number&gt;"]" "("&lt;length of instruction&gt;")"
200     * "&lt;"&lt;target instruction&gt;"&gt;" "@"&lt;branch target offset&gt;
201     *
202     * @param verbose long/short format switch
203     * @return mnemonic for instruction
204     */
205    @Override
206    public String toString(final boolean verbose) {
207        final String s = super.toString(verbose);
208        String t = "null";
209        if (target != null) {
210            if (verbose) {
211                if (target.getInstruction() == this) {
212                    t = "<points to itself>";
213                } else if (target.getInstruction() == null) {
214                    t = "<null instruction!!!?>";
215                } else {
216                    // I'm more interested in the address of the target then
217                    // the instruction located there.
218                    // t = target.getInstruction().toString(false); // Avoid circles
219                    t = "" + target.getPosition();
220                }
221            } else {
222                index = target.getPosition();
223                // index = getTargetOffset(); crashes if positions haven't been set
224                // t = "" + (index + position);
225                t = "" + index;
226            }
227        }
228        return s + " -> " + t;
229    }
230
231    /**
232     * Called by InstructionList.setPositions when setting the position for every instruction. In the presence of variable
233     * length instructions `setPositions' performs multiple passes over the instruction list to calculate the correct (byte)
234     * positions and offsets by calling this function.
235     *
236     * @param offset additional offset caused by preceding (variable length) instructions
237     * @param max_offset the maximum offset that may be caused by these instructions
238     * @return additional offset caused by possible change of this instruction's length
239     */
240    protected int updatePosition(final int offset, final int max_offset) {
241        position += offset;
242        return 0;
243    }
244
245    /**
246     * @param old_ih old target
247     * @param new_ih new target
248     */
249    @Override
250    public void updateTarget(final InstructionHandle old_ih, final InstructionHandle new_ih) {
251        if (target != old_ih) {
252            throw new ClassGenException("Not targeting " + old_ih + ", but " + target);
253        }
254        setTarget(new_ih);
255    }
256
257}