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, 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; // 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     * Dump 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}