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