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