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 super class 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 array have not 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 * @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}