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, i.e., 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 * Dump 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 * @return array of match target offsets 164 */ 165 public int[] getIndices() { 166 return indices; 167 } 168 169 /** 170 * @return index entry from indices 171 * @since 6.0 172 */ 173 final int getIndices(final int index) { 174 return indices[index]; 175 } 176 177 /** 178 * @return match entry 179 * @since 6.0 180 */ 181 final int getMatch(final int index) { 182 return match[index]; 183 } 184 185 /** 186 * @return the match_length 187 * @since 6.0 188 */ 189 final int getMatchLength() { 190 return match_length; 191 } 192 193 /** 194 * @return array of match indices 195 */ 196 public int[] getMatchs() { 197 return match; 198 } 199 200 /** 201 * 202 * @return the padding 203 * @since 6.0 204 */ 205 final int getPadding() { 206 return padding; 207 } 208 209 /** 210 * @return target entry 211 * @since 6.0 212 */ 213 final InstructionHandle getTarget(final int index) { 214 return targets[index]; 215 } 216 217 /** 218 * @return array of match targets 219 */ 220 public InstructionHandle[] getTargets() { 221 return targets; 222 } 223 224 /** 225 * Reads needed data (for example index) from file. 226 */ 227 @Override 228 protected void initFromFile(final ByteSequence bytes, final boolean wide) throws IOException { 229 padding = (4 - bytes.getIndex() % 4) % 4; // Compute number of pad bytes 230 for (int i = 0; i < padding; i++) { 231 bytes.readByte(); 232 } 233 // Default branch target common for both cases (TABLESWITCH, LOOKUPSWITCH) 234 super.setIndex(bytes.readInt()); 235 } 236 237 /** 238 * @param fixedLength the fixed_length to set 239 * @since 6.0 240 */ 241 final void setFixedLength(final int fixedLength) { 242 this.fixed_length = fixedLength; 243 } 244 245 /** @since 6.0 */ 246 final int setIndices(final int i, final int value) { 247 indices[i] = value; 248 return value; // Allow use in nested calls 249 } 250 251 /** 252 * 253 * @param array 254 * @since 6.0 255 */ 256 final void setIndices(final int[] array) { 257 indices = array; 258 } 259 260 /** 261 * 262 * @param index 263 * @param value 264 * @since 6.0 265 */ 266 final void setMatch(final int index, final int value) { 267 match[index] = value; 268 } 269 270 /** 271 * 272 * @param array 273 * @since 6.0 274 */ 275 final void setMatches(final int[] array) { 276 match = array; 277 } 278 279 /** 280 * @param matchLength the match_length to set 281 * @since 6.0 282 */ 283 final int setMatchLength(final int matchLength) { 284 this.match_length = matchLength; 285 return matchLength; 286 } 287 288 /** 289 * Sets branch target for 'i'th case 290 */ 291 public void setTarget(final int i, final InstructionHandle target) { // TODO could be package-protected? 292 notifyTarget(targets[i], target, this); 293 targets[i] = target; 294 } 295 296 /** 297 * 298 * @param array 299 * @since 6.0 300 */ 301 final void setTargets(final InstructionHandle[] array) { 302 targets = array; 303 } 304 305 /** 306 * @return mnemonic for instruction 307 */ 308 @Override 309 public String toString(final boolean verbose) { 310 final StringBuilder buf = new StringBuilder(super.toString(verbose)); 311 if (verbose) { 312 for (int i = 0; i < match_length; i++) { 313 String s = "null"; 314 if (targets[i] != null) { 315 if (targets[i].getInstruction() == this) { 316 s = "<points to itself>"; 317 } else { 318 s = targets[i].getInstruction().toString(); 319 } 320 } 321 buf.append("(").append(match[i]).append(", ").append(s).append(" = {").append(indices[i]).append("})"); 322 } 323 } else { 324 buf.append(" ..."); 325 } 326 return buf.toString(); 327 } 328 329 /** 330 * Since this is a variable length instruction, it may shift the following instructions which then need to update their 331 * position. 332 * 333 * Called by InstructionList.setPositions when setting the position for every instruction. In the presence of variable 334 * length instructions 'setPositions' performs multiple passes over the instruction list to calculate the correct (byte) 335 * positions and offsets by calling this function. 336 * 337 * @param offset additional offset caused by preceding (variable length) instructions 338 * @param maxOffset the maximum offset that may be caused by these instructions 339 * @return additional offset caused by possible change of this instruction's length 340 */ 341 @Override 342 protected int updatePosition(final int offset, final int maxOffset) { 343 setPosition(getPosition() + offset); // Additional offset caused by preceding SWITCHs, GOTOs, etc. 344 final short oldLength = (short) super.getLength(); 345 /* 346 * Alignment on 4-byte-boundary, + 1, because of tag byte. 347 */ 348 padding = (4 - (getPosition() + 1) % 4) % 4; 349 super.setLength((short) (fixed_length + padding)); // Update length 350 return super.getLength() - oldLength; 351 } 352 353 /** 354 * @param oldIh old target 355 * @param newIh new target 356 */ 357 @Override 358 public void updateTarget(final InstructionHandle oldIh, final InstructionHandle newIh) { 359 boolean targeted = false; 360 if (super.getTarget() == oldIh) { 361 targeted = true; 362 setTarget(newIh); 363 } 364 for (int i = 0; i < targets.length; i++) { 365 if (targets[i] == oldIh) { 366 targeted = true; 367 setTarget(i, newIh); 368 } 369 } 370 if (!targeted) { 371 throw new ClassGenException("Not targeting " + oldIh); 372 } 373 } 374}