View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   https://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.bcel.generic;
20  
21  import java.io.DataOutputStream;
22  import java.io.IOException;
23  
24  import org.apache.bcel.util.ByteSequence;
25  
26  /**
27   * Select - Abstract super class for LOOKUPSWITCH and TABLESWITCH instructions.
28   *
29   * <p>
30   * We use our super's {@code target} property as the default target.
31   *
32   * @see LOOKUPSWITCH
33   * @see TABLESWITCH
34   * @see InstructionList
35   */
36  public abstract class Select extends BranchInstruction implements VariableLengthInstruction, StackConsumer /* @since 6.0 */, StackProducer {
37  
38      /**
39       * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
40       */
41      @Deprecated
42      protected int[] match; // matches, that is, case 1: ... TODO could be package-protected?
43  
44      /**
45       * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
46       */
47      @Deprecated
48      protected int[] indices; // target offsets TODO could be package-protected?
49  
50      /**
51       * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
52       */
53      @Deprecated
54      protected InstructionHandle[] targets; // target objects in instruction list TODO could be package-protected?
55  
56      /**
57       * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
58       */
59      @Deprecated
60      protected int fixed_length; // fixed length defined by subclasses TODO could be package-protected?
61  
62      /**
63       * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
64       */
65      @Deprecated
66      protected int match_length; // number of cases TODO could be package-protected?
67  
68      /**
69       * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
70       */
71      @Deprecated
72      protected int padding; // number of pad bytes for alignment TODO could be package-protected?
73  
74      /**
75       * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise.
76       */
77      Select() {
78      }
79  
80      /**
81       * (Match, target) pairs for switch. 'Match' and 'targets' must have the same length of course.
82       *
83       * @param match array of matching values.
84       * @param targets instruction targets.
85       * @param defaultTarget default instruction target.
86       */
87      Select(final short opcode, final int[] match, final InstructionHandle[] targets, final InstructionHandle defaultTarget) {
88          // don't set default target before instruction is built
89          super(opcode, null);
90          this.match = match;
91          this.targets = targets;
92          // now it's safe to set default target
93          setTarget(defaultTarget);
94          for (final InstructionHandle target2 : targets) {
95              notifyTarget(null, target2, this);
96          }
97          if ((match_length = match.length) != targets.length) {
98              throw new ClassGenException("Match and target array have not the same length: Match length: " + match.length + " Target length: " + targets.length);
99          }
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      * Dumps 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      * Gets the match target offsets.
164      *
165      * @return array of match target offsets.
166      */
167     public int[] getIndices() {
168         return indices;
169     }
170 
171     /**
172      * @return index entry from indices.
173      * @since 6.0
174      */
175     final int getIndices(final int index) {
176         return indices[index];
177     }
178 
179     /**
180      * @return match entry.
181      * @since 6.0
182      */
183     final int getMatch(final int index) {
184         return match[index];
185     }
186 
187     /**
188      * @return The match_length.
189      * @since 6.0
190      */
191     final int getMatchLength() {
192         return match_length;
193     }
194 
195     /**
196      * Gets the match indices.
197      *
198      * @return array of match indices.
199      */
200     public int[] getMatchs() {
201         return match;
202     }
203 
204     /**
205      *
206      * @return The padding.
207      * @since 6.0
208      */
209     final int getPadding() {
210         return padding;
211     }
212 
213     /**
214      * @return target entry.
215      * @since 6.0
216      */
217     final InstructionHandle getTarget(final int index) {
218         return targets[index];
219     }
220 
221     /**
222      * Gets the match targets.
223      *
224      * @return array of match targets.
225      */
226     public InstructionHandle[] getTargets() {
227         return targets;
228     }
229 
230     /**
231      * Reads needed data (for example index) from file.
232      */
233     @Override
234     protected void initFromFile(final ByteSequence bytes, final boolean wide) throws IOException {
235         padding = (4 - bytes.getIndex() % 4) % 4; // Compute number of pad bytes
236         for (int i = 0; i < padding; i++) {
237             bytes.readByte();
238         }
239         // Default branch target common for both cases (TABLESWITCH, LOOKUPSWITCH)
240         super.setIndex(bytes.readInt());
241     }
242 
243     /**
244      * @param fixedLength The fixed_length to set.
245      * @since 6.0
246      */
247     final void setFixedLength(final int fixedLength) {
248         this.fixed_length = fixedLength;
249     }
250 
251     /** @since 6.0 */
252     final int setIndices(final int i, final int value) {
253         indices[i] = value;
254         return value; // Allow use in nested calls
255     }
256 
257     /**
258      *
259      * @param array.
260      * @since 6.0
261      */
262     final void setIndices(final int[] array) {
263         indices = array;
264     }
265 
266     /**
267      *
268      * @param index.
269      * @param value.
270      * @since 6.0
271      */
272     final void setMatch(final int index, final int value) {
273         match[index] = value;
274     }
275 
276     /**
277      *
278      * @param array.
279      * @since 6.0
280      */
281     final void setMatches(final int[] array) {
282         match = array;
283     }
284 
285     /**
286      * @param matchLength The match_length to set.
287      * @since 6.0
288      */
289     final int setMatchLength(final int matchLength) {
290         this.match_length = matchLength;
291         return matchLength;
292     }
293 
294     /**
295      * Sets branch target for 'i'th case.
296      *
297      * @param i The case index.
298      * @param target The branch target.
299      */
300     public void setTarget(final int i, final InstructionHandle target) { // TODO could be package-protected?
301         notifyTarget(targets[i], target, this);
302         targets[i] = target;
303     }
304 
305     /**
306      *
307      * @param array.
308      * @since 6.0
309      */
310     final void setTargets(final InstructionHandle[] array) {
311         targets = array;
312     }
313 
314     /**
315      * @return mnemonic for instruction.
316      */
317     @Override
318     public String toString(final boolean verbose) {
319         final StringBuilder buf = new StringBuilder(super.toString(verbose));
320         if (verbose) {
321             for (int i = 0; i < match_length; i++) {
322                 String s = "null";
323                 if (targets[i] != null) {
324                     if (targets[i].getInstruction() == this) {
325                         s = "<points to itself>";
326                     } else {
327                         s = targets[i].getInstruction().toString();
328                     }
329                 }
330                 buf.append("(").append(match[i]).append(", ").append(s).append(" = {").append(indices[i]).append("})");
331             }
332         } else {
333             buf.append(" ...");
334         }
335         return buf.toString();
336     }
337 
338     /**
339      * Since this is a variable length instruction, it may shift the following instructions which then need to update their
340      * position.
341      *
342      * Called by InstructionList.setPositions when setting the position for every instruction. In the presence of variable
343      * length instructions 'setPositions' performs multiple passes over the instruction list to calculate the correct (byte)
344      * positions and offsets by calling this function.
345      *
346      * @param offset additional offset caused by preceding (variable length) instructions.
347      * @param maxOffset The maximum offset that may be caused by these instructions.
348      * @return additional offset caused by possible change of this instruction's length.
349      */
350     @Override
351     protected int updatePosition(final int offset, final int maxOffset) {
352         setPosition(getPosition() + offset); // Additional offset caused by preceding SWITCHs, GOTOs, etc.
353         final short oldLength = (short) super.getLength();
354         /*
355          * Alignment on 4-byte-boundary, + 1, because of tag byte.
356          */
357         padding = (4 - (getPosition() + 1) % 4) % 4;
358         super.setLength((short) (fixed_length + padding)); // Update length
359         return super.getLength() - oldLength;
360     }
361 
362     /**
363      * @param oldIh old target.
364      * @param newIh new target.
365      */
366     @Override
367     public void updateTarget(final InstructionHandle oldIh, final InstructionHandle newIh) {
368         boolean targeted = false;
369         if (super.getTarget() == oldIh) {
370             targeted = true;
371             setTarget(newIh);
372         }
373         for (int i = 0; i < targets.length; i++) {
374             if (targets[i] == oldIh) {
375                 targeted = true;
376                 setTarget(i, newIh);
377             }
378         }
379         if (!targeted) {
380             throw new ClassGenException("Not targeting " + oldIh);
381         }
382     }
383 }