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