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 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 }