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