001 package serp.bytecode; 002 003 import java.util.*; 004 005 import serp.bytecode.visitor.*; 006 import serp.util.*; 007 008 /** 009 * A conversion opcode such as <code>i2l, f2i</code>, etc. 010 * Changing the types of the instruction will automatically 011 * update the underlying opcode. Converting from one type to the same 012 * type will result in a <code>nop</code>. 013 * 014 * @author Abe White 015 */ 016 public class ConvertInstruction extends TypedInstruction { 017 private static final Class[][] _mappings = new Class[][] { 018 { boolean.class, int.class }, 019 { void.class, int.class }, 020 { Object.class, int.class }, 021 }; 022 private static final Class[][] _fromMappings = new Class[][] { 023 { boolean.class, int.class }, 024 { void.class, int.class }, 025 { Object.class, int.class }, 026 { byte.class, int.class }, 027 { char.class, int.class }, 028 { short.class, int.class }, 029 }; 030 String _toType = null; 031 String _fromType = null; 032 033 ConvertInstruction(Code owner) { 034 super(owner); 035 } 036 037 ConvertInstruction(Code owner, int opcode) { 038 super(owner, opcode); 039 } 040 041 public int getLogicalStackChange() { 042 return 0; 043 } 044 045 public int getStackChange() { 046 switch (getOpcode()) { 047 case Constants.I2L: 048 case Constants.I2D: 049 case Constants.F2L: 050 case Constants.F2D: 051 return 1; 052 case Constants.L2I: 053 case Constants.L2F: 054 case Constants.D2I: 055 case Constants.D2F: 056 return -1; 057 default: 058 return 0; 059 } 060 } 061 062 public String getTypeName() { 063 switch (getOpcode()) { 064 case Constants.L2I: 065 case Constants.F2I: 066 case Constants.D2I: 067 return int.class.getName(); 068 case Constants.I2L: 069 case Constants.F2L: 070 case Constants.D2L: 071 return long.class.getName(); 072 case Constants.I2F: 073 case Constants.L2F: 074 case Constants.D2F: 075 return float.class.getName(); 076 case Constants.I2D: 077 case Constants.L2D: 078 case Constants.F2D: 079 return double.class.getName(); 080 case Constants.I2B: 081 return byte.class.getName(); 082 case Constants.I2C: 083 return char.class.getName(); 084 case Constants.I2S: 085 return short.class.getName(); 086 default: 087 return _toType; 088 } 089 } 090 091 public TypedInstruction setType(String type) { 092 String toType = mapType(type, _mappings, true); 093 String fromType = getFromTypeName(); 094 095 // if no valid opcode, remember current types in case they reset one 096 // to create a valid opcode 097 if (toType == null || fromType == null || toType.equals(fromType)) { 098 _toType = toType; 099 _fromType = fromType; 100 return (TypedInstruction) setOpcode(Constants.NOP); 101 } 102 103 // ok, valid conversion possible, forget saved types 104 _toType = null; 105 _fromType = null; 106 107 char to = toType.charAt(0); 108 char from = fromType.charAt(0); 109 switch (to) { 110 case 'i': 111 switch (from) { 112 case 'l': 113 return (TypedInstruction) setOpcode(Constants.L2I); 114 case 'f': 115 return (TypedInstruction) setOpcode(Constants.F2I); 116 case 'd': 117 return (TypedInstruction) setOpcode(Constants.D2I); 118 } 119 case 'l': 120 switch (from) { 121 case 'i': 122 return (TypedInstruction) setOpcode(Constants.I2L); 123 case 'f': 124 return (TypedInstruction) setOpcode(Constants.F2L); 125 case 'd': 126 return (TypedInstruction) setOpcode(Constants.D2L); 127 } 128 case 'f': 129 switch (from) { 130 case 'i': 131 return (TypedInstruction) setOpcode(Constants.I2F); 132 case 'l': 133 return (TypedInstruction) setOpcode(Constants.L2F); 134 case 'd': 135 return (TypedInstruction) setOpcode(Constants.D2F); 136 } 137 case 'd': 138 switch (from) { 139 case 'i': 140 return (TypedInstruction) setOpcode(Constants.I2D); 141 case 'l': 142 return (TypedInstruction) setOpcode(Constants.L2D); 143 case 'f': 144 return (TypedInstruction) setOpcode(Constants.F2D); 145 } 146 case 'b': 147 if (from == 'i') 148 return (TypedInstruction) setOpcode(Constants.I2B); 149 case 'C': 150 if (from == 'i') 151 return (TypedInstruction) setOpcode(Constants.I2C); 152 case 'S': 153 if (from == 'i') 154 return (TypedInstruction) setOpcode(Constants.I2S); 155 default: 156 throw new IllegalStateException(); 157 } 158 } 159 160 /** 161 * Return the name of the type being converted from. 162 * If neither type has been set, this method will return null. 163 */ 164 public String getFromTypeName() { 165 switch (getOpcode()) { 166 case Constants.I2L: 167 case Constants.I2F: 168 case Constants.I2D: 169 case Constants.I2B: 170 case Constants.I2S: 171 case Constants.I2C: 172 return int.class.getName(); 173 case Constants.L2I: 174 case Constants.L2F: 175 case Constants.L2D: 176 return long.class.getName(); 177 case Constants.F2I: 178 case Constants.F2L: 179 case Constants.F2D: 180 return float.class.getName(); 181 case Constants.D2I: 182 case Constants.D2L: 183 case Constants.D2F: 184 return double.class.getName(); 185 default: 186 return _fromType; 187 } 188 } 189 190 /** 191 * Return the {@link Class} of the type being converted from. 192 * If neither type has been set, this method will return null. 193 */ 194 public Class getFromType() { 195 String type = getFromTypeName(); 196 if (type == null) 197 return null; 198 return Strings.toClass(type, getClassLoader()); 199 } 200 201 /** 202 * Return the bytecode of the type being converted from. 203 * If neither type has been set, this method will return null. 204 */ 205 public BCClass getFromTypeBC() { 206 String type = getFromTypeName(); 207 if (type == null) 208 return null; 209 return getProject().loadClass(type, getClassLoader()); 210 } 211 212 /** 213 * Set the type being converted from. Types that have no direct 214 * support will be converted accordingly. 215 * 216 * @return this instruction, for method chaining 217 */ 218 public ConvertInstruction setFromType(String type) { 219 String fromType = mapType(type, _fromMappings, true); 220 String toType = getTypeName(); 221 222 // if no valid opcode, remember current types in case they reset one 223 // to create a valid opcode 224 if ((toType == null) || (fromType == null) || toType.equals(fromType)) { 225 _toType = toType; 226 _fromType = fromType; 227 return (ConvertInstruction) setOpcode(Constants.NOP); 228 } 229 230 // ok, valid conversion possible, forget saved types 231 _toType = null; 232 _fromType = null; 233 234 char to = toType.charAt(0); 235 char from = fromType.charAt(0); 236 switch (from) { 237 case 'i': 238 switch (to) { 239 case 'l': 240 return (ConvertInstruction) setOpcode(Constants.I2L); 241 case 'f': 242 return (ConvertInstruction) setOpcode(Constants.I2F); 243 case 'd': 244 return (ConvertInstruction) setOpcode(Constants.I2D); 245 case 'b': 246 return (ConvertInstruction) setOpcode(Constants.I2B); 247 case 'c': 248 return (ConvertInstruction) setOpcode(Constants.I2C); 249 case 's': 250 return (ConvertInstruction) setOpcode(Constants.I2S); 251 } 252 case 'l': 253 switch (to) { 254 case 'i': 255 return (ConvertInstruction) setOpcode(Constants.L2I); 256 case 'f': 257 return (ConvertInstruction) setOpcode(Constants.L2F); 258 case 'd': 259 return (ConvertInstruction) setOpcode(Constants.L2D); 260 } 261 case 'f': 262 switch (to) { 263 case 'i': 264 return (ConvertInstruction) setOpcode(Constants.F2I); 265 case 'l': 266 return (ConvertInstruction) setOpcode(Constants.F2L); 267 case 'd': 268 return (ConvertInstruction) setOpcode(Constants.F2D); 269 } 270 case 'd': 271 switch (to) { 272 case 'i': 273 return (ConvertInstruction) setOpcode(Constants.D2I); 274 case 'l': 275 return (ConvertInstruction) setOpcode(Constants.D2L); 276 case 'f': 277 return (ConvertInstruction) setOpcode(Constants.D2F); 278 } 279 default: 280 throw new IllegalStateException(); 281 } 282 } 283 284 /** 285 * Set the type being converted from. Types that have no direct 286 * support will be converted accordingly. 287 * 288 * @return this instruction, for method chaining 289 */ 290 public ConvertInstruction setFromType(Class type) { 291 if (type == null) 292 return setFromType((String) null); 293 return setFromType(type.getName()); 294 } 295 296 /** 297 * Set the type being converted from. Types that have no direct 298 * support will be converted accordingly. 299 * 300 * @return this instruction, for method chaining 301 */ 302 public ConvertInstruction setFromType(BCClass type) { 303 if (type == null) 304 return setFromType((String) null); 305 return setFromType(type.getName()); 306 } 307 308 /** 309 * ConvertInstructions are equal if the types they convert between are 310 * either equal or unset. 311 */ 312 public boolean equalsInstruction(Instruction other) { 313 if (other == this) 314 return true; 315 if (!(other instanceof ConvertInstruction)) 316 return false; 317 318 ConvertInstruction ins = (ConvertInstruction) other; 319 if (getOpcode() != Constants.NOP && getOpcode() == ins.getOpcode()) 320 return true; 321 322 String type = getTypeName(); 323 String otherType = ins.getTypeName(); 324 if (!(type == null || otherType == null || type.equals(otherType))) 325 return false; 326 327 type = getFromTypeName(); 328 otherType = ins.getFromTypeName(); 329 return type == null || otherType == null || type.equals(otherType); 330 } 331 332 public void acceptVisit(BCVisitor visit) { 333 visit.enterConvertInstruction(this); 334 visit.exitConvertInstruction(this); 335 } 336 337 void read(Instruction orig) { 338 super.read(orig); 339 ConvertInstruction ins = (ConvertInstruction) orig; 340 _toType = ins._toType; 341 _fromType = ins._fromType; 342 } 343 }