001 package sysModel.classFile.code.instructions; 002 003 import sysModel.classFile.code.Opcode; 004 import sysModel.classFile.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 short getBytecodeLength(short pc) { 059 int pad = 3 - (pc % 4); 060 return (short)(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, short pc, short paddingPC, LineNumberTable lnt) { 073 int[] branchTargets = Opcode.getBranchTargets(bytecode, pc, paddingPC); 074 075 _defaultTarget = lnt.getLineNumber((short)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((short)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(short 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((short)_defaultTarget) - pc, b, pad + 1); 100 Types.bytesFromInt((int)_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((short)_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 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 return Arrays.equals(_targets, lookupSwitchInstruction._targets); 132 133 } 134 135 /** 136 * Return hash code. 137 * 138 * @return hash code 139 */ 140 public int hashCode() { 141 return _defaultTarget; 142 } 143 144 /** 145 * Return an array of target indices. 146 * 147 * @return array of target indices. 148 */ 149 public short[] getBranchTargets() { 150 short[] bt = new short[_targets.length + 1]; 151 bt[0] = (short)_defaultTarget; 152 int i = 1; 153 for(int t : _targets) { 154 bt[i++] = (short)t; 155 } 156 return bt; 157 } 158 159 /** 160 * Set the branch target indices. 161 * 162 * @param branchTargets array of target indices 163 */ 164 public void setBranchTargets(short[] branchTargets) { 165 if (branchTargets.length != _targets.length + 1) { 166 throw new IllegalArgumentException("LOOKUPSWITCH has incorrect number of targets"); 167 } 168 _defaultTarget = branchTargets[0]; 169 for(int i = 1; i < branchTargets.length; ++i) { 170 _targets[i - 1] = branchTargets[i]; 171 } 172 } 173 174 /** 175 * Return instruction in human-readable form. 176 * 177 * @return string representation 178 */ 179 public String toString() { 180 StringBuffer x = new StringBuffer(); 181 x.append(Opcode.getOpcodeName(Opcode.LOOKUPSWITCH)); 182 x.append(" default = "); 183 x.append(_defaultTarget); 184 x.append(" n = "); 185 x.append(_targets.length); 186 for(int i = 0; i < _targets.length; ++i) { 187 x.append(" ("); 188 x.append(_keys[i]); 189 x.append("->"); 190 x.append(_targets[i]); 191 x.append(')'); 192 } 193 return x.toString(); 194 } 195 } 196