    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 * Select - Abstract super class for LOOKUPSWITCH and TABLESWITCH instructions.
    027 *
    028 * <p>
    029 * We use our super's {@code target} property as the default target.
    030 *
    031 * @see LOOKUPSWITCH
    032 * @see TABLESWITCH
    033 * @see InstructionList
    034 */
    035public abstract class Select extends BranchInstruction implements VariableLengthInstruction, StackConsumer /* @since 6.0 */, StackProducer {
    036
    037    /**
    038     * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
    039     */
    040    @Deprecated
    041    protected int[] match; // matches, i.e., case 1: ... TODO could be package-protected?
    042
    043    /**
    044     * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
    045     */
    046    @Deprecated
    047    protected int[] indices; // target offsets TODO could be package-protected?
    048
    049    /**
    050     * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
    051     */
    052    @Deprecated
    053    protected InstructionHandle[] targets; // target objects in instruction list TODO could be package-protected?
    054
    055    /**
    056     * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
    057     */
    058    @Deprecated
    059    protected int fixed_length; // fixed length defined by subclasses TODO could be package-protected?
    060
    061    /**
    062     * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
    063     */
    064    @Deprecated
    065    protected int match_length; // number of cases TODO could be package-protected?
    066
    067    /**
    068     * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
    069     */
    070    @Deprecated
    071    protected int padding; // number of pad bytes for alignment TODO could be package-protected?
    072
    073    /**
    074     * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise.
    075     */
    076    Select() {
    077    }
    078
    079    /**
    080     * (Match, target) pairs for switch. `Match' and `targets' must have the same length of course.
    081     *
    082     * @param match array of matching values
    083     * @param targets instruction targets
    084     * @param defaultTarget default instruction target
    085     */
    086    Select(final short opcode, final int[] match, final InstructionHandle[] targets, final InstructionHandle defaultTarget) {
    087        // don't set default target before instuction is built
    088        super(opcode, null);
    089        this.match = match;
    090        this.targets = targets;
    091        // now it's safe to set default target
    092        setTarget(defaultTarget);
    093        for (final InstructionHandle target2 : targets) {
    094            notifyTarget(null, target2, this);
    095        }
    096        if ((match_length = match.length) != targets.length) {
    097            throw new ClassGenException("Match and target array have not the same length: Match length: " + match.length + " Target length: " + targets.length);
    098        }
    099        indices = new int[match_length];
    100    }
    101
    102    @Override
    103    protected Object clone() throws CloneNotSupportedException {
    104        final Select copy = (Select) super.clone();
    105        copy.match = match.clone();
    106        copy.indices = indices.clone();
    107        copy.targets = targets.clone();
    108        return copy;
    109    }
    110
    111    /**
    112     * @return true, if ih is target of this instruction
    113     */
    114    @Override
    115    public boolean containsTarget(final InstructionHandle ih) {
    116        if (super.getTarget() == ih) {
    117            return true;
    118        }
    119        for (final InstructionHandle target2 : targets) {
    120            if (target2 == ih) {
    121                return true;
    122            }
    123        }
    124        return false;
    125    }
    126
    127    /**
    128     * Inform targets that they're not targeted anymore.
    129     */
    130    @Override
    131    void dispose() {
    132        super.dispose();
    133        for (final InstructionHandle target2 : targets) {
    134            target2.removeTargeter(this);
    135        }
    136    }
    137
    138    /**
    139     * Dump instruction as byte code to stream out.
    140     *
    141     * @param out Output stream
    142     */
    143    @Override
    144    public void dump(final DataOutputStream out) throws IOException {
    145        out.writeByte(super.getOpcode());
    146        for (int i = 0; i < padding; i++) {
    147            out.writeByte(0);
    148        }
    149        super.setIndex(getTargetOffset()); // Write default target offset
    150        out.writeInt(super.getIndex());
    151    }
    152
    153    /**
    154     * @return the fixed_length
    155     * @since 6.0
    156     */
    157    final int getFixed_length() {
    158        return fixed_length;
    159    }
    160
    161    /**
    162     * @return array of match target offsets
    163     */
    164    public int[] getIndices() {
    165        return indices;
    166    }
    167
    168    /**
    169     * @return index entry from indices
    170     * @since 6.0
    171     */
    172    final int getIndices(final int index) {
    173        return indices[index];
    174    }
    175
    176    /**
    177     * @return match entry
    178     * @since 6.0
    179     */
    180    final int getMatch(final int index) {
    181        return match[index];
    182    }
    183
    184    /**
    185     * @return the match_length
    186     * @since 6.0
    187     */
    188    final int getMatch_length() {
    189        return match_length;
    190    }
    191
    192    /**
    193     * @return array of match indices
    194     */
    195    public int[] getMatchs() {
    196        return match;
    197    }
    198
    199    /**
    200     *
    201     * @return the padding
    202     * @since 6.0
    203     */
    204    final int getPadding() {
    205        return padding;
    206    }
    207
    208    /**
    209     * @return target entry
    210     * @since 6.0
    211     */
    212    final InstructionHandle getTarget(final int index) {
    213        return targets[index];
    214    }
    215
    216    /**
    217     * @return array of match targets
    218     */
    219    public InstructionHandle[] getTargets() {
    220        return targets;
    221    }
    222
    223    /**
    224     * Read needed data (e.g. index) from file.
    225     */
    226    @Override
    227    protected void initFromFile(final ByteSequence bytes, final boolean wide) throws IOException {
    228        padding = (4 - bytes.getIndex() % 4) % 4; // Compute number of pad bytes
    229        for (int i = 0; i < padding; i++) {
    230            bytes.readByte();
    231        }
    232        // Default branch target common for both cases (TABLESWITCH, LOOKUPSWITCH)
    233        super.setIndex(bytes.readInt());
    234    }
    235
    236    /**
    237     * @param fixed_length the fixed_length to set
    238     * @since 6.0
    239     */
    240    final void setFixed_length(final int fixed_length) {
    241        this.fixed_length = fixed_length;
    242    }
    243
    244    /** @since 6.0 */
    245    final int setIndices(final int i, final int value) {
    246        indices[i] = value;
    247        return value; // Allow use in nested calls
    248    }
    249
    250    /**
    251     *
    252     * @param array
    253     * @since 6.0
    254     */
    255    final void setIndices(final int[] array) {
    256        indices = array;
    257    }
    258
    259    /**
    260     *
    261     * @param index
    262     * @param value
    263     * @since 6.0
    264     */
    265    final void setMatch(final int index, final int value) {
    266        match[index] = value;
    267    }
    268
    269    /**
    270     * @param match_length the match_length to set
    271     * @since 6.0
    272     */
    273    final int setMatch_length(final int match_length) {
    274        this.match_length = match_length;
    275        return match_length;
    276    }
    277
    278    /**
    279     *
    280     * @param array
    281     * @since 6.0
    282     */
    283    final void setMatches(final int[] array) {
    284        match = array;
    285    }
    286
    287    /**
    288     * Set branch target for `i'th case
    289     */
    290    public void setTarget(final int i, final InstructionHandle target) { // TODO could be package-protected?
    291        notifyTarget(targets[i], target, this);
    292        targets[i] = target;
    293    }
    294
    295    /**
    296     *
    297     * @param array
    298     * @since 6.0
    299     */
    300    final void setTargets(final InstructionHandle[] array) {
    301        targets = array;
    302    }
    303
    304    /**
    305     * @return mnemonic for instruction
    306     */
    307    @Override
    308    public String toString(final boolean verbose) {
    309        final StringBuilder buf = new StringBuilder(super.toString(verbose));
    310        if (verbose) {
    311            for (int i = 0; i < match_length; i++) {
    312                String s = "null";
    313                if (targets[i] != null) {
    314                    s = targets[i].getInstruction().toString();
    315                }
    316                buf.append("(").append(match[i]).append(", ").append(s).append(" = {").append(indices[i]).append("})");
    317            }
    318        } else {
    319            buf.append(" ...");
    320        }
    321        return buf.toString();
    322    }
    323
    324    /**
    325     * Since this is a variable length instruction, it may shift the following instructions which then need to update their
    326     * position.
    327     *
    328     * Called by InstructionList.setPositions when setting the position for every instruction. In the presence of variable
    329     * length instructions `setPositions' performs multiple passes over the instruction list to calculate the correct (byte)
    330     * positions and offsets by calling this function.
    331     *
    332     * @param offset additional offset caused by preceding (variable length) instructions
    333     * @param max_offset the maximum offset that may be caused by these instructions
    334     * @return additional offset caused by possible change of this instruction's length
    335     */
    336    @Override
    337    protected int updatePosition(final int offset, final int max_offset) {
    338        setPosition(getPosition() + offset); // Additional offset caused by preceding SWITCHs, GOTOs, etc.
    339        final short old_length = (short) super.getLength();
    340        /*
    341         * Alignment on 4-byte-boundary, + 1, because of tag byte.
    342         */
    343        padding = (4 - (getPosition() + 1) % 4) % 4;
    344        super.setLength((short) (fixed_length + padding)); // Update length
    345        return super.getLength() - old_length;
    346    }
    347
    348    /**
    349     * @param old_ih old target
    350     * @param new_ih new target
    351     */
    352    @Override
    353    public void updateTarget(final InstructionHandle old_ih, final InstructionHandle new_ih) {
    354        boolean targeted = false;
    355        if (super.getTarget() == old_ih) {
    356            targeted = true;
    357            setTarget(new_ih);
    358        }
    359        for (int i = 0; i < targets.length; i++) {
    360            if (targets[i] == old_ih) {
    361                targeted = true;
    362                setTarget(i, new_ih);
    363            }
    364        }
    365        if (!targeted) {
    366            throw new ClassGenException("Not targeting " + old_ih);
    367        }
    368    }
    369}