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 }