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    * &lt;position in byte code&gt;
 114    * &lt;name of opcode&gt; "["&lt;opcode number&gt;"]"
 115    * "("&lt;length of instruction&gt;")"
 116    * "&lt;"&lt;target instruction&gt;"&gt;" "@"&lt;branch target offset&gt;
 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 }