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