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