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