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 import java.util.Arrays; 007 008 /** 009 * LOOKUPSWITCH Java instruction. 010 * 011 * @author Mathias Ricken 012 */ 013 public class LookupSwitchInstruction extends AInstruction { 014 /** 015 * Default target. 016 */ 017 protected int _defaultTarget; 018 019 /** 020 * Branch targets. 021 */ 022 protected int[] _targets; 023 024 /** 025 * Branch keys. 026 */ 027 protected int[] _keys; 028 029 /** 030 * Constructor. 031 * 032 * @param defaultTarget default target line number 033 * @param keys keys 034 * @param targets target line numbers 035 */ 036 public LookupSwitchInstruction(int defaultTarget, int[] keys, int[] targets) { 037 _defaultTarget = defaultTarget; 038 _targets = targets; 039 _keys = keys; 040 } 041 042 /** 043 * Get the opcode of this instruction. 044 * 045 * @return opcode 046 */ 047 public byte getOpcode() { 048 return Opcode.LOOKUPSWITCH; 049 } 050 051 /** 052 * Get the length bytecode for this instruction, padded for the specified program counter value. 053 * 054 * @param pc PC for padding 055 * 056 * @return bytecode length 057 */ 058 public int getBytecodeLength(int pc) { 059 int pad = 3 - (pc % 4); 060 return (pad + 9 + (_targets.length) * 8); 061 } 062 063 /** 064 * Make a new LOOKUPSWITCH instruction from the bytecode stating at pc, padded using paddingPC, and use the line 065 * number table for branches. 066 * 067 * @param bytecode bytecode 068 * @param pc starting index in bytecode 069 * @param paddingPC PC for padding 070 * @param lnt line number table for branches 071 */ 072 public LookupSwitchInstruction(byte[] bytecode, int pc, int paddingPC, LineNumberTable lnt) { 073 int[] branchTargets = Opcode.getBranchTargets(bytecode, pc, paddingPC); 074 075 _defaultTarget = lnt.getLineNumber(branchTargets[0]); 076 077 int pad = 3 - (paddingPC % 4); 078 _targets = new int[branchTargets.length - 1]; 079 _keys = new int[branchTargets.length - 1]; 080 for(int i = 1; i < branchTargets.length; ++i) { 081 _targets[i - 1] = lnt.getLineNumber(branchTargets[i]); 082 _keys[i - 1] = Types.intFromBytes(bytecode, pc + pad + 1 + i * 8); 083 } 084 } 085 086 /** 087 * Get the bytecode for this instruction, padded for the specified program counter value, with branch targets 088 * according to the specified line number table 089 * 090 * @param pc PC for padding 091 * @param lnt line number table for branches 092 * 093 * @return bytecode 094 */ 095 public byte[] getBytecode(int pc, LineNumberTable lnt) { 096 int pad = 3 - (pc % 4); 097 byte[] b = new byte[getBytecodeLength(pc)]; 098 b[0] = getOpcode(); 099 Types.bytesFromInt(lnt.getPC(_defaultTarget) - pc, b, pad + 1); 100 Types.bytesFromInt(_targets.length, b, pad + 5); 101 for(int i = 0; i < _targets.length; ++i) { 102 Types.bytesFromInt(_keys[i], b, pad + 9 + i * 8); 103 Types.bytesFromInt(lnt.getPC(_targets[i]) - pc, b, pad + 13 + i * 8); 104 } 105 return b; 106 } 107 108 /** 109 * Return true of this instruction and the other object are equal. 110 * 111 * @param o other object 112 * 113 * @return true if equal 114 */ 115 public boolean equals(Object o) { 116 if (this == o) { 117 return true; 118 } 119 if (!(o instanceof LookupSwitchInstruction)) { 120 return false; 121 } 122 123 final LookupSwitchInstruction lookupSwitchInstruction = (LookupSwitchInstruction)o; 124 125 if (_defaultTarget != lookupSwitchInstruction._defaultTarget) { 126 return false; 127 } 128 if (!Arrays.equals(_keys, lookupSwitchInstruction._keys)) { 129 return false; 130 } 131 if (!Arrays.equals(_targets, lookupSwitchInstruction._targets)) { 132 return false; 133 } 134 135 return true; 136 } 137 138 /** 139 * Return hash code. 140 * 141 * @return hash code 142 */ 143 public int hashCode() { 144 return _defaultTarget; 145 } 146 147 /** 148 * Return an array of target indices. 149 * 150 * @return array of target indices. 151 */ 152 public int[] getBranchTargets() { 153 int[] bt = new int[_targets.length + 1]; 154 bt[0] = _defaultTarget; 155 int i = 1; 156 for(int t : _targets) { 157 bt[i++] = t; 158 } 159 return bt; 160 } 161 162 /** 163 * Set the branch target indices. 164 * 165 * @param branchTargets array of target indices 166 */ 167 public void setBranchTargets(int[] branchTargets) { 168 if (branchTargets.length != _targets.length + 1) { 169 throw new IllegalArgumentException("LOOKUPSWITCH has incorrect number of targets"); 170 } 171 _defaultTarget = branchTargets[0]; 172 for(int i = 1; i < branchTargets.length; ++i) { 173 _targets[i - 1] = branchTargets[i]; 174 } 175 } 176 177 /** 178 * Return instruction in human-readable form. 179 * 180 * @return string representation 181 */ 182 public String toString() { 183 StringBuilder x = new StringBuilder(); 184 x.append(Opcode.getOpcodeName(Opcode.LOOKUPSWITCH)); 185 x.append(" default = "); 186 x.append(_defaultTarget); 187 x.append(" n = "); 188 x.append(_targets.length); 189 for(int i = 0; i < _targets.length; ++i) { 190 x.append(" ("); 191 x.append(_keys[i]); 192 x.append("->"); 193 x.append(_targets[i]); 194 x.append(")"); 195 } 196 return x.toString(); 197 } 198 } 199