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 * Abstract super class for branching instructions like GOTO, IFEQ, etc.. 027 * Branch instructions may have a variable length, namely GOTO, JSR, 028 * LOOKUPSWITCH and TABLESWITCH. 029 * 030 * @see InstructionList 031 * @version $Id: BranchInstruction.java 1812166 2017-10-13 23:48:11Z ggregory $ 032 */ 033public abstract class BranchInstruction extends Instruction implements InstructionTargeter { 034 035 /** 036 * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter 037 */ 038 @Deprecated 039 protected int index; // Branch target relative to this instruction 040 041 /** 042 * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter 043 */ 044 @Deprecated 045 protected InstructionHandle target; // Target object in instruction list 046 047 /** 048 * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter 049 */ 050 @Deprecated 051 protected int position; // Byte code offset 052 053 054 /** 055 * Empty constructor needed for Instruction.readInstruction. 056 * Not to be used otherwise. 057 */ 058 BranchInstruction() { 059 } 060 061 062 /** Common super constructor 063 * @param opcode Instruction opcode 064 * @param target instruction to branch to 065 */ 066 protected BranchInstruction(final short opcode, final InstructionHandle target) { 067 super(opcode, (short) 3); 068 setTarget(target); 069 } 070 071 072 /** 073 * Dump instruction as byte code to stream out. 074 * @param out Output stream 075 */ 076 @Override 077 public void dump( final DataOutputStream out ) throws IOException { 078 out.writeByte(super.getOpcode()); 079 index = getTargetOffset(); 080 if (!isValidShort(index)) { 081 throw new ClassGenException("Branch target offset too large for short: " + index); 082 } 083 out.writeShort(index); // May be negative, i.e., point backwards 084 } 085 086 087 /** 088 * @param _target branch target 089 * @return the offset to `target' relative to this instruction 090 */ 091 protected int getTargetOffset( final InstructionHandle _target ) { 092 if (_target == null) { 093 throw new ClassGenException("Target of " + super.toString(true) 094 + " is invalid null handle"); 095 } 096 final int t = _target.getPosition(); 097 if (t < 0) { 098 throw new ClassGenException("Invalid branch target position offset for " 099 + super.toString(true) + ":" + t + ":" + _target); 100 } 101 return t - position; 102 } 103 104 105 /** 106 * @return the offset to this instruction's target 107 */ 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 */ 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 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 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 */ 187 public final int getIndex() { 188 return index; 189 } 190 191 192 /** 193 * @return target of branch instruction 194 */ 195 public InstructionHandle getTarget() { 196 return target; 197 } 198 199 200 /** 201 * Set branch target 202 * @param target branch target 203 */ 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 */ 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 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 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 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 */ 262 protected int getPosition() { 263 return position; 264 } 265 266 267 /** 268 * @param position the position to set 269 * @since 6.0 270 */ 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 */ 280 protected void setIndex(final int index) { 281 this.index = index; 282 } 283 284}