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 23 import org.apache.bcel.util.ByteSequence; 24 25 /** 26 * Select - Abstract super class for LOOKUPSWITCH and TABLESWITCH instructions. 27 * 28 * <p>We use our super's <code>target</code> property as the default target. 29 * 30 * @version $Id$ 31 * @see LOOKUPSWITCH 32 * @see TABLESWITCH 33 * @see InstructionList 34 */ 35 public abstract class Select extends BranchInstruction implements VariableLengthInstruction, 36 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 = 0; // number of pad bytes for alignment TODO could be package-protected? 73 74 75 /** 76 * Empty constructor needed for Instruction.readInstruction. 77 * Not to be used otherwise. 78 */ Select()79 Select() { 80 } 81 82 83 /** 84 * (Match, target) pairs for switch. 85 * `Match' and `targets' must have the same length of course. 86 * 87 * @param match array of matching values 88 * @param targets instruction targets 89 * @param defaultTarget default instruction target 90 */ Select(final short opcode, final int[] match, final InstructionHandle[] targets, final InstructionHandle defaultTarget)91 Select(final short opcode, final int[] match, final InstructionHandle[] targets, final InstructionHandle defaultTarget) { 92 // don't set default target before instuction is built 93 super(opcode, null); 94 this.match = match; 95 this.targets = targets; 96 // now it's safe to set default target 97 setTarget(defaultTarget); 98 for (final InstructionHandle target2 : targets) { 99 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 updatePosition( final int offset, final int max_offset )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 dump( final DataOutputStream out )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 initFromFile( final ByteSequence bytes, final boolean wide )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 toString( final boolean verbose )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 */ setTarget( final int i, final InstructionHandle target )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 updateTarget( final InstructionHandle old_ih, final InstructionHandle new_ih )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 containsTarget( final InstructionHandle ih )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 clone()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 dispose()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 */ getMatchs()259 public int[] getMatchs() { 260 return match; 261 } 262 263 264 /** 265 * @return array of match target offsets 266 */ getIndices()267 public int[] getIndices() { 268 return indices; 269 } 270 271 272 /** 273 * @return array of match targets 274 */ getTargets()275 public InstructionHandle[] getTargets() { 276 return targets; 277 } 278 279 /** 280 * @return match entry 281 * @since 6.0 282 */ getMatch(final int index)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 */ getIndices(final int index)292 final int getIndices(final int index) { 293 return indices[index]; 294 } 295 296 /** 297 * @return target entry 298 * @since 6.0 299 */ getTarget(final int index)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 */ getFixed_length()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 */ setFixed_length(final int fixed_length)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 */ getMatch_length()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 */ setMatch_length(final int match_length)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 */ setMatch(final int index, final int value)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 */ setIndices(final int[] array)356 final void setIndices(final int[] array) { 357 indices = array; 358 } 359 360 /** 361 * 362 * @param array 363 * @since 6.0 364 */ setMatches(final int[] array)365 final void setMatches(final int[] array) { 366 match = array; 367 } 368 369 /** 370 * 371 * @param array 372 * @since 6.0 373 */ setTargets(final InstructionHandle[] array)374 final void setTargets(final InstructionHandle[] array) { 375 targets = array; 376 } 377 378 /** 379 * 380 * @return the padding 381 * @since 6.0 382 */ getPadding()383 final int getPadding() { 384 return padding; 385 } 386 387 388 /** @since 6.0 */ setIndices(final int i, final int value)389 final int setIndices(final int i, final int value) { 390 indices[i] = value; 391 return value; // Allow use in nested calls 392 } 393 } 394