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 }