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}