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} 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}