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, i.e., 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      * 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 }