001 package serp.bytecode; 002 003 import java.io.*; 004 import java.lang.reflect.*; 005 006 import serp.bytecode.lowlevel.*; 007 import serp.bytecode.visitor.*; 008 import serp.util.*; 009 010 /** 011 * Instruction that takes as an argument a field to operate 012 * on. Examples include <code>getfield, getstatic, setfield, setstatic</code>. 013 * 014 * @author Abe White 015 */ 016 public abstract class FieldInstruction extends Instruction { 017 private int _index = 0; 018 019 FieldInstruction(Code owner, int opcode) { 020 super(owner, opcode); 021 } 022 023 int getLength() { 024 return super.getLength() + 2; 025 } 026 027 //////////////////// 028 // Field operations 029 //////////////////// 030 031 /** 032 * Return the index in the class {@link ConstantPool} of the 033 * {@link ComplexEntry} describing the field to operate on. 034 */ 035 public int getFieldIndex() { 036 return _index; 037 } 038 039 /** 040 * Set the index in the class {@link ConstantPool} of the 041 * {@link ComplexEntry} describing the field to operate on. 042 * 043 * @return this instruction, for method chaining 044 */ 045 public FieldInstruction setFieldIndex(int index) { 046 _index = index; 047 return this; 048 } 049 050 /** 051 * Return the field this instruction operates on, or null if not set. 052 */ 053 public BCField getField() { 054 String dec = getFieldDeclarerName(); 055 if (dec == null) 056 return null; 057 058 BCClass bc = getProject().loadClass(dec, getClassLoader()); 059 BCField[] fields = bc.getFields(getFieldName()); 060 if (fields.length == 0) 061 return null; 062 return fields[0]; 063 } 064 065 /** 066 * Set the field this instruction operates on. 067 * 068 * @return this instruction, for method chaining 069 */ 070 public FieldInstruction setField(BCField field) { 071 if (field == null) 072 return setFieldIndex(0); 073 return setField(field.getDeclarer().getName(), field.getName(), 074 field.getTypeName()); 075 } 076 077 /** 078 * Set the field this instruction operates on. 079 * 080 * @return this instruction, for method chaining 081 */ 082 public FieldInstruction setField(Field field) { 083 if (field == null) 084 return setFieldIndex(0); 085 return setField(field.getDeclaringClass(), field.getName(), 086 field.getType()); 087 } 088 089 /** 090 * Set the field this instruction operates on. 091 * 092 * @param dec the full class name of the field's declaring class 093 * @param name the field name 094 * @param type the full class name of the field type 095 * @return this instruction, for method chaining 096 */ 097 public FieldInstruction setField(String dec, String name, String type) { 098 if (dec == null && name == null && type == null) 099 return setFieldIndex(0); 100 if (dec == null) 101 dec = ""; 102 if (name == null) 103 name = ""; 104 if (type == null) 105 type = ""; 106 107 dec = getProject().getNameCache().getInternalForm(dec, false); 108 type = getProject().getNameCache().getInternalForm(type, true); 109 return setFieldIndex(getPool().findFieldEntry(dec, name, type, true)); 110 } 111 112 /** 113 * Set the field this instruction operates on, for fields that are 114 * declared by the current class. 115 * 116 * @param name the field name 117 * @param type the full class name of the field type 118 * @return this instruction, for method chaining 119 */ 120 public FieldInstruction setField(String name, String type) { 121 BCClass owner = getCode().getMethod().getDeclarer(); 122 return setField(owner.getName(), name, type); 123 } 124 125 /** 126 * Set the field this instruction operates on. 127 * 128 * @param dec the field's declaring class 129 * @param name the field name 130 * @param type the class of the field type 131 * @return this instruction, for method chaining 132 */ 133 public FieldInstruction setField(Class dec, String name, Class type) { 134 String decName = (dec == null) ? null : dec.getName(); 135 String typeName = (type == null) ? null : type.getName(); 136 return setField(decName, name, typeName); 137 } 138 139 /** 140 * Set the field this instruction operates on, for fields that are 141 * declared by the current class. 142 * 143 * @param name the field name 144 * @param type the class of the field type 145 * @return this instruction, for method chaining 146 */ 147 public FieldInstruction setField(String name, Class type) { 148 BCClass owner = getCode().getMethod().getDeclarer(); 149 String typeName = (type == null) ? null : type.getName(); 150 return setField(owner.getName(), name, typeName); 151 } 152 153 /** 154 * Set the field this instruction operates on. 155 * 156 * @param dec the field's declaring class 157 * @param name the field name 158 * @param type the class of the field type 159 * @return this instruction, for method chaining 160 */ 161 public FieldInstruction setField(BCClass dec, String name, BCClass type) { 162 String decName = (dec == null) ? null : dec.getName(); 163 String typeName = (type == null) ? null : type.getName(); 164 return setField(decName, name, typeName); 165 } 166 167 /** 168 * Set the field this instruction operates on, for fields that are 169 * declared by the current class. 170 * 171 * @param name the field name 172 * @param type the class of the field type 173 * @return this instruction, for method chaining 174 */ 175 public FieldInstruction setField(String name, BCClass type) { 176 BCClass owner = getCode().getMethod().getDeclarer(); 177 String typeName = (type == null) ? null : type.getName(); 178 return setField(owner.getName(), name, typeName); 179 } 180 181 //////////////////////////////// 182 // Name, Type, Owner operations 183 //////////////////////////////// 184 185 /** 186 * Return the name of the field this instruction operates on, or null 187 * if not set. 188 */ 189 public String getFieldName() { 190 int index = getFieldIndex(); 191 if (index == 0) 192 return null; 193 194 ComplexEntry entry = (ComplexEntry) getPool().getEntry(index); 195 String name = entry.getNameAndTypeEntry().getNameEntry().getValue(); 196 if (name.length() == 0) 197 return null; 198 return name; 199 } 200 201 /** 202 * Set the name of the field this instruction operates on. 203 * 204 * @return this instruction, for method chaining 205 */ 206 public FieldInstruction setFieldName(String name) { 207 return setField(getFieldDeclarerName(), name, getFieldTypeName()); 208 } 209 210 /** 211 * Return the type of the field this instruction operates on, or null 212 * if not set. 213 */ 214 public String getFieldTypeName() { 215 int index = getFieldIndex(); 216 if (index == 0) 217 return null; 218 219 ComplexEntry entry = (ComplexEntry) getPool().getEntry(index); 220 String name = getProject().getNameCache().getExternalForm(entry. 221 getNameAndTypeEntry().getDescriptorEntry().getValue(), false); 222 if (name.length() == 0) 223 return null; 224 return name; 225 } 226 227 /** 228 * Return the type of the field this instruction operates on, or null 229 * if not set. 230 */ 231 public Class getFieldType() { 232 String type = getFieldTypeName(); 233 if (type == null) 234 return null; 235 return Strings.toClass(type, getClassLoader()); 236 } 237 238 /** 239 * Return the type of the field this instruction operates on, or null 240 * if not set. 241 */ 242 public BCClass getFieldTypeBC() { 243 String type = getFieldTypeName(); 244 if (type == null) 245 return null; 246 return getProject().loadClass(type, getClassLoader()); 247 } 248 249 /** 250 * Set the type of the field this instruction operates on. 251 * 252 * @return this instruction, for method chaining 253 */ 254 public FieldInstruction setFieldType(String type) { 255 return setField(getFieldDeclarerName(), getFieldName(), type); 256 } 257 258 /** 259 * Set the type of the field this instruction operates on. 260 * 261 * @return this instruction, for method chaining 262 */ 263 public FieldInstruction setFieldType(Class type) { 264 String name = null; 265 if (type != null) 266 name = type.getName(); 267 return setFieldType(name); 268 } 269 270 /** 271 * Set the type of the field this instruction operates on. 272 * 273 * @return this instruction, for method chaining 274 */ 275 public FieldInstruction setFieldType(BCClass type) { 276 String name = null; 277 if (type != null) 278 name = type.getName(); 279 return setFieldType(name); 280 } 281 282 /** 283 * Return the declaring class of the field this instruction operates on, 284 * or null if not set. 285 */ 286 public String getFieldDeclarerName() { 287 int index = getFieldIndex(); 288 if (index == 0) 289 return null; 290 291 ComplexEntry entry = (ComplexEntry) getPool().getEntry(index); 292 String name = getProject().getNameCache().getExternalForm(entry. 293 getClassEntry().getNameEntry().getValue(), false); 294 if (name.length() == 0) 295 return null; 296 return name; 297 } 298 299 /** 300 * Return the declaring class of the field this instruction operates on, 301 * or null if not set. 302 */ 303 public Class getFieldDeclarerType() { 304 String type = getFieldDeclarerName(); 305 if (type == null) 306 return null; 307 return Strings.toClass(type, getClassLoader()); 308 } 309 310 /** 311 * Return the declaring class of the field this instruction operates on, 312 * or null if not set. 313 */ 314 public BCClass getFieldDeclarerBC() { 315 String type = getFieldDeclarerName(); 316 if (type == null) 317 return null; 318 return getProject().loadClass(type, getClassLoader()); 319 } 320 321 /** 322 * Set the declaring class of the field this instruction operates on. 323 * 324 * @return this instruction, for method chaining 325 */ 326 public FieldInstruction setFieldDeclarer(String type) { 327 return setField(type, getFieldName(), getFieldTypeName()); 328 } 329 330 /** 331 * Set the declaring class of the field this instruction operates on. 332 * 333 * @return this instruction, for method chaining 334 */ 335 public FieldInstruction setFieldDeclarer(Class type) { 336 String name = null; 337 if (type != null) 338 name = type.getName(); 339 return setFieldDeclarer(name); 340 } 341 342 /** 343 * Set the declaring class of the field this instruction operates on. 344 * 345 * @return this instruction, for method chaining 346 */ 347 public FieldInstruction setFieldDeclarer(BCClass type) { 348 String name = null; 349 if (type != null) 350 name = type.getName(); 351 return setFieldDeclarer(name); 352 } 353 354 /** 355 * FieldInstructions are equal if the field they reference is the same, 356 * or if the field of either is unset. 357 */ 358 public boolean equalsInstruction(Instruction other) { 359 if (other == this) 360 return true; 361 if (!(other instanceof FieldInstruction)) 362 return false; 363 if (!super.equalsInstruction(other)) 364 return false; 365 366 FieldInstruction ins = (FieldInstruction) other; 367 String s1 = getFieldName(); 368 String s2 = ins.getFieldName(); 369 if (!(s1 == null || s2 == null || s1.equals(s2))) 370 return false; 371 372 s1 = getFieldTypeName(); 373 s2 = ins.getFieldTypeName(); 374 if (!(s1 == null || s2 == null || s1.equals(s2))) 375 return false; 376 377 s1 = getFieldDeclarerName(); 378 s2 = ins.getFieldDeclarerName(); 379 if (!(s1 == null || s2 == null || s1.equals(s2))) 380 return false; 381 return true; 382 } 383 384 void read(Instruction orig) { 385 super.read(orig); 386 FieldInstruction ins = (FieldInstruction) orig; 387 setField(ins.getFieldDeclarerName(), ins.getFieldName(), 388 ins.getFieldTypeName()); 389 } 390 391 void read(DataInput in) throws IOException { 392 super.read(in); 393 setFieldIndex(in.readUnsignedShort()); 394 } 395 396 void write(DataOutput out) throws IOException { 397 super.write(out); 398 out.writeShort(getFieldIndex()); 399 } 400 }