001 package edu.rice.cs.cunit.classFile; 002 003 import edu.rice.cs.cunit.classFile.attributes.AAttributeInfo; 004 import edu.rice.cs.cunit.classFile.attributes.CodeAttributeInfo; 005 import edu.rice.cs.cunit.classFile.attributes.LineNumberTableAttributeInfo; 006 import static edu.rice.cs.cunit.classFile.attributes.LineNumberTableAttributeInfo.LineNumberRecord; 007 import edu.rice.cs.cunit.classFile.attributes.visitors.ADefaultAttributeVisitor; 008 import edu.rice.cs.cunit.classFile.code.InstructionList; 009 import edu.rice.cs.cunit.classFile.constantPool.AUTFPoolInfo; 010 import edu.rice.cs.cunit.classFile.constantPool.ConstantPool; 011 import edu.rice.cs.cunit.classFile.constantPool.visitors.CheckUTFVisitor; 012 013 import java.io.DataInputStream; 014 import java.io.DataOutputStream; 015 import java.io.IOException; 016 import java.util.ArrayList; 017 import java.util.Collections; 018 import java.util.Comparator; 019 020 /** 021 * Represents a method in a class file. 022 * 023 * @author Mathias Ricken 024 */ 025 026 public final class MethodInfo { 027 /** 028 * Method access flags. 029 */ 030 private short _accessFlags; 031 032 /** 033 * Name information. 034 */ 035 private AUTFPoolInfo _name; 036 037 /** 038 * Type descriptor information. 039 */ 040 private AUTFPoolInfo _descriptor; 041 042 /** 043 * Attributes. 044 */ 045 private ArrayList<AAttributeInfo> _attributes = new ArrayList<AAttributeInfo>(); 046 047 /** 048 * Constructor 049 * 050 * @param accessFlags method access flags 051 * @param name method name 052 * @param signature method descriptor 053 * @param attributes array of attributes 054 */ 055 public MethodInfo(short accessFlags, 056 AUTFPoolInfo name, 057 AUTFPoolInfo signature, 058 AAttributeInfo[] attributes) { 059 _accessFlags = accessFlags; 060 _name = name; 061 _descriptor = signature; 062 if (attributes != null) { 063 for(AAttributeInfo attr : attributes) { 064 _attributes.add(attr); 065 } 066 } 067 } 068 069 /** 070 * Constructor 071 * 072 * @param di input stream 073 * @param pool constant pool 074 * 075 * @throws IOException 076 * @throws ClassFormatError 077 */ 078 public MethodInfo(DataInputStream di, ConstantPool pool) throws IOException, ClassFormatError { 079 _accessFlags = di.readShort(); 080 _name = pool.get(di.readShort()).execute(CheckUTFVisitor.singleton(), null); 081 _descriptor = pool.get(di.readShort()).execute(CheckUTFVisitor.singleton(), null); 082 083 int count = di.readShort(); 084 for(int i = 0; i < count; i++) { 085 _attributes.add(AAttributeInfo.read(di, pool)); 086 } 087 } 088 089 /** 090 * Write this method into the stream. 091 * 092 * @param dos output stream 093 * @param pool constant pool 094 * 095 * @throws IOException 096 */ 097 public void write(DataOutputStream dos, ConstantPool pool) throws IOException { 098 dos.writeShort(_accessFlags); 099 dos.writeShort(pool.indexOf(_name)); 100 dos.writeShort(pool.indexOf(_descriptor)); 101 dos.writeShort(_attributes.size()); 102 for(AAttributeInfo attr : _attributes) { 103 attr.write(dos); 104 } 105 } 106 107 /** 108 * Return a human-readable version of this method. 109 * 110 * @return string 111 */ 112 public String toString() { 113 String s = _descriptor.toString(); 114 String paramSig = s.substring(s.indexOf('(') + 1, s.indexOf(')')); 115 String returnSig = s.substring(s.indexOf(')') + 1); 116 117 // handle constructor correctly 118 StringBuilder parameterList = new StringBuilder(); 119 parameterList.append(_name + "("); 120 121 char initialParameter = 'a'; 122 if ((paramSig.length() > 0) && paramSig.charAt(0) != 'V') { 123 while(paramSig.length() > 0) { 124 StringBuilder varName = new StringBuilder(); 125 varName.append(initialParameter); 126 initialParameter++; 127 parameterList.append(ClassFileTools.getTypeString(paramSig, varName.toString())); 128 paramSig = ClassFileTools.getNextSignature(paramSig); 129 if (paramSig.length() > 0) { 130 parameterList.append(", "); 131 } 132 } 133 134 } 135 parameterList.append(")"); 136 137 StringBuilder x = new StringBuilder(); 138 x.append(ClassFileTools.getAccessString(_accessFlags)); 139 x.append(ClassFileTools.getTypeString(returnSig, parameterList.toString())); 140 141 return x.toString(); 142 } 143 144 /** 145 * Return a human-readable version of this method. 146 * 147 * @param pool constant pool 148 * 149 * @return string 150 */ 151 public String toString(final ConstantPool pool) { 152 return toString(pool, true, true); 153 } 154 155 /** 156 * Return a human-readable version of this method. 157 * 158 * @param pool constant pool 159 * @param lineNumbers print line numbers 160 * @param PCs print PC values 161 * @return string 162 */ 163 public String toString(final ConstantPool pool, boolean lineNumbers, boolean PCs) { 164 String s = _descriptor.toString(); 165 String paramSig = s.substring(s.indexOf('(') + 1, s.indexOf(')')); 166 String returnSig = s.substring(s.indexOf(')') + 1); 167 168 // handle constructor correctly 169 StringBuilder parameterList = new StringBuilder(); 170 parameterList.append(_name + "("); 171 172 char initialParameter = 'a'; 173 if ((paramSig.length() > 0) && paramSig.charAt(0) != 'V') { 174 while(paramSig.length() > 0) { 175 StringBuilder varName = new StringBuilder(); 176 varName.append(initialParameter); 177 initialParameter++; 178 parameterList.append(ClassFileTools.getTypeString(paramSig, varName.toString())); 179 paramSig = ClassFileTools.getNextSignature(paramSig); 180 if (paramSig.length() > 0) { 181 parameterList.append(", "); 182 } 183 } 184 185 } 186 parameterList.append(")"); 187 188 final StringBuilder x = new StringBuilder(); 189 x.append(ClassFileTools.getAccessString(_accessFlags)); 190 x.append(ClassFileTools.getTypeString(returnSig, parameterList.toString())); 191 192 for(AAttributeInfo attr : _attributes) { 193 x.append("\n\t" + attr.toString()); 194 } 195 196 if ((_accessFlags & (ClassFile.ACC_NATIVE | ClassFile.ACC_ABSTRACT)) == 0) { 197 CodeAttributeInfo ca = getCodeAttributeInfo(); 198 x.append("\n"+(new InstructionList(ca.getCode())).toString(lineNumbers, PCs, pool)); 199 200 ArrayList<LineNumberRecord> lntErrors = 201 checkLineNumbers(); 202 if (lntErrors.size()>0) { 203 x.append("\t"+LineNumberTableAttributeInfo.getAttributeName()+" Errors!\n"); 204 for(LineNumberRecord lnr : lntErrors) { 205 x.append("\t\t(PC=" + lnr.startPC + " @ " + lnr.lineNo + ")\n"); 206 } 207 } 208 } 209 210 return x.toString(); 211 } 212 213 /** 214 * Check the line number table for errors and return a list of erroneous LineNumberRecords. 215 * @return list of errors; empty if none found 216 */ 217 public ArrayList<LineNumberRecord> checkLineNumbers() { 218 ArrayList<LineNumberRecord> errors = 219 new ArrayList<LineNumberRecord>(); 220 221 if ((_accessFlags & (ClassFile.ACC_NATIVE | ClassFile.ACC_ABSTRACT)) == 0) { 222 CodeAttributeInfo ca = getCodeAttributeInfo(); 223 LineNumberTableAttributeInfo lnt = null; 224 for (AAttributeInfo attr: ca.getAttributes()) { 225 lnt = attr.execute(new ADefaultAttributeVisitor<LineNumberTableAttributeInfo, Object>() { 226 public LineNumberTableAttributeInfo defaultCase(AAttributeInfo host, Object param) { 227 return null; 228 } 229 public LineNumberTableAttributeInfo lineNumberTableCase(LineNumberTableAttributeInfo host, Object param) { 230 return host; 231 } 232 }, null); 233 if (lnt!=null) { break; } 234 } 235 if (lnt!=null) { 236 InstructionList il = new InstructionList(ca.getCode()); 237 ArrayList<LineNumberRecord> lns = 238 new ArrayList<LineNumberRecord>(); 239 for(LineNumberRecord l: lnt.getLineNumbers()) { 240 lns.add(l); 241 } 242 Collections.sort(lns, new Comparator<LineNumberRecord>() { 243 public int compare(LineNumberRecord o1, LineNumberRecord o2) { 244 return o1.startPC-o2.startPC; 245 } 246 247 }); 248 for(LineNumberRecord l: lns) { 249 boolean found = true; 250 while(il.getPCFromIndex(il.getIndex())<l.startPC) { 251 found = il.advanceIndex(); 252 if (!found) { break; } 253 } 254 if (!found || (il.getPCFromIndex(il.getIndex())!=l.startPC)) { 255 errors.add(l); 256 } 257 } 258 } 259 } 260 261 return errors; 262 } 263 264 /** 265 * Accessor for access flags. 266 * 267 * @return access flags 268 */ 269 public short getAccessFlags() { 270 return _accessFlags; 271 } 272 273 /** 274 * Mutator for access flags. 275 * 276 * @param accessFlags new access flags 277 */ 278 public void setAccessFlags(short accessFlags) { 279 _accessFlags = accessFlags; 280 } 281 282 /** 283 * Accessor for field name. 284 * 285 * @return field name 286 */ 287 public AUTFPoolInfo getName() { 288 return _name; 289 } 290 291 /** 292 * Mutator for field name. 293 * 294 * @param name new field name 295 */ 296 public void setName(AUTFPoolInfo name) { 297 _name = name; 298 } 299 300 /** 301 * Accessor for descriptor. 302 * 303 * @return descriptor 304 */ 305 public AUTFPoolInfo getDescriptor() { 306 return _descriptor; 307 } 308 309 /** 310 * Mutator for descriptor. 311 * 312 * @param descriptor new descriptor 313 */ 314 public void setDescriptor(AUTFPoolInfo descriptor) { 315 _descriptor = descriptor; 316 } 317 318 /** 319 * Accessor for attributes. 320 * 321 * @return attributes 322 */ 323 public ArrayList<AAttributeInfo> getAttributes() { 324 return _attributes; 325 } 326 327 328 /** 329 * Return the attribute with the specified name. 330 * 331 * @param name attribute name 332 * 333 * @return attribute or null if not found 334 */ 335 public AAttributeInfo getAttribute(String name) { 336 if (0 == _attributes.size()) { 337 return null; 338 } 339 for(AAttributeInfo attr : _attributes) { 340 if (0 == name.compareTo(attr.getName().toString())) { 341 return attr; 342 } 343 } 344 return null; 345 } 346 347 /** 348 * Return this method's code attribute info. 349 * 350 * @return code attribute info 351 */ 352 public CodeAttributeInfo getCodeAttributeInfo() { 353 for(AAttributeInfo attr : _attributes) { 354 CodeAttributeInfo code = attr.execute(new ADefaultAttributeVisitor<CodeAttributeInfo, Object>() { 355 public CodeAttributeInfo defaultCase(AAttributeInfo host, Object o) { 356 return null; 357 } 358 359 public CodeAttributeInfo codeCase(CodeAttributeInfo host, Object o) { 360 return host; 361 } 362 }, null); 363 if (code != null) { 364 return code; 365 } 366 367 } 368 throw new ClassFormatError("Method has no Code attribute"); 369 } 370 371 /** 372 * Return the method's signature, i.e. it's name with parameter types followed in parentheses: 373 * "foo(int,float,java.lang.String)". 374 * @return signature 375 */ 376 public String getSignature() { 377 String s = _descriptor.toString(); 378 String paramSig = s.substring(s.indexOf('(') + 1, s.indexOf(')')); 379 380 // handle constructor correctly 381 StringBuilder sb = new StringBuilder(getName().toString()); 382 sb.append('('); 383 384 if ((paramSig.length() > 0) && paramSig.charAt(0) != 'V') { 385 while(paramSig.length() > 0) { 386 sb.append(ClassFileTools.getTypeString(paramSig, "").trim()); 387 paramSig = ClassFileTools.getNextSignature(paramSig); 388 if (paramSig.length() > 0) { 389 sb.append(","); 390 } 391 } 392 } 393 sb.append(")"); 394 return sb.toString(); 395 } 396 397 /** 398 * Indicates whether some other object is "equal to" this one. 399 * @param o the reference object with which to compare. 400 * @return <code>true</code> if this object is the same as the obj argument; <code>false</code> otherwise. 401 */ 402 public boolean equals(Object o) { 403 if (this == o) { 404 return true; 405 } 406 if (o == null || getClass() != o.getClass()) { 407 return false; 408 } 409 410 MethodInfo that = (MethodInfo)o; 411 412 if (_accessFlags != that._accessFlags) { 413 return false; 414 } 415 if (_attributes != null ? !_attributes.equals(that._attributes) : that._attributes != null) { 416 return false; 417 } 418 if (_descriptor != null ? !_descriptor.equals(that._descriptor) : that._descriptor != null) { 419 return false; 420 } 421 if (_name != null ? !_name.equals(that._name) : that._name != null) { 422 return false; 423 } 424 425 return true; 426 } 427 428 /** 429 * Returns a hash code value for the object. 430 * @return a hash code value for this object. 431 */ 432 public int hashCode() { 433 int result; 434 result = (int)_accessFlags; 435 result = 31 * result + (_name != null ? _name.hashCode() : 0); 436 result = 31 * result + (_descriptor != null ? _descriptor.hashCode() : 0); 437 result = 31 * result + (_attributes != null ? _attributes.hashCode() : 0); 438 return result; 439 } 440 }