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