001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * https://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.bcel.generic; 020 021import java.io.DataOutputStream; 022import java.io.IOException; 023 024import org.apache.bcel.util.ByteSequence; 025 026/** 027 * Select - Abstract superclass for LOOKUPSWITCH and TABLESWITCH instructions. 028 * 029 * <p> 030 * We use our super's {@code target} property as the default target. 031 * 032 * @see LOOKUPSWITCH 033 * @see TABLESWITCH 034 * @see InstructionList 035 */ 036public abstract class Select extends BranchInstruction implements VariableLengthInstruction, StackConsumer /* @since 6.0 */, StackProducer { 037 038 /** 039 * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter 040 */ 041 @Deprecated 042 protected int[] match; // matches, that is, case 1: ... TODO could be package-protected? 043 044 /** 045 * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter 046 */ 047 @Deprecated 048 protected int[] indices; // target offsets TODO could be package-protected? 049 050 /** 051 * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter 052 */ 053 @Deprecated 054 protected InstructionHandle[] targets; // target objects in instruction list TODO could be package-protected? 055 056 /** 057 * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter 058 */ 059 @Deprecated 060 protected int fixed_length; // fixed length defined by subclasses TODO could be package-protected? 061 062 /** 063 * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter 064 */ 065 @Deprecated 066 protected int match_length; // number of cases TODO could be package-protected? 067 068 /** 069 * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter 070 */ 071 @Deprecated 072 protected int padding; // number of pad bytes for alignment TODO could be package-protected? 073 074 /** 075 * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. 076 */ 077 Select() { 078 } 079 080 /** 081 * (Match, target) pairs for switch. 'Match' and 'targets' must have the same length of course. 082 * 083 * @param match array of matching values. 084 * @param targets instruction targets. 085 * @param defaultTarget default instruction target. 086 */ 087 Select(final short opcode, final int[] match, final InstructionHandle[] targets, final InstructionHandle defaultTarget) { 088 // don't set default target before instruction is built 089 super(opcode, null); 090 this.match = match; 091 this.targets = targets; 092 // now it's safe to set default target 093 setTarget(defaultTarget); 094 for (final InstructionHandle target2 : targets) { 095 notifyTarget(null, target2, this); 096 } 097 if ((match_length = match.length) != targets.length) { 098 throw new ClassGenException("Match and target arrays do not have the same length: Match length: " + match.length + " Target length: " + targets.length); 099 } 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 * Gets the instruction length without padding. 156 * 157 * @return The fixed_length. 158 * @since 6.0 159 */ 160 final int getFixedLength() { 161 return fixed_length; 162 } 163 164 /** 165 * Gets the match target offsets. 166 * 167 * @return array of match target offsets. 168 */ 169 public int[] getIndices() { 170 return indices; 171 } 172 173 /** 174 * Gets the target offset at the given index. 175 * 176 * @return index entry from indices. 177 * @since 6.0 178 */ 179 final int getIndices(final int index) { 180 return indices[index]; 181 } 182 183 /** 184 * Gets the match value at the given index. 185 * 186 * @return match entry. 187 * @since 6.0 188 */ 189 final int getMatch(final int index) { 190 return match[index]; 191 } 192 193 /** 194 * Gets the number of match values. 195 * 196 * @return The match_length. 197 * @since 6.0 198 */ 199 final int getMatchLength() { 200 return match_length; 201 } 202 203 /** 204 * Gets the match indices. 205 * 206 * @return array of match indices. 207 */ 208 public int[] getMatchs() { 209 return match; 210 } 211 212 /** 213 * Gets the number of padding bytes. 214 * 215 * @return The padding. 216 * @since 6.0 217 */ 218 final int getPadding() { 219 return padding; 220 } 221 222 /** 223 * Gets the target at the given index. 224 * 225 * @return target entry. 226 * @since 6.0 227 */ 228 final InstructionHandle getTarget(final int index) { 229 return targets[index]; 230 } 231 232 /** 233 * Gets the match targets. 234 * 235 * @return array of match targets. 236 */ 237 public InstructionHandle[] getTargets() { 238 return targets; 239 } 240 241 /** 242 * Reads needed data (for example index) from file. 243 */ 244 @Override 245 protected void initFromFile(final ByteSequence bytes, final boolean wide) throws IOException { 246 padding = (4 - bytes.getIndex() % 4) % 4; // Compute number of pad bytes 247 for (int i = 0; i < padding; i++) { 248 bytes.readByte(); 249 } 250 // Default branch target common for both cases (TABLESWITCH, LOOKUPSWITCH) 251 super.setIndex(bytes.readInt()); 252 } 253 254 /** 255 * Sets the instruction length without padding. 256 * 257 * @param fixedLength The fixed_length to set. 258 * @since 6.0 259 */ 260 final void setFixedLength(final int fixedLength) { 261 this.fixed_length = fixedLength; 262 } 263 264 /** 265 * Sets the target offset at the given index. 266 * 267 * @since 6.0 268 */ 269 final int setIndices(final int i, final int value) { 270 indices[i] = value; 271 return value; // Allow use in nested calls 272 } 273 274 /** 275 * Sets the target offsets. 276 * 277 * @param array The new target offsets. 278 * @since 6.0 279 */ 280 final void setIndices(final int[] array) { 281 indices = array; 282 } 283 284 /** 285 * Sets the match value at the given index. 286 * 287 * @param index The match index. 288 * @param value The match value. 289 * @since 6.0 290 */ 291 final void setMatch(final int index, final int value) { 292 match[index] = value; 293 } 294 295 /** 296 * Sets the match values. 297 * 298 * @param array The new match values. 299 * @since 6.0 300 */ 301 final void setMatches(final int[] array) { 302 match = array; 303 } 304 305 /** 306 * Sets the number of match values. 307 * 308 * @param matchLength The match_length to set. 309 * @since 6.0 310 */ 311 final int setMatchLength(final int matchLength) { 312 this.match_length = matchLength; 313 return matchLength; 314 } 315 316 /** 317 * Sets branch target for 'i'th case. 318 * 319 * @param i The case index. 320 * @param target The branch target. 321 */ 322 public void setTarget(final int i, final InstructionHandle target) { // TODO could be package-protected? 323 notifyTarget(targets[i], target, this); 324 targets[i] = target; 325 } 326 327 /** 328 * Sets the match targets. 329 * 330 * @param array The new match targets. 331 * @since 6.0 332 */ 333 final void setTargets(final InstructionHandle[] array) { 334 targets = array; 335 } 336 337 /** 338 * @return mnemonic for instruction. 339 */ 340 @Override 341 public String toString(final boolean verbose) { 342 final StringBuilder buf = new StringBuilder(super.toString(verbose)); 343 if (verbose) { 344 for (int i = 0; i < match_length; i++) { 345 String s = "null"; 346 if (targets[i] != null) { 347 if (targets[i].getInstruction() == this) { 348 s = "<points to itself>"; 349 } else { 350 s = targets[i].getInstruction().toString(); 351 } 352 } 353 buf.append("(").append(match[i]).append(", ").append(s).append(" = {").append(indices[i]).append("})"); 354 } 355 } else { 356 buf.append(" ..."); 357 } 358 return buf.toString(); 359 } 360 361 /** 362 * Since this is a variable length instruction, it may shift the following instructions which then need to update their 363 * position. 364 * 365 * Called by InstructionList.setPositions when setting the position for every instruction. In the presence of variable 366 * length instructions 'setPositions' performs multiple passes over the instruction list to calculate the correct (byte) 367 * positions and offsets by calling this function. 368 * 369 * @param offset additional offset caused by preceding (variable length) instructions. 370 * @param maxOffset The maximum offset that may be caused by these instructions. 371 * @return additional offset caused by possible change of this instruction's length. 372 */ 373 @Override 374 protected int updatePosition(final int offset, final int maxOffset) { 375 setPosition(getPosition() + offset); // Additional offset caused by preceding switch instructions, GOTO instructions, and so on. 376 final short oldLength = (short) super.getLength(); 377 /* 378 * Alignment on 4-byte-boundary, + 1, because of tag byte. 379 */ 380 padding = (4 - (getPosition() + 1) % 4) % 4; 381 super.setLength((short) (fixed_length + padding)); // Update length 382 return super.getLength() - oldLength; 383 } 384 385 /** 386 * @param oldIh old target. 387 * @param newIh new target. 388 */ 389 @Override 390 public void updateTarget(final InstructionHandle oldIh, final InstructionHandle newIh) { 391 boolean targeted = false; 392 if (super.getTarget() == oldIh) { 393 targeted = true; 394 setTarget(newIh); 395 } 396 for (int i = 0; i < targets.length; i++) { 397 if (targets[i] == oldIh) { 398 targeted = true; 399 setTarget(i, newIh); 400 } 401 } 402 if (!targeted) { 403 throw new ClassGenException("Not targeting " + oldIh); 404 } 405 } 406}