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 1749603 2016-06-21 20:50:19Z 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 the Class.newInstance() statement in
077     * Instruction.readInstruction(). 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}