1 package serp.bytecode; 2 3 import java.io.*; 4 5 import serp.bytecode.lowlevel.*; 6 import serp.bytecode.visitor.*; 7 import serp.util.*; 8 9 /*** 10 * An instruction that that loads a constant onto the stack. 11 * The opcode represented by this instruction may change depending on the 12 * type and value of the constant set. For example, if the constant value 13 * is initially set to 5, the opcode will be <code>iconst5</code>; if later 14 * incremented to 6, the opcode will be changed to <code>bipush(6)</code>. 15 * 16 * @author Abe White 17 */ 18 public class ConstantInstruction extends TypedInstruction { 19 private int _arg = -1; 20 21 ConstantInstruction(Code owner) { 22 super(owner); 23 } 24 25 ConstantInstruction(Code owner, int opcode) { 26 super(owner, opcode); 27 } 28 29 int getLength() { 30 switch (getOpcode()) { 31 case Constants.BIPUSH: 32 case Constants.LDC: 33 return super.getLength() + 1; 34 case Constants.SIPUSH: 35 case Constants.LDCW: 36 case Constants.LDC2W: 37 return super.getLength() + 2; 38 default: 39 return super.getLength(); 40 } 41 } 42 43 public int getStackChange() { 44 String type = getTypeName(); 45 if (double.class.getName().equals(type) 46 || long.class.getName().equals(type)) 47 return 2; 48 return 1; 49 } 50 51 public int getLogicalStackChange() { 52 return 1; 53 } 54 55 public String getTypeName() { 56 int opcode = getOpcode(); 57 switch (opcode) { 58 case Constants.NOP: 59 return null; 60 case Constants.ACONSTNULL: 61 return Object.class.getName(); 62 case Constants.ICONSTM1: 63 case Constants.ICONST0: 64 case Constants.ICONST1: 65 case Constants.ICONST2: 66 case Constants.ICONST3: 67 case Constants.ICONST4: 68 case Constants.ICONST5: 69 case Constants.BIPUSH: 70 case Constants.SIPUSH: 71 return int.class.getName(); 72 case Constants.LCONST0: 73 case Constants.LCONST1: 74 return long.class.getName(); 75 case Constants.FCONST0: 76 case Constants.FCONST1: 77 case Constants.FCONST2: 78 return float.class.getName(); 79 case Constants.DCONST0: 80 case Constants.DCONST1: 81 return double.class.getName(); 82 } 83 84 Entry entry = getPool().getEntry(_arg); 85 switch (entry.getType()) { 86 case Entry.UTF8: 87 case Entry.STRING: 88 return String.class.getName(); 89 case Entry.INT: 90 return int.class.getName(); 91 case Entry.FLOAT: 92 return float.class.getName(); 93 case Entry.LONG: 94 return long.class.getName(); 95 case Entry.DOUBLE: 96 return double.class.getName(); 97 case Entry.CLASS: 98 return Class.class.getName(); 99 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 boolean clsName = false; 159 if (value instanceof Boolean) 160 value = Numbers.valueOf((((Boolean) value).booleanValue()) ? 1 : 0); 161 else if (value instanceof Character) 162 value = Numbers.valueOf((int) ((Character) value).charValue()); 163 else if (value instanceof Byte) 164 value = Numbers.valueOf(((Byte) value).intValue()); 165 else if (value instanceof Short) 166 value = Numbers.valueOf(((Short) value).intValue()); 167 else if (value instanceof Class) { 168 value = ((Class) value).getName(); 169 clsName = true; 170 } else if (value instanceof BCClass) { 171 value = ((BCClass) value).getName(); 172 clsName = true; 173 } else if (value != null && !(value instanceof Number) 174 && !(value instanceof String)) 175 throw new IllegalArgumentException("value = " + value); 176 177 calculateOpcode(value, clsName, false); 178 return this; 179 } 180 181 /*** 182 * Return the string value of this constant, or null if not set. 183 */ 184 public String getStringValue() { 185 return (String) getValue(); 186 } 187 188 /*** 189 * Return the int value of this constant, or 0 if not set. 190 */ 191 public int getIntValue() { 192 Object value = getValue(); 193 return (value == null) ? 0 : ((Number) value).intValue(); 194 } 195 196 /*** 197 * Return the long value of this constant, or 0 if not set. 198 */ 199 public long getLongValue() { 200 Object value = getValue(); 201 return (value == null) ? 0L : ((Number) value).longValue(); 202 } 203 204 /*** 205 * Return the float value of this constant, or 0 if not set. 206 */ 207 public float getFloatValue() { 208 Object value = getValue(); 209 return (value == null) ? 0F : ((Number) value).floatValue(); 210 } 211 212 /*** 213 * Return the double value of this constant, or 0 if not set. 214 */ 215 public double getDoubleValue() { 216 Object value = getValue(); 217 return (value == null) ? 0D : ((Number) value).doubleValue(); 218 } 219 220 /*** 221 * Return the class value of this constant, or null if not set. 222 */ 223 public String getClassNameValue() { 224 return (String) getValue(); 225 } 226 227 /*** 228 * Set this constant to null. 229 * 230 * @return this instruction, for method chaining 231 */ 232 public ConstantInstruction setNull() { 233 calculateOpcode(null, false, false); 234 return this; 235 } 236 237 /*** 238 * Set the value of this constant. 239 * 240 * @return this instruction, for method chaining 241 */ 242 public ConstantInstruction setValue(String value) { 243 return setValue(value, false); 244 } 245 246 public ConstantInstruction setValue(String value, boolean clsName) { 247 calculateOpcode(value, clsName, 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(Class value) { 257 if (value == null) 258 return setNull(); 259 calculateOpcode(value.getName(), true, false); 260 return this; 261 } 262 263 /*** 264 * Set the value of this constant. 265 * 266 * @return this instruction, for method chaining 267 */ 268 public ConstantInstruction setValue(BCClass value) { 269 if (value == null) 270 return setNull(); 271 calculateOpcode(value.getName(), true, false); 272 return this; 273 } 274 275 /*** 276 * Set the value of this constant. 277 * 278 * @return this instruction, for method chaining 279 */ 280 public ConstantInstruction setValue(int value) { 281 calculateOpcode(Numbers.valueOf(value), false, false); 282 return this; 283 } 284 285 /*** 286 * Set the value of this constant. 287 * 288 * @return this instruction, for method chaining 289 */ 290 public ConstantInstruction setValue(long value) { 291 calculateOpcode(Numbers.valueOf(value), false, false); 292 return this; 293 } 294 295 /*** 296 * Set the value of this constant. 297 * 298 * @return this instruction, for method chaining 299 */ 300 public ConstantInstruction setValue(float value) { 301 calculateOpcode(new Float(value), false, false); 302 return this; 303 } 304 305 /*** 306 * Set the value of this constant. 307 * 308 * @return this instruction, for method chaining 309 */ 310 public ConstantInstruction setValue(double value) { 311 calculateOpcode(new Double(value), false, false); 312 return this; 313 } 314 315 /*** 316 * Set the value of this constant; note that this type is converted to int. 317 * 318 * @return this instruction, for method chaining 319 */ 320 public ConstantInstruction setValue(boolean value) { 321 return setValue((value) ? 1 : 0); 322 } 323 324 /*** 325 * Set the value of this constant; note that this type is converted to int. 326 * 327 * @return this instruction, for method chaining 328 */ 329 public ConstantInstruction setValue(short value) { 330 return setValue((int) value); 331 } 332 333 /*** 334 * Set the value of this constant; note that this type is converted to int. 335 * 336 * @return this instruction, for method chaining 337 */ 338 public ConstantInstruction setValue(char value) { 339 return setValue((int) value); 340 } 341 342 /*** 343 * ConstantInstructions are equal if the const they reference is the same, 344 * or if the const of either is unset. 345 */ 346 public boolean equalsInstruction(Instruction other) { 347 if (this == other) 348 return true; 349 if (!(other instanceof ConstantInstruction)) 350 return false; 351 352 ConstantInstruction ci = (ConstantInstruction) other; 353 Object value = getValue(); 354 Object otherValue = ci.getValue(); 355 if (value == null || otherValue == null) 356 return true; 357 if (getTypeName() == null || ci.getTypeName() == null) 358 return true; 359 return value.equals(otherValue) 360 && getTypeName().equals(ci.getTypeName()); 361 } 362 363 public void acceptVisit(BCVisitor visit) { 364 visit.enterConstantInstruction(this); 365 visit.exitConstantInstruction(this); 366 } 367 368 void read(Instruction orig) { 369 super.read(orig); 370 ConstantInstruction ci = (ConstantInstruction) orig; 371 calculateOpcode(ci.getValue(), 372 Class.class.getName().equals(ci.getTypeName()), 373 ci.getOpcode() == Constants.LDCW); 374 } 375 376 void read(DataInput in) throws IOException { 377 super.read(in); 378 switch (getOpcode()) { 379 case Constants.BIPUSH: 380 case Constants.LDC: 381 _arg = in.readUnsignedByte(); 382 break; 383 case Constants.SIPUSH: 384 case Constants.LDCW: 385 case Constants.LDC2W: 386 _arg = in.readUnsignedShort(); 387 } 388 } 389 390 void write(DataOutput out) throws IOException { 391 super.write(out); 392 switch (getOpcode()) { 393 case Constants.BIPUSH: 394 case Constants.LDC: 395 out.writeByte(_arg); 396 break; 397 case Constants.SIPUSH: 398 case Constants.LDCW: 399 case Constants.LDC2W: 400 out.writeShort(_arg); 401 break; 402 } 403 } 404 405 private void calculateOpcode(Object value, boolean clsName, boolean wide) { 406 int len = getLength(); 407 _arg = -1; 408 if (value == null) 409 setOpcode(Constants.ACONSTNULL); 410 else if (clsName) { 411 String name = getProject().getNameCache().getInternalForm((String) 412 value, false); 413 _arg = getPool().findClassEntry(name, true); 414 setOpcode(Constants.LDCW); 415 ensureBytecodeVersion(); 416 } else if (value instanceof Float) { 417 float floatVal = ((Float) value).floatValue(); 418 if ((floatVal == 0) || (floatVal == 1) || (floatVal == 2)) 419 setOpcode(Constants.FCONST0 + (int) floatVal); 420 else { 421 _arg = getPool().findFloatEntry((float) floatVal, true); 422 setOpcode((_arg > 255 || wide) ? Constants.LDCW 423 : Constants.LDC); 424 } 425 } else if (value instanceof Long) { 426 long longVal = ((Long) value).longValue(); 427 if (longVal == 0 || longVal == 1) 428 setOpcode(Constants.LCONST0 + (int) longVal); 429 else { 430 _arg = getPool().findLongEntry(longVal, true); 431 setOpcode(Constants.LDC2W); 432 } 433 } else if (value instanceof Double) { 434 double doubleVal = ((Double) value).doubleValue(); 435 if (doubleVal == 0 || doubleVal == 1) 436 setOpcode(Constants.DCONST0 + (int) doubleVal); 437 else { 438 _arg = getPool().findDoubleEntry(doubleVal, true); 439 setOpcode(Constants.LDC2W); 440 } 441 } else if (value instanceof Integer) { 442 int intVal = ((Integer) value).intValue(); 443 if (intVal >= -1 && intVal <= 5) 444 setOpcode(Constants.ICONST0 + intVal); 445 else if ((intVal >= -(2 << 6)) && (intVal < (2 << 6))) { 446 setOpcode(Constants.BIPUSH); 447 _arg = intVal; 448 } else if (intVal >= -(2 << 14) && intVal < (2 << 14)) { 449 setOpcode(Constants.SIPUSH); 450 _arg = intVal; 451 } else { 452 _arg = getPool().findIntEntry(intVal, true); 453 setOpcode((_arg > 255 || wide) ? Constants.LDCW 454 : Constants.LDC); 455 } 456 } else if (value instanceof String) { 457 _arg = getPool().findStringEntry((String) value, true); 458 setOpcode((_arg > 255 || wide) ? Constants.LDCW : Constants.LDC); 459 } else 460 throw new IllegalArgumentException(String.valueOf(value)); 461 462 if (len != getLength()) 463 invalidateByteIndexes(); 464 } 465 466 /*** 467 * When adding class entries, make sure the bytecode spec supports them. 468 */ 469 private void ensureBytecodeVersion() { 470 BCClass bc = getCode().getMethod().getDeclarer(); 471 if (bc.getMajorVersion() < Constants.MAJOR_VERSION_JAVA5) { 472 bc.setMajorVersion(Constants.MAJOR_VERSION_JAVA5); 473 bc.setMinorVersion(Constants.MINOR_VERSION_JAVA5); 474 } 475 } 476 }