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