1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 package org.apache.bcel.generic; 19 20 import java.io.DataOutputStream; 21 import java.io.IOException; 22 23 import org.apache.bcel.util.ByteSequence; 24 25 /** 26 * Abstract super class for branching instructions like GOTO, IFEQ, etc.. 27 * Branch instructions may have a variable length, namely GOTO, JSR, 28 * LOOKUPSWITCH and TABLESWITCH. 29 * 30 * @see InstructionList 31 * @version $Id$ 32 */ 33 public abstract class BranchInstruction extends Instruction implements InstructionTargeter { 34 35 /** 36 * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter 37 */ 38 @Deprecated 39 protected int index; // Branch target relative to this instruction 40 41 /** 42 * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter 43 */ 44 @Deprecated 45 protected InstructionHandle target; // Target object in instruction list 46 47 /** 48 * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter 49 */ 50 @Deprecated 51 protected int position; // Byte code offset 52 53 54 /** 55 * Empty constructor needed for Instruction.readInstruction. 56 * Not to be used otherwise. 57 */ BranchInstruction()58 BranchInstruction() { 59 } 60 61 62 /** Common super constructor 63 * @param opcode Instruction opcode 64 * @param target instruction to branch to 65 */ BranchInstruction(final short opcode, final InstructionHandle target)66 protected BranchInstruction(final short opcode, final InstructionHandle target) { 67 super(opcode, (short) 3); 68 setTarget(target); 69 } 70 71 72 /** 73 * Dump instruction as byte code to stream out. 74 * @param out Output stream 75 */ 76 @Override dump( final DataOutputStream out )77 public void dump( final DataOutputStream out ) throws IOException { 78 out.writeByte(super.getOpcode()); 79 index = getTargetOffset(); 80 if (!isValidShort(index)) { 81 throw new ClassGenException("Branch target offset too large for short: " + index); 82 } 83 out.writeShort(index); // May be negative, i.e., point backwards 84 } 85 86 87 /** 88 * @param _target branch target 89 * @return the offset to `target' relative to this instruction 90 */ getTargetOffset( final InstructionHandle _target )91 protected int getTargetOffset( final InstructionHandle _target ) { 92 if (_target == null) { 93 throw new ClassGenException("Target of " + super.toString(true) 94 + " is invalid null handle"); 95 } 96 final int t = _target.getPosition(); 97 if (t < 0) { 98 throw new ClassGenException("Invalid branch target position offset for " 99 + super.toString(true) + ":" + t + ":" + _target); 100 } 101 return t - position; 102 } 103 104 105 /** 106 * @return the offset to this instruction's target 107 */ getTargetOffset()108 protected int getTargetOffset() { 109 return getTargetOffset(target); 110 } 111 112 113 /** 114 * Called by InstructionList.setPositions when setting the position for every 115 * instruction. In the presence of variable length instructions `setPositions' 116 * performs multiple passes over the instruction list to calculate the 117 * correct (byte) positions and offsets by calling this function. 118 * 119 * @param offset additional offset caused by preceding (variable length) instructions 120 * @param max_offset the maximum offset that may be caused by these instructions 121 * @return additional offset caused by possible change of this instruction's length 122 */ updatePosition( final int offset, final int max_offset )123 protected int updatePosition( final int offset, final int max_offset ) { 124 position += offset; 125 return 0; 126 } 127 128 129 /** 130 * Long output format: 131 * 132 * <position in byte code> 133 * <name of opcode> "["<opcode number>"]" 134 * "("<length of instruction>")" 135 * "<"<target instruction>">" "@"<branch target offset> 136 * 137 * @param verbose long/short format switch 138 * @return mnemonic for instruction 139 */ 140 @Override toString( final boolean verbose )141 public String toString( final boolean verbose ) { 142 final String s = super.toString(verbose); 143 String t = "null"; 144 if (verbose) { 145 if (target != null) { 146 if (target.getInstruction() == this) { 147 t = "<points to itself>"; 148 } else if (target.getInstruction() == null) { 149 t = "<null instruction!!!?>"; 150 } else { 151 // I'm more interested in the address of the target then 152 // the instruction located there. 153 //t = target.getInstruction().toString(false); // Avoid circles 154 t = "" + target.getPosition(); 155 } 156 } 157 } else { 158 if (target != null) { 159 index = target.getPosition(); 160 // index = getTargetOffset(); crashes if positions haven't been set 161 // t = "" + (index + position); 162 t = "" + index; 163 } 164 } 165 return s + " -> " + t; 166 } 167 168 169 /** 170 * Read needed data (e.g. index) from file. Conversion to a InstructionHandle 171 * is done in InstructionList(byte[]). 172 * 173 * @param bytes input stream 174 * @param wide wide prefix? 175 * @see InstructionList 176 */ 177 @Override initFromFile( final ByteSequence bytes, final boolean wide )178 protected void initFromFile( final ByteSequence bytes, final boolean wide ) throws IOException { 179 super.setLength(3); 180 index = bytes.readShort(); 181 } 182 183 184 /** 185 * @return target offset in byte code 186 */ getIndex()187 public final int getIndex() { 188 return index; 189 } 190 191 192 /** 193 * @return target of branch instruction 194 */ getTarget()195 public InstructionHandle getTarget() { 196 return target; 197 } 198 199 200 /** 201 * Set branch target 202 * @param target branch target 203 */ setTarget( final InstructionHandle target )204 public void setTarget( final InstructionHandle target ) { 205 notifyTarget(this.target, target, this); 206 this.target = target; 207 } 208 209 210 /** 211 * Used by BranchInstruction, LocalVariableGen, CodeExceptionGen, LineNumberGen 212 */ notifyTarget( final InstructionHandle old_ih, final InstructionHandle new_ih, final InstructionTargeter t )213 static void notifyTarget( final InstructionHandle old_ih, final InstructionHandle new_ih, 214 final InstructionTargeter t ) { 215 if (old_ih != null) { 216 old_ih.removeTargeter(t); 217 } 218 if (new_ih != null) { 219 new_ih.addTargeter(t); 220 } 221 } 222 223 224 /** 225 * @param old_ih old target 226 * @param new_ih new target 227 */ 228 @Override updateTarget( final InstructionHandle old_ih, final InstructionHandle new_ih )229 public void updateTarget( final InstructionHandle old_ih, final InstructionHandle new_ih ) { 230 if (target == old_ih) { 231 setTarget(new_ih); 232 } else { 233 throw new ClassGenException("Not targeting " + old_ih + ", but " + target); 234 } 235 } 236 237 238 /** 239 * @return true, if ih is target of this instruction 240 */ 241 @Override containsTarget( final InstructionHandle ih )242 public boolean containsTarget( final InstructionHandle ih ) { 243 return target == ih; 244 } 245 246 247 /** 248 * Inform target that it's not targeted anymore. 249 */ 250 @Override dispose()251 void dispose() { 252 setTarget(null); 253 index = -1; 254 position = -1; 255 } 256 257 258 /** 259 * @return the position 260 * @since 6.0 261 */ getPosition()262 protected int getPosition() { 263 return position; 264 } 265 266 267 /** 268 * @param position the position to set 269 * @since 6.0 270 */ setPosition(final int position)271 protected void setPosition(final int position) { 272 this.position = position; 273 } 274 275 276 /** 277 * @param index the index to set 278 * @since 6.0 279 */ setIndex(final int index)280 protected void setIndex(final int index) { 281 this.index = index; 282 } 283 284 } 285