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