1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 */
18 package org.apache.bcel.generic;
19
20 import java.io.DataOutputStream;
21 import java.io.IOException;
22 import org.apache.bcel.util.ByteSequence;
23
24 /**
25 * Select - Abstract super class for LOOKUPSWITCH and TABLESWITCH instructions.
26 *
27 * <p>We use our super's <code>target</code> property as the default target.
28 *
29 * @version $Id: Select.java 1152077 2011-07-29 02:29:42Z dbrosius $
30 * @author <A HREF="mailto:m.dahm@gmx.de">M. Dahm</A>
31 * @see LOOKUPSWITCH
32 * @see TABLESWITCH
33 * @see InstructionList
34 */
35 public abstract class Select extends BranchInstruction implements VariableLengthInstruction,
36 StackConsumer {
37
38 private static final long serialVersionUID = 2806771744559217250L;
39 protected int[] match; // matches, i.e., case 1: ...
40 protected int[] indices; // target offsets
41 protected InstructionHandle[] targets; // target objects in instruction list
42 protected int fixed_length; // fixed length defined by subclasses
43 protected int match_length; // number of cases
44 protected int padding = 0; // number of pad bytes for alignment
45
46
47 /**
48 * Empty constructor needed for the Class.newInstance() statement in
49 * Instruction.readInstruction(). Not to be used otherwise.
50 */
51 Select() {
52 }
53
54
55 /**
56 * (Match, target) pairs for switch.
57 * `Match' and `targets' must have the same length of course.
58 *
59 * @param match array of matching values
60 * @param targets instruction targets
61 * @param defaultTarget default instruction target
62 */
63 Select(short opcode, int[] match, InstructionHandle[] targets, InstructionHandle defaultTarget) {
64 super(opcode, defaultTarget);
65 this.targets = targets;
66 for (int i = 0; i < targets.length; i++) {
67 notifyTarget(null, targets[i], this);
68 }
69 this.match = match;
70 if ((match_length = match.length) != targets.length) {
71 throw new ClassGenException("Match and target array have not the same length: Match length: " + match.length + " Target length: " + targets.length);
72 }
73 indices = new int[match_length];
74 }
75
76
77 /**
78 * Since this is a variable length instruction, it may shift the following
79 * instructions which then need to update their position.
80 *
81 * Called by InstructionList.setPositions when setting the position for every
82 * instruction. In the presence of variable length instructions `setPositions'
83 * performs multiple passes over the instruction list to calculate the
84 * correct (byte) positions and offsets by calling this function.
85 *
86 * @param offset additional offset caused by preceding (variable length) instructions
87 * @param max_offset the maximum offset that may be caused by these instructions
88 * @return additional offset caused by possible change of this instruction's length
89 */
90 @Override
91 protected int updatePosition( int offset, int max_offset ) {
92 position += offset; // Additional offset caused by preceding SWITCHs, GOTOs, etc.
93 short old_length = length;
94 /* Alignment on 4-byte-boundary, + 1, because of tag byte.
95 */
96 padding = (4 - ((position + 1) % 4)) % 4;
97 length = (short) (fixed_length + padding); // Update length
98 return length - old_length;
99 }
100
101
102 /**
103 * Dump instruction as byte code to stream out.
104 * @param out Output stream
105 */
106 @Override
107 public void dump( DataOutputStream out ) throws IOException {
108 out.writeByte(opcode);
109 for (int i = 0; i < padding; i++) {
110 out.writeByte(0);
111 }
112 index = getTargetOffset(); // Write default target offset
113 out.writeInt(index);
114 }
115
116
117 /**
118 * Read needed data (e.g. index) from file.
119 */
120 @Override
121 protected void initFromFile( ByteSequence bytes, boolean wide ) throws IOException {
122 padding = (4 - (bytes.getIndex() % 4)) % 4; // Compute number of pad bytes
123 for (int i = 0; i < padding; i++) {
124 bytes.readByte();
125 }
126 // Default branch target common for both cases (TABLESWITCH, LOOKUPSWITCH)
127 index = bytes.readInt();
128 }
129
130
131 /**
132 * @return mnemonic for instruction
133 */
134 @Override
135 public String toString( boolean verbose ) {
136 StringBuilder buf = new StringBuilder(super.toString(verbose));
137 if (verbose) {
138 for (int i = 0; i < match_length; i++) {
139 String s = "null";
140 if (targets[i] != null) {
141 s = targets[i].getInstruction().toString();
142 }
143 buf.append("(").append(match[i]).append(", ").append(s).append(" = {").append(
144 indices[i]).append("})");
145 }
146 } else {
147 buf.append(" ...");
148 }
149 return buf.toString();
150 }
151
152
153 /**
154 * Set branch target for `i'th case
155 */
156 public void setTarget( int i, InstructionHandle target ) {
157 notifyTarget(targets[i], target, this);
158 targets[i] = target;
159 }
160
161
162 /**
163 * @param old_ih old target
164 * @param new_ih new target
165 */
166 @Override
167 public void updateTarget( InstructionHandle old_ih, InstructionHandle new_ih ) {
168 boolean targeted = false;
169 if (target == old_ih) {
170 targeted = true;
171 setTarget(new_ih);
172 }
173 for (int i = 0; i < targets.length; i++) {
174 if (targets[i] == old_ih) {
175 targeted = true;
176 setTarget(i, new_ih);
177 }
178 }
179 if (!targeted) {
180 throw new ClassGenException("Not targeting " + old_ih);
181 }
182 }
183
184
185 /**
186 * @return true, if ih is target of this instruction
187 */
188 @Override
189 public boolean containsTarget( InstructionHandle ih ) {
190 if (target == ih) {
191 return true;
192 }
193 for (int i = 0; i < targets.length; i++) {
194 if (targets[i] == ih) {
195 return true;
196 }
197 }
198 return false;
199 }
200
201
202 @Override
203 protected Object clone() throws CloneNotSupportedException {
204 Select copy = (Select) super.clone();
205 copy.match = match.clone();
206 copy.indices = indices.clone();
207 copy.targets = targets.clone();
208 return copy;
209 }
210
211
212 /**
213 * Inform targets that they're not targeted anymore.
214 */
215 @Override
216 void dispose() {
217 super.dispose();
218 for (int i = 0; i < targets.length; i++) {
219 targets[i].removeTargeter(this);
220 }
221 }
222
223
224 /**
225 * @return array of match indices
226 */
227 public int[] getMatchs() {
228 return match;
229 }
230
231
232 /**
233 * @return array of match target offsets
234 */
235 public int[] getIndices() {
236 return indices;
237 }
238
239
240 /**
241 * @return array of match targets
242 */
243 public InstructionHandle[] getTargets() {
244 return targets;
245 }
246 }