    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     * 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}