001 package serp.bytecode; 002 003 import java.io.*; 004 005 import serp.bytecode.lowlevel.*; 006 import serp.bytecode.visitor.*; 007 import serp.util.*; 008 009 /** 010 * An instruction that that loads a constant onto the stack. 011 * The opcode represented by this instruction may change depending on the 012 * type and value of the constant set. For example, if the constant value 013 * is initially set to 5, the opcode will be <code>iconst5</code>; if later 014 * incremented to 6, the opcode will be changed to <code>bipush(6)</code>. 015 * 016 * @author Abe White 017 */ 018 public class ConstantInstruction extends TypedInstruction { 019 private int _arg = -1; 020 021 ConstantInstruction(Code owner) { 022 super(owner); 023 } 024 025 ConstantInstruction(Code owner, int opcode) { 026 super(owner, opcode); 027 } 028 029 int getLength() { 030 switch (getOpcode()) { 031 case Constants.BIPUSH: 032 case Constants.LDC: 033 return super.getLength() + 1; 034 case Constants.SIPUSH: 035 case Constants.LDCW: 036 case Constants.LDC2W: 037 return super.getLength() + 2; 038 default: 039 return super.getLength(); 040 } 041 } 042 043 public int getStackChange() { 044 String type = getTypeName(); 045 if (double.class.getName().equals(type) 046 || long.class.getName().equals(type)) 047 return 2; 048 return 1; 049 } 050 051 public int getLogicalStackChange() { 052 return 1; 053 } 054 055 public String getTypeName() { 056 int opcode = getOpcode(); 057 switch (opcode) { 058 case Constants.NOP: 059 return null; 060 case Constants.ACONSTNULL: 061 return Object.class.getName(); 062 case Constants.ICONSTM1: 063 case Constants.ICONST0: 064 case Constants.ICONST1: 065 case Constants.ICONST2: 066 case Constants.ICONST3: 067 case Constants.ICONST4: 068 case Constants.ICONST5: 069 case Constants.BIPUSH: 070 case Constants.SIPUSH: 071 return int.class.getName(); 072 case Constants.LCONST0: 073 case Constants.LCONST1: 074 return long.class.getName(); 075 case Constants.FCONST0: 076 case Constants.FCONST1: 077 case Constants.FCONST2: 078 return float.class.getName(); 079 case Constants.DCONST0: 080 case Constants.DCONST1: 081 return double.class.getName(); 082 } 083 084 Entry entry = getPool().getEntry(_arg); 085 switch (entry.getType()) { 086 case Entry.UTF8: 087 case Entry.STRING: 088 return String.class.getName(); 089 case Entry.INT: 090 return int.class.getName(); 091 case Entry.FLOAT: 092 return float.class.getName(); 093 case Entry.LONG: 094 return long.class.getName(); 095 case Entry.DOUBLE: 096 return double.class.getName(); 097 case Entry.CLASS: 098 return Class.class.getName(); 099 default: 100 return null; 101 } 102 } 103 104 public TypedInstruction setType(String type) { 105 throw new UnsupportedOperationException("Use setValue"); 106 } 107 108 /** 109 * Return the value of the constant as its wrapper type, or null if 110 * not set. Returns class values as the class name. 111 */ 112 public Object getValue() { 113 int opcode = getOpcode(); 114 switch (opcode) { 115 case Constants.NOP: 116 case Constants.ACONSTNULL: 117 return null; 118 case Constants.ICONSTM1: 119 case Constants.ICONST0: 120 case Constants.ICONST1: 121 case Constants.ICONST2: 122 case Constants.ICONST3: 123 case Constants.ICONST4: 124 case Constants.ICONST5: 125 return Numbers.valueOf(opcode - Constants.ICONST0); 126 case Constants.LCONST0: 127 case Constants.LCONST1: 128 return Numbers.valueOf((long) (opcode - Constants.LCONST0)); 129 case Constants.FCONST0: 130 case Constants.FCONST1: 131 case Constants.FCONST2: 132 return new Float(opcode - Constants.FCONST0); 133 case Constants.DCONST0: 134 case Constants.DCONST1: 135 return new Double(opcode - Constants.DCONST0); 136 case Constants.BIPUSH: 137 case Constants.SIPUSH: 138 return Numbers.valueOf(_arg); 139 default: 140 Entry entry = getPool().getEntry(_arg); 141 Object val = ((ConstantEntry) entry).getConstant(); 142 if (entry.getType() == Entry.CLASS) 143 return getProject().getNameCache().getExternalForm((String) val, 144 false); 145 return val; 146 } 147 } 148 149 /** 150 * Set the constant to the given value. The value should be 151 * an instance of String, Integer, Long, Double, Float, Class, BCClass, or 152 * null depending on the constant type. If the given value is not 153 * supported directly, it will be converted accordingly. 154 * 155 * @return this instruction, for method chaining 156 */ 157 public ConstantInstruction setValue(Object value) { 158 if (value instanceof Boolean) 159 value = Numbers.valueOf((((Boolean) value).booleanValue()) ? 1 : 0); 160 else if (value instanceof Character) 161 value = Numbers.valueOf((int) ((Character) value).charValue()); 162 else if (value instanceof Byte) 163 value = Numbers.valueOf(((Byte) value).intValue()); 164 else if (value instanceof Short) 165 value = Numbers.valueOf(((Short) value).intValue()); 166 else if ((value != null) && !(value instanceof Number) 167 && !(value instanceof String) && !(value instanceof Class) 168 && !(value instanceof BCClass)) 169 throw new IllegalArgumentException("value = " + value); 170 171 calculateOpcode(value, false); 172 return this; 173 } 174 175 /** 176 * Return the string value of this constant, or null if not set. 177 */ 178 public String getStringValue() { 179 return (String) getValue(); 180 } 181 182 /** 183 * Return the int value of this constant, or 0 if not set. 184 */ 185 public int getIntValue() { 186 Object value = getValue(); 187 return (value == null) ? 0 : ((Number) value).intValue(); 188 } 189 190 /** 191 * Return the long value of this constant, or 0 if not set. 192 */ 193 public long getLongValue() { 194 Object value = getValue(); 195 return (value == null) ? 0L : ((Number) value).longValue(); 196 } 197 198 /** 199 * Return the float value of this constant, or 0 if not set. 200 */ 201 public float getFloatValue() { 202 Object value = getValue(); 203 return (value == null) ? 0F : ((Number) value).floatValue(); 204 } 205 206 /** 207 * Return the double value of this constant, or 0 if not set. 208 */ 209 public double getDoubleValue() { 210 Object value = getValue(); 211 return (value == null) ? 0D : ((Number) value).doubleValue(); 212 } 213 214 /** 215 * Return the class value of this constant, or null if not set. 216 */ 217 public String getClassNameValue() { 218 return (String) getValue(); 219 } 220 221 /** 222 * Set this constant to null. 223 * 224 * @return this instruction, for method chaining 225 */ 226 public ConstantInstruction setNull() { 227 calculateOpcode(null, false); 228 return this; 229 } 230 231 /** 232 * Set the value of this constant. 233 * 234 * @return this instruction, for method chaining 235 */ 236 public ConstantInstruction setValue(String value) { 237 calculateOpcode(value, false); 238 return this; 239 } 240 241 /** 242 * Set the value of this constant. 243 * 244 * @return this instruction, for method chaining 245 */ 246 public ConstantInstruction setValue(Class value) { 247 calculateOpcode(value, false); 248 return this; 249 } 250 251 /** 252 * Set the value of this constant. 253 * 254 * @return this instruction, for method chaining 255 */ 256 public ConstantInstruction setValue(BCClass value) { 257 calculateOpcode(value, false); 258 return this; 259 } 260 261 /** 262 * Set the value of this constant. 263 * 264 * @return this instruction, for method chaining 265 */ 266 public ConstantInstruction setValue(int value) { 267 calculateOpcode(Numbers.valueOf(value), false); 268 return this; 269 } 270 271 /** 272 * Set the value of this constant. 273 * 274 * @return this instruction, for method chaining 275 */ 276 public ConstantInstruction setValue(long value) { 277 calculateOpcode(Numbers.valueOf(value), false); 278 return this; 279 } 280 281 /** 282 * Set the value of this constant. 283 * 284 * @return this instruction, for method chaining 285 */ 286 public ConstantInstruction setValue(float value) { 287 calculateOpcode(new Float(value), false); 288 return this; 289 } 290 291 /** 292 * Set the value of this constant. 293 * 294 * @return this instruction, for method chaining 295 */ 296 public ConstantInstruction setValue(double value) { 297 calculateOpcode(new Double(value), false); 298 return this; 299 } 300 301 /** 302 * Set the value of this constant; note that this type is converted to int. 303 * 304 * @return this instruction, for method chaining 305 */ 306 public ConstantInstruction setValue(boolean value) { 307 return setValue((value) ? 1 : 0); 308 } 309 310 /** 311 * Set the value of this constant; note that this type is converted to int. 312 * 313 * @return this instruction, for method chaining 314 */ 315 public ConstantInstruction setValue(short value) { 316 return setValue((int) value); 317 } 318 319 /** 320 * Set the value of this constant; note that this type is converted to int. 321 * 322 * @return this instruction, for method chaining 323 */ 324 public ConstantInstruction setValue(char value) { 325 return setValue((int) value); 326 } 327 328 /** 329 * ConstantInstructions are equal if the const they reference is the same, 330 * or if the const of either is unset. 331 */ 332 public boolean equalsInstruction(Instruction other) { 333 if (this == other) 334 return true; 335 if (!(other instanceof ConstantInstruction)) 336 return false; 337 338 Object value = getValue(); 339 Object otherValue = ((ConstantInstruction) other).getValue(); 340 return (value == null) || (otherValue == null) 341 || value.equals(otherValue); 342 } 343 344 public void acceptVisit(BCVisitor visit) { 345 visit.enterConstantInstruction(this); 346 visit.exitConstantInstruction(this); 347 } 348 349 void read(Instruction orig) { 350 super.read(orig); 351 ConstantInstruction ci = (ConstantInstruction) orig; 352 calculateOpcode(ci.getValue(), ci.getOpcode() == Constants.LDCW); 353 } 354 355 void read(DataInput in) throws IOException { 356 super.read(in); 357 switch (getOpcode()) { 358 case Constants.BIPUSH: 359 case Constants.LDC: 360 _arg = in.readUnsignedByte(); 361 break; 362 case Constants.SIPUSH: 363 case Constants.LDCW: 364 case Constants.LDC2W: 365 _arg = in.readUnsignedShort(); 366 } 367 } 368 369 void write(DataOutput out) throws IOException { 370 super.write(out); 371 switch (getOpcode()) { 372 case Constants.BIPUSH: 373 case Constants.LDC: 374 out.writeByte(_arg); 375 break; 376 case Constants.SIPUSH: 377 case Constants.LDCW: 378 case Constants.LDC2W: 379 out.writeShort(_arg); 380 break; 381 } 382 } 383 384 private void calculateOpcode(Object value, boolean wide) { 385 int len = getLength(); 386 _arg = -1; 387 if (value == null) 388 setOpcode(Constants.ACONSTNULL); 389 else if (value instanceof Float) { 390 float floatVal = ((Float) value).floatValue(); 391 if ((floatVal == 0) || (floatVal == 1) || (floatVal == 2)) 392 setOpcode(Constants.FCONST0 + (int) floatVal); 393 else { 394 _arg = getPool().findFloatEntry((float) floatVal, true); 395 setOpcode((_arg > 255 || wide) ? Constants.LDCW 396 : Constants.LDC); 397 } 398 } else if (value instanceof Long) { 399 long longVal = ((Long) value).longValue(); 400 if (longVal == 0 || longVal == 1) 401 setOpcode(Constants.LCONST0 + (int) longVal); 402 else { 403 _arg = getPool().findLongEntry(longVal, true); 404 setOpcode(Constants.LDC2W); 405 } 406 } else if (value instanceof Double) { 407 double doubleVal = ((Double) value).doubleValue(); 408 if (doubleVal == 0 || doubleVal == 1) 409 setOpcode(Constants.DCONST0 + (int) doubleVal); 410 else { 411 _arg = getPool().findDoubleEntry(doubleVal, true); 412 setOpcode(Constants.LDC2W); 413 } 414 } else if (value instanceof Integer) { 415 int intVal = ((Integer) value).intValue(); 416 if (intVal >= -1 && intVal <= 5) 417 setOpcode(Constants.ICONST0 + intVal); 418 else if ((intVal >= -(2 << 6)) && (intVal < (2 << 6))) { 419 setOpcode(Constants.BIPUSH); 420 _arg = intVal; 421 } else if (intVal >= -(2 << 14) && intVal < (2 << 14)) { 422 setOpcode(Constants.SIPUSH); 423 _arg = intVal; 424 } else { 425 _arg = getPool().findIntEntry(intVal, true); 426 setOpcode((_arg > 255 || wide) ? Constants.LDCW 427 : Constants.LDC); 428 } 429 } else if (value instanceof String) { 430 _arg = getPool().findStringEntry((String) value, true); 431 setOpcode((_arg > 255 || wide) ? Constants.LDCW : Constants.LDC); 432 } else if (value instanceof Class) { 433 String name = getProject().getNameCache().getInternalForm(((Class) 434 value).getName(), false); 435 _arg = getPool().findClassEntry(name, true); 436 setOpcode(Constants.LDCW); 437 } else if (value instanceof BCClass) { 438 BCClass bc = (BCClass) value; 439 ClassEntry entry = (ClassEntry)bc.getPool().getEntry(bc.getIndex()); 440 if (bc.getPool() == getPool()) 441 _arg = getPool().indexOf(entry); 442 else 443 _arg = getPool().findClassEntry((String) entry.getConstant(), 444 true); 445 setOpcode(Constants.LDCW); 446 } else 447 throw new IllegalArgumentException(String.valueOf(value)); 448 449 if (len != getLength()) 450 invalidateByteIndexes(); 451 } 452 }