001 package edu.rice.cs.cunit.classFile.code.instructions; 002 003 import edu.rice.cs.cunit.classFile.code.Opcode; 004 import edu.rice.cs.cunit.util.Types; 005 006 /** 007 * Branch Java instruction. 008 * 009 * @author Mathias Ricken 010 */ 011 public class BranchInstruction extends AInstruction { 012 /** 013 * Opcode. 014 */ 015 protected byte _opcode; 016 017 /** 018 * Branch target. 019 */ 020 protected int _target; 021 022 /** 023 * Constructor. 024 * 025 * @param opcode branch opcode 026 * @param target target line number 027 */ 028 public BranchInstruction(byte opcode, int target) { 029 switch(opcode) { 030 case Opcode.IFEQ: 031 case Opcode.IFNE: 032 case Opcode.IFLT: 033 case Opcode.IFGE: 034 case Opcode.IFGT: 035 case Opcode.IFLE: 036 case Opcode.IF_ICMPEQ: 037 case Opcode.IF_ICMPNE: 038 case Opcode.IF_ICMPLT: 039 case Opcode.IF_ICMPGE: 040 case Opcode.IF_ICMPGT: 041 case Opcode.IF_ICMPLE: 042 case Opcode.IF_ACMPEQ: 043 case Opcode.IF_ACMPNE: 044 case Opcode.GOTO: 045 case Opcode.JSR: 046 case Opcode.IFNULL: 047 case Opcode.IFNONNULL: 048 break; 049 default: 050 throw new IllegalArgumentException("Invalid branch opcode"); 051 } 052 _opcode = opcode; 053 _target = target; 054 } 055 056 /** 057 * Get the opcode of this instruction. 058 * 059 * @return opcode 060 */ 061 public byte getOpcode() { 062 return _opcode; 063 } 064 065 /** 066 * Get the length bytecode for this instruction, padded for the specified program counter value. 067 * 068 * @param pc PC for padding 069 * 070 * @return bytecode length 071 */ 072 public int getBytecodeLength(int pc) { 073 return 3; 074 } 075 076 /** 077 * Make a new branch instruction from the bytecode stating at pc, padded using paddingPC, and use the line number 078 * table for branches. 079 * 080 * @param bytecode bytecode 081 * @param pc starting index in bytecode 082 * @param paddingPC PC for padding 083 * @param lnt line number table for branches 084 */ 085 public BranchInstruction(byte[] bytecode, int pc, int paddingPC, LineNumberTable lnt) { 086 _opcode = bytecode[pc]; 087 _target = lnt.getLineNumber(pc + Types.shortFromBytes(bytecode, pc + 1)); 088 } 089 090 /** 091 * Get the bytecode for this instruction, padded for the specified program counter value, with branch targets 092 * according to the specified line number table * @param pc PC for padding 093 * 094 * @param lnt line number table for branches 095 * 096 * @return bytecode 097 */ 098 public byte[] getBytecode(int pc, LineNumberTable lnt) { 099 byte[] b = new byte[getBytecodeLength(pc)]; 100 101 b[0] = getOpcode(); 102 Types.bytesFromShort((short)(lnt.getPC(_target) - pc), b, 1); 103 104 return b; 105 } 106 107 /** 108 * Return true of this instruction and the other object are equal. 109 * 110 * @param o other object 111 * 112 * @return true if equal 113 */ 114 public boolean equals(Object o) { 115 if (this == o) { 116 return true; 117 } 118 if (!(o instanceof BranchInstruction)) { 119 return false; 120 } 121 122 final BranchInstruction branchInstruction = (BranchInstruction)o; 123 124 if (_opcode != branchInstruction._opcode) { 125 return false; 126 } 127 if (_target != branchInstruction._target) { 128 return false; 129 } 130 131 return true; 132 } 133 134 /** 135 * Return hash code. 136 * 137 * @return hash code 138 */ 139 public int hashCode() { 140 int result; 141 result = (int)_opcode; 142 result = 29 * result + _target; 143 return result; 144 } 145 146 /** 147 * Return an array of target indices. 148 * 149 * @return array of target indices. 150 */ 151 public int[] getBranchTargets() { 152 return new int[]{_target}; 153 } 154 155 /** 156 * Set the branch target indices. 157 * 158 * @param branchTargets array of target indices 159 */ 160 public void setBranchTargets(int[] branchTargets) { 161 if (branchTargets.length != 1) { 162 throw new IllegalArgumentException("Branch instruction can only have one target"); 163 } 164 _target = branchTargets[0]; 165 } 166 167 /** 168 * Return instruction in human-readable form. 169 * 170 * @return string representation 171 */ 172 public String toString() { 173 StringBuilder x = new StringBuilder(); 174 x.append(Opcode.getOpcodeName(_opcode)); 175 x.append(" "); 176 x.append(_target); 177 return x.toString(); 178 } 179 } 180