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