1   // Copyright 2013 Bill Campbell, Swami Iyer and Bahar Akbal-Delibas
2   
3   package jminusminus;
4   
5   import java.io.EOFException;
6   import java.io.IOException;
7   import java.io.DataInputStream;
8   import java.io.InputStream;
9   import java.util.ArrayList;
10  import static jminusminus.CLConstants.*;
11  
12  /**
13   * CLAbsorber is for reading a Java class into an in-memory CLFile
14   * representation and printing it out to STDOUT in a format similar to that of
15   * javap.
16   */
17  
18  public class CLAbsorber {
19  
20      /** CLFile representation of the class that is read. */
21      private CLFile classFile;
22  
23      /** Whether or not an error occurred in reading the class. */
24      private boolean errorHasOccurred;
25  
26      /** Name of the class that is read. */
27      private String className;
28  
29      /**
30       * Print the specified warning to STDERR.
31       * 
32       * @param message
33       *            warning.
34       * @param args
35       *            related values.
36       */
37  
38      private void reportWarning(String message, Object... args) {
39          System.err.printf("CLAbsorber Warning: " + message + "\n", args);
40      }
41  
42      /**
43       * Print the specified error message to STDERR and set the error flag to
44       * true.
45       * 
46       * @param message
47       *            error message.
48       * @param args
49       *            related values.
50       */
51  
52      private void reportError(String message, Object... args) {
53          System.err.printf("CLAbsorber Error: " + message + "\n", args);
54          errorHasOccurred = true;
55      }
56  
57      /**
58       * Read the constant pool information from the specified stream, and return
59       * the information as a CLConstantPool object.
60       * 
61       * @param in
62       *            input stream.
63       * @return the constant pool.
64       */
65  
66      private CLConstantPool readConstantPool(CLInputStream in) {
67          CLConstantPool cp = new CLConstantPool();
68          try {
69              for (int i = 1; i < classFile.constantPoolCount; i++) {
70                  int tag = in.readUnsignedByte();
71                  switch (tag) {
72                  case CONSTANT_Class:
73                      cp
74                              .addCPItem(new CLConstantClassInfo(in
75                                      .readUnsignedShort()));
76                      break;
77                  case CONSTANT_Fieldref:
78                      cp.addCPItem(new CLConstantFieldRefInfo(in
79                              .readUnsignedShort(), in.readUnsignedShort()));
80                      break;
81                  case CONSTANT_Methodref:
82                      cp.addCPItem(new CLConstantMethodRefInfo(in
83                              .readUnsignedShort(), in.readUnsignedShort()));
84                      break;
85                  case CONSTANT_InterfaceMethodref:
86                      cp.addCPItem(new CLConstantInterfaceMethodRefInfo(in
87                              .readUnsignedShort(), in.readUnsignedShort()));
88                      break;
89                  case CONSTANT_String:
90                      cp.addCPItem(new CLConstantStringInfo(in
91                              .readUnsignedShort()));
92                      break;
93                  case CONSTANT_Integer:
94                      cp.addCPItem(new CLConstantIntegerInfo(in.readInt()));
95                      break;
96                  case CONSTANT_Float:
97                      cp.addCPItem(new CLConstantFloatInfo(in.readFloat()));
98                      break;
99                  case CONSTANT_Long:
100                     cp.addCPItem(new CLConstantLongInfo(in.readLong()));
101                     i++;
102                     break;
103                 case CONSTANT_Double:
104                     cp.addCPItem(new CLConstantDoubleInfo(in.readDouble()));
105                     i++;
106                     break;
107                 case CONSTANT_NameAndType:
108                     cp.addCPItem(new CLConstantNameAndTypeInfo(in
109                             .readUnsignedShort(), in.readUnsignedShort()));
110                     break;
111                 case CONSTANT_Utf8:
112                     int length = in.readUnsignedShort();
113                     byte[] b = new byte[length];
114                     in.read(b);
115                     cp.addCPItem(new CLConstantUtf8Info(b));
116                     break;
117                 default:
118                     reportError("Unknown cp_info tag '%d'", tag);
119                     return cp;
120                 }
121             }
122         } catch (IOException e) {
123             reportError("Error reading constant pool from file %s", className);
124         }
125         return cp;
126     }
127 
128     /**
129      * Read the fields from the specified stream, and return them as a list.
130      * 
131      * @param in
132      *            input stream.
133      * @param fieldsCount
134      *            number of fields.
135      * @return list of fields.
136      */
137 
138     private ArrayList<CLFieldInfo> readFields(CLInputStream in, int fieldsCount) {
139         ArrayList<CLFieldInfo> fields = new ArrayList<CLFieldInfo>();
140         try {
141             for (int i = 0; i < fieldsCount; i++) {
142                 int accessFlags = in.readUnsignedShort();
143                 int nameIndex = in.readUnsignedShort();
144                 int descriptorIndex = in.readUnsignedShort();
145                 int attributesCount = in.readUnsignedShort();
146                 fields.add(new CLFieldInfo(accessFlags, nameIndex,
147                         descriptorIndex, attributesCount, readAttributes(in,
148                                 attributesCount)));
149             }
150         } catch (IOException e) {
151             reportError("Error reading fields from file %s", className);
152         }
153         return fields;
154     }
155 
156     /**
157      * Read the methods from the specified stream, and return them as a list.
158      * 
159      * @param in
160      *            input stream.
161      * @param methodsCount
162      *            number of methods.
163      * @return the methods.
164      */
165 
166     private ArrayList<CLMethodInfo> readMethods(CLInputStream in,
167             int methodsCount) {
168         ArrayList<CLMethodInfo> methods = new ArrayList<CLMethodInfo>();
169         try {
170             for (int i = 0; i < methodsCount; i++) {
171                 int accessFlags = in.readUnsignedShort();
172                 int nameIndex = in.readUnsignedShort();
173                 int descriptorIndex = in.readUnsignedShort();
174                 int attributesCount = in.readUnsignedShort();
175                 methods.add(new CLMethodInfo(accessFlags, nameIndex,
176                         descriptorIndex, attributesCount, readAttributes(in,
177                                 attributesCount)));
178             }
179         } catch (IOException e) {
180             reportError("Error reading methods from file %s", className);
181         }
182         return methods;
183     }
184 
185     /**
186      * Read the attributes from the specified stream, and return them as a list
187      * 
188      * @param in
189      *            input stream.
190      * @param attributeCount
191      *            number of attributes.
192      * @return list of attributes.
193      */
194 
195     private ArrayList<CLAttributeInfo> readAttributes(CLInputStream in,
196             int attributesCount) {
197         ArrayList<CLAttributeInfo> attributes = new ArrayList<CLAttributeInfo>();
198         try {
199             CLConstantPool cp = classFile.constantPool;
200             for (int i = 0; i < attributesCount; i++) {
201                 int attributeNameIndex = in.readUnsignedShort();
202                 long attributeLength = in.readUnsignedInt();
203                 CLAttributeInfo attributeInfo = null;
204                 String attributeName = new String(((CLConstantUtf8Info) cp
205                         .cpItem(attributeNameIndex)).b);
206                 if (attributeName.equals(ATT_CONSTANT_VALUE)) {
207                     attributeInfo = readConstantValueAttribute(in,
208                             attributeNameIndex, attributeLength);
209                 } else if (attributeName.equals(ATT_CODE)) {
210                     attributeInfo = readCodeAttribute(in, attributeNameIndex,
211                             attributeLength);
212                 } else if (attributeName.equals(ATT_EXCEPTIONS)) {
213                     attributeInfo = readExceptionsAttribute(in,
214                             attributeNameIndex, attributeLength);
215                 } else if (attributeName.equals(ATT_INNER_CLASSES)) {
216                     attributeInfo = readInnerClassesAttribute(in,
217                             attributeNameIndex, attributeLength);
218                 } else if (attributeName.equals(ATT_ENCLOSING_METHOD)) {
219                     attributeInfo = readEnclosingMethodAttribute(in,
220                             attributeNameIndex, attributeLength);
221                 } else if (attributeName.equals(ATT_SYNTHETIC)) {
222                     attributeInfo = readSyntheticAttribute(in,
223                             attributeNameIndex, attributeLength);
224                 } else if (attributeName.equals(ATT_SIGNATURE)) {
225                     attributeInfo = readSignatureAttribute(in,
226                             attributeNameIndex, attributeLength);
227                 } else if (attributeName.equals(ATT_SOURCE_FILE)) {
228                     attributeInfo = readSourceFileAttribute(in,
229                             attributeNameIndex, attributeLength);
230                 } else if (attributeName.equals(ATT_SOURCE_DEBUG_EXTENSION)) {
231                     attributeInfo = readSourceDebugExtensionAttribute(in,
232                             attributeNameIndex, attributeLength);
233                 } else if (attributeName.equals(ATT_LINE_NUMBER_TABLE)) {
234                     attributeInfo = readLineNumberTableAttribute(in,
235                             attributeNameIndex, attributeLength);
236                 } else if (attributeName.equals(ATT_LOCAL_VARIABLE_TABLE)) {
237                     attributeInfo = readLocalVariableTableAttribute(in,
238                             attributeNameIndex, attributeLength);
239                 } else if (attributeName.equals(ATT_LOCAL_VARIABLE_TYPE_TABLE)) {
240                     attributeInfo = readLocalVariableTypeTableAttribute(in,
241                             attributeNameIndex, attributeLength);
242                 } else if (attributeName.equals(ATT_DEPRECATED)) {
243                     attributeInfo = readDeprecatedAttribute(in,
244                             attributeNameIndex, attributeLength);
245                 } else if (attributeName
246                         .equals(ATT_RUNTIME_VISIBLE_ANNOTATIONS)) {
247                     attributeInfo = readRuntimeVisibleAnnotationsAttribute(in,
248                             attributeNameIndex, attributeLength);
249                 } else if (attributeName
250                         .equals(ATT_RUNTIME_INVISIBLE_ANNOTATIONS)) {
251                     attributeInfo = readRuntimeInvisibleAnnotationsAttribute(
252                             in, attributeNameIndex, attributeLength);
253                 } else if (attributeName
254                         .equals(ATT_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS)) {
255                     attributeInfo = readRuntimeVisibleParameterAnnotationsAttribute(
256                             in, attributeNameIndex, attributeLength);
257                 } else if (attributeName
258                         .equals(ATT_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS)) {
259                     attributeInfo = readRuntimeInvisibleParameterAnnotationsAttribute(
260                             in, attributeNameIndex, attributeLength);
261                 } else if (attributeName.equals(ATT_ANNOTATION_DEFAULT)) {
262                     attributeInfo = readAnnotationDefaultAttribute(in,
263                             attributeNameIndex, attributeLength);
264                 } else {
265                     reportWarning("Unknown attribute '%s'", attributeName,
266                             className);
267                     for (long j = 0; j < attributeLength; j++) {
268                         in.readUnsignedByte();
269                     }
270                 }
271                 if (attributeInfo != null) {
272                     attributes.add(attributeInfo);
273                 }
274             }
275         } catch (IOException e) {
276             reportError("Error reading attributes from file %s", className);
277         }
278         return attributes;
279     }
280 
281     /**
282      * Read a ConstantValue attribute from the specified input stream, and
283      * return it.
284      * 
285      * @param in
286      *            input stream.
287      * @param attributeNameIndex
288      *            constant pool index of the attribute name.
289      * @param attributeLength
290      *            length of attribute.
291      * @return a ConstantValue attribute.
292      */
293 
294     private CLConstantValueAttribute readConstantValueAttribute(
295             CLInputStream in, int attributeNameIndex, long attributeLength) {
296         CLConstantValueAttribute attribute = null;
297         try {
298             attribute = new CLConstantValueAttribute(attributeNameIndex,
299                     attributeLength, in.readUnsignedShort());
300         } catch (IOException e) {
301             reportError("Error reading ConstantValue_attribute from file %s",
302                     className);
303         }
304         return attribute;
305     }
306 
307     /**
308      * Read a Code attribute from the specified input stream, and return it.
309      * 
310      * @param in
311      *            input stream.
312      * @param attributeNameIndex
313      *            constant pool index of the attribute name.
314      * @param attributeLength
315      *            length of attribute.
316      * @return a Code attribute.
317      */
318 
319     private CLCodeAttribute readCodeAttribute(CLInputStream in,
320             int attributeNameIndex, long attributeLength) {
321         CLCodeAttribute attribute = null;
322         try {
323             int maxStack = in.readUnsignedShort();
324             int maxLocals = in.readUnsignedShort();
325             ArrayList<Integer> code = new ArrayList<Integer>();
326             long codeLength = in.readUnsignedInt();
327             for (long l = 0; l < codeLength; l++) {
328                 code.add(in.readUnsignedByte());
329             }
330             int exceptionTableLength = in.readUnsignedShort();
331             ArrayList<CLExceptionInfo> exceptionTable = new ArrayList<CLExceptionInfo>();
332             for (int l = 0; l < exceptionTableLength; l++) {
333                 int startPC = in.readUnsignedShort();
334                 int endPC = in.readUnsignedShort();
335                 int handlerPC = in.readUnsignedShort();
336                 int catchType = in.readUnsignedShort();
337                 exceptionTable.add(new CLExceptionInfo(startPC, endPC,
338                         handlerPC, catchType));
339             }
340             int codeAttrAttributesCount = in.readUnsignedShort();
341             ArrayList<CLAttributeInfo> codeAttrAttributes = readAttributes(in,
342                     codeAttrAttributesCount);
343             attribute = new CLCodeAttribute(attributeNameIndex,
344                     attributeLength, maxStack, maxLocals, codeLength, code,
345                     exceptionTableLength, exceptionTable,
346                     codeAttrAttributesCount, codeAttrAttributes);
347         } catch (IOException e) {
348             reportError("Error reading Code_attribute from file %s", className);
349         }
350         return attribute;
351     }
352 
353     /**
354      * Read an Exceptions attribute from the specified input stream, and return
355      * it.
356      * 
357      * @param in
358      *            input stream.
359      * @param attributeNameIndex
360      *            constant pool index of the attribute name.
361      * @param attributeLength
362      *            length of attribute.
363      * @return an Exceptions attribute.
364      */
365 
366     private CLExceptionsAttribute readExceptionsAttribute(CLInputStream in,
367             int attributeNameIndex, long attributeLength) {
368         CLExceptionsAttribute attribute = null;
369         try {
370             int numberOfExceptions = in.readUnsignedShort();
371             ArrayList<Integer> exceptionIndexTable = new ArrayList<Integer>();
372             for (int l = 0; l < numberOfExceptions; l++) {
373                 exceptionIndexTable.add(in.readUnsignedShort());
374             }
375             attribute = new CLExceptionsAttribute(attributeNameIndex,
376                     attributeLength, numberOfExceptions, exceptionIndexTable);
377         } catch (IOException e) {
378             reportError("Error reading Exceptions_attribute from file %s",
379                     className);
380         }
381         return attribute;
382     }
383 
384     /**
385      * Read an InnerClasses attribute from the specified input stream, and
386      * return it.
387      * 
388      * @param in
389      *            input stream.
390      * @param attributeNameIndex
391      *            constant pool index of the attribute name.
392      * @param attributeLength
393      *            length of attribute.
394      * @return an InnerClasses attribute.
395      */
396 
397     private CLInnerClassesAttribute readInnerClassesAttribute(CLInputStream in,
398             int attributeNameIndex, long attributeLength) {
399         CLInnerClassesAttribute attribute = null;
400         try {
401             int numberOfClasses = in.readUnsignedShort();
402             ArrayList<CLInnerClassInfo> classes = new ArrayList<CLInnerClassInfo>();
403             for (int m = 0; m < numberOfClasses; m++) {
404                 classes.add(new CLInnerClassInfo(in.readUnsignedShort(), in
405                         .readUnsignedShort(), in.readUnsignedShort(), in
406                         .readUnsignedShort()));
407             }
408             attribute = new CLInnerClassesAttribute(attributeNameIndex,
409                     attributeLength, numberOfClasses, classes);
410 
411         } catch (IOException e) {
412             reportError("Error reading InnerClasses_attribute from file %s",
413                     className);
414         }
415         return attribute;
416     }
417 
418     /**
419      * Read an EnclosingMethod attribute from the specified input stream, and
420      * return it.
421      * 
422      * @param in
423      *            input stream.
424      * @param attributeNameIndex
425      *            constant pool index of the attribute name.
426      * @param attributeLength
427      *            length of attribute.
428      * @return an EnclosingMethod attribute.
429      */
430 
431     private CLEnclosingMethodAttribute readEnclosingMethodAttribute(
432             CLInputStream in, int attributeNameIndex, long attributeLength) {
433         CLEnclosingMethodAttribute attribute = null;
434         try {
435             attribute = new CLEnclosingMethodAttribute(attributeNameIndex,
436                     attributeLength, in.readUnsignedShort(), in
437                             .readUnsignedShort());
438         } catch (IOException e) {
439             reportError("Error reading EnclosingMethod_attribute from file %s",
440                     className);
441         }
442         return attribute;
443     }
444 
445     /**
446      * Read a Synthetic attribute from the specified input stream, and return
447      * it.
448      * 
449      * @param in
450      *            input stream.
451      * @param attributeNameIndex
452      *            constant pool index of the attribute name.
453      * @param attributeLength
454      *            length of attribute.
455      * @return a Synthetic attribute.
456      */
457 
458     private CLSyntheticAttribute readSyntheticAttribute(CLInputStream in,
459             int attributeNameIndex, long attributeLength) {
460         return new CLSyntheticAttribute(attributeNameIndex, attributeLength);
461     }
462 
463     /**
464      * Read a Signature attribute from the specified input stream, and return
465      * it.
466      * 
467      * @param in
468      *            input stream.
469      * @param attributeNameIndex
470      *            constant pool index of the attribute name.
471      * @param attributeLength
472      *            length of attribute.
473      * @return a Signature attribute.
474      */
475 
476     private CLSignatureAttribute readSignatureAttribute(CLInputStream in,
477             int attributeNameIndex, long attributeLength) {
478         CLSignatureAttribute attribute = null;
479         try {
480             attribute = new CLSignatureAttribute(attributeNameIndex,
481                     attributeLength, in.readUnsignedShort());
482         } catch (IOException e) {
483             reportError("Error reading Signature_attribute from file %s",
484                     className);
485         }
486         return attribute;
487     }
488 
489     /**
490      * Read a SourceFile attribute from the specified input stream, and return
491      * it.
492      * 
493      * @param in
494      *            input stream.
495      * @param attributeNameIndex
496      *            constant pool index of the attribute name.
497      * @param attributeLength
498      *            length of attribute.
499      * @return a SourceFile attribute.
500      */
501 
502     private CLSourceFileAttribute readSourceFileAttribute(CLInputStream in,
503             int attributeNameIndex, long attributeLength) {
504         CLSourceFileAttribute attribute = null;
505         try {
506             attribute = new CLSourceFileAttribute(attributeNameIndex,
507                     attributeLength, in.readUnsignedShort());
508         } catch (IOException e) {
509             reportError("Error reading SourceFile_attribute from file %s",
510                     className);
511         }
512         return attribute;
513     }
514 
515     /**
516      * Read a SourceDebugExtension attribute from the specified input stream,
517      * and return it.
518      * 
519      * @param in
520      *            input stream.
521      * @param attributeNameIndex
522      *            constant pool index of the attribute name.
523      * @param attributeLength
524      *            length of attribute.
525      * @return a SourceDebugExtension attribute.
526      */
527 
528     private CLSourceDebugExtensionAttribute readSourceDebugExtensionAttribute(
529             CLInputStream in, int attributeNameIndex, long attributeLength) {
530         CLSourceDebugExtensionAttribute attribute = null;
531         try {
532             byte[] b = new byte[(int) attributeLength];
533 
534             in.read(b);
535             attribute = new CLSourceDebugExtensionAttribute(attributeNameIndex,
536                     attributeLength, b);
537         } catch (IOException e) {
538             reportError("Error reading SourceDebugExtension_attribute "
539                     + "from file %s", className);
540         }
541         return attribute;
542     }
543 
544     /**
545      * Read a LineNumberTable attribute from the specified input stream, and
546      * return it.
547      * 
548      * @param in
549      *            input stream.
550      * @param attributeNameIndex
551      *            constant pool index of the attribute name.
552      * @param attributeLength
553      *            length of attribute.
554      * @return a LineNumberTable attribute.
555      */
556 
557     private CLLineNumberTableAttribute readLineNumberTableAttribute(
558             CLInputStream in, int attributeNameIndex, long attributeLength) {
559         CLLineNumberTableAttribute attribute = null;
560         try {
561             int lineNumberTableLength = in.readUnsignedShort();
562             ArrayList<CLLineNumberInfo> lineNumberTable = new ArrayList<CLLineNumberInfo>();
563             for (int m = 0; m < lineNumberTableLength; m++) {
564                 lineNumberTable.add(new CLLineNumberInfo(
565                         in.readUnsignedShort(), in.readUnsignedShort()));
566             }
567             attribute = new CLLineNumberTableAttribute(attributeNameIndex,
568                     attributeLength, lineNumberTableLength, lineNumberTable);
569         } catch (IOException e) {
570             reportError("Error reading LineNumberTable_attribute from file %s",
571                     className);
572         }
573         return attribute;
574     }
575 
576     /**
577      * Read a LocalVariableTable attribute from the specified input stream, and
578      * return it.
579      * 
580      * @param in
581      *            input stream.
582      * @param attributeNameIndex
583      *            constant pool index of the attribute name.
584      * @param attributeLength
585      *            length of attribute.
586      * @return a LocalVariableTable attribute.
587      */
588 
589     private CLLocalVariableTableAttribute readLocalVariableTableAttribute(
590             CLInputStream in, int attributeNameIndex, long attributeLength) {
591         CLLocalVariableTableAttribute attribute = null;
592         try {
593             int localVariableTableLength = in.readUnsignedShort();
594             ArrayList<CLLocalVariableInfo> localVariableTable = new ArrayList<CLLocalVariableInfo>();
595             for (int m = 0; m < localVariableTableLength; m++) {
596                 localVariableTable.add(new CLLocalVariableInfo(in
597                         .readUnsignedShort(), in.readUnsignedShort(), in
598                         .readUnsignedShort(), in.readUnsignedShort(), in
599                         .readUnsignedShort()));
600             }
601             attribute = new CLLocalVariableTableAttribute(attributeNameIndex,
602                     attributeLength, localVariableTableLength,
603                     localVariableTable);
604         } catch (IOException e) {
605             reportError("Error reading LocalVariableTable_attribute "
606                     + "from file %s", className);
607         }
608         return attribute;
609     }
610 
611     /**
612      * Read a LocalVariableTypeTable attribute from the specified input stream,
613      * and return it.
614      * 
615      * @param in
616      *            input stream.
617      * @param attributeNameIndex
618      *            constant pool index of the attribute name.
619      * @param attributeLength
620      *            length of attribute.
621      * @return a LocalVariableTypeTable attribute.
622      */
623 
624     private CLLocalVariableTypeTableAttribute readLocalVariableTypeTableAttribute(
625             CLInputStream in, int attributeNameIndex, long attributeLength) {
626         CLLocalVariableTypeTableAttribute attribute = null;
627         try {
628             int localVariableTypeTableLength = in.readUnsignedShort();
629             ArrayList<CLLocalVariableTypeInfo> localVariableTypeTable = new ArrayList<CLLocalVariableTypeInfo>();
630             for (int m = 0; m < localVariableTypeTableLength; m++) {
631                 localVariableTypeTable.add(new CLLocalVariableTypeInfo(in
632                         .readUnsignedShort(), in.readUnsignedShort(), in
633                         .readUnsignedShort(), in.readUnsignedShort(), in
634                         .readUnsignedShort()));
635             }
636             attribute = new CLLocalVariableTypeTableAttribute(
637                     attributeNameIndex, attributeLength,
638                     localVariableTypeTableLength, localVariableTypeTable);
639         } catch (IOException e) {
640             reportError("Error reading LocalVariableTypeTable_attribute"
641                     + "file %s", className);
642         }
643         return attribute;
644     }
645 
646     /**
647      * Read a Deprecated attribute from the specified input stream, and return
648      * it.
649      * 
650      * @param in
651      *            input stream.
652      * @param attributeNameIndex
653      *            constant pool index of the attribute name.
654      * @param attributeLength
655      *            length of attribute.
656      * @return a Deprecated attribute.
657      */
658 
659     private CLDeprecatedAttribute readDeprecatedAttribute(CLInputStream in,
660             int attributeNameIndex, long attributeLength) {
661         return new CLDeprecatedAttribute(attributeNameIndex, attributeLength);
662     }
663 
664     /**
665      * Read a RuntimeVisibleAnnotations attribute from the specified input
666      * stream, and return it.
667      * 
668      * @param in
669      *            input stream.
670      * @param attributeNameIndex
671      *            constant pool index of the attribute name.
672      * @param attributeLength
673      *            length of attribute.
674      * @return a RuntimeVisibleAnnotations attribute.
675      */
676 
677     private CLRuntimeVisibleAnnotationsAttribute readRuntimeVisibleAnnotationsAttribute(
678             CLInputStream in, int attributeNameIndex, long attributeLength) {
679         CLRuntimeVisibleAnnotationsAttribute attribute = null;
680         try {
681             int numAnnotations = in.readUnsignedShort();
682             ArrayList<CLAnnotation> annotations = new ArrayList<CLAnnotation>();
683             for (int i = 0; i < numAnnotations; i++) {
684                 CLAnnotation annotation = readAnnotation(in);
685                 annotations.add(annotation);
686             }
687             attribute = new CLRuntimeVisibleAnnotationsAttribute(
688                     attributeNameIndex, attributeLength, numAnnotations,
689                     annotations);
690         } catch (IOException e) {
691             reportError("Error reading RuntimeVisibleAnnotations_attribute"
692                     + "from file %s", className);
693         }
694         return attribute;
695     }
696 
697     /**
698      * Read a RuntimeInvisibleAnnotations attribute from the specified input
699      * stream, and return it.
700      * 
701      * @param in
702      *            input stream.
703      * @param attributeNameIndex
704      *            constant pool index of the attribute name.
705      * @param attributeLength
706      *            length of attribute.
707      * @return a RuntimeInvisibleAnnotations attribute.
708      */
709 
710     private CLRuntimeInvisibleAnnotationsAttribute readRuntimeInvisibleAnnotationsAttribute(
711             CLInputStream in, int attributeNameIndex, long attributeLength) {
712         CLRuntimeInvisibleAnnotationsAttribute attribute = null;
713         try {
714             int numAnnotations = in.readUnsignedShort();
715             ArrayList<CLAnnotation> annotations = new ArrayList<CLAnnotation>();
716             for (int i = 0; i < numAnnotations; i++) {
717                 CLAnnotation annotation = readAnnotation(in);
718                 annotations.add(annotation);
719             }
720             attribute = new CLRuntimeInvisibleAnnotationsAttribute(
721                     attributeNameIndex, attributeLength, numAnnotations,
722                     annotations);
723         } catch (IOException e) {
724             reportError("Error reading RuntimeInvisibleAnnotations_attribute"
725                     + "from file %s", className);
726         }
727         return attribute;
728     }
729 
730     /**
731      * Read a RuntimeVisibleParameterAnnotations attribute from the specified
732      * input stream, and return it.
733      * 
734      * @param in
735      *            input stream.
736      * @param attributeNameIndex
737      *            constant pool index of the attribute name.
738      * @param attributeLength
739      *            length of attribute.
740      * @return a RuntimeVisibleParameterAnnotations attribute.
741      */
742 
743     private CLRuntimeVisibleParameterAnnotationsAttribute readRuntimeVisibleParameterAnnotationsAttribute(
744             CLInputStream in, int attributeNameIndex, long attributeLength) {
745         CLRuntimeVisibleParameterAnnotationsAttribute attribute = null;
746         try {
747             int numParameters = in.readUnsignedByte();
748             ArrayList<CLParameterAnnotationInfo> parameterAnnotations = new ArrayList<CLParameterAnnotationInfo>();
749             for (int i = 0; i < numParameters; i++) {
750                 int numAnnotations = in.readUnsignedShort();
751                 ArrayList<CLAnnotation> annotations = new ArrayList<CLAnnotation>();
752                 for (int j = 0; j < numAnnotations; j++) {
753                     CLAnnotation annotation = readAnnotation(in);
754                     annotations.add(annotation);
755                 }
756                 parameterAnnotations.add(new CLParameterAnnotationInfo(
757                         numAnnotations, annotations));
758             }
759             attribute = new CLRuntimeVisibleParameterAnnotationsAttribute(
760                     attributeNameIndex, attributeLength, (short) numParameters,
761                     parameterAnnotations);
762         } catch (IOException e) {
763             reportError("Error reading "
764                     + "RuntimeVisibleParameterAnnotations_attribute"
765                     + " from file %s", className);
766         }
767         return attribute;
768     }
769 
770     /**
771      * Read a RuntimeInvisibleParameterAnnotations attribute from the specified
772      * input stream, and return it.
773      * 
774      * @param in
775      *            input stream.
776      * @param attributeNameIndex
777      *            constant pool index of the attribute name.
778      * @param attributeLength
779      *            length of attribute.
780      * @return a RuntimeInvisibleParameterAnnotations attribute.
781      */
782 
783     private CLRuntimeInvisibleParameterAnnotationsAttribute readRuntimeInvisibleParameterAnnotationsAttribute(
784             CLInputStream in, int attributeNameIndex, long attributeLength) {
785         CLRuntimeInvisibleParameterAnnotationsAttribute attribute = null;
786         try {
787             int numParameters = in.readUnsignedByte();
788             ArrayList<CLParameterAnnotationInfo> parameterAnnotations = new ArrayList<CLParameterAnnotationInfo>();
789             for (int i = 0; i < numParameters; i++) {
790                 int numAnnotations = in.readUnsignedShort();
791                 ArrayList<CLAnnotation> annotations = new ArrayList<CLAnnotation>();
792                 for (int j = 0; j < numAnnotations; j++) {
793                     CLAnnotation annotation = readAnnotation(in);
794                     annotations.add(annotation);
795                 }
796                 parameterAnnotations.add(new CLParameterAnnotationInfo(
797                         numAnnotations, annotations));
798             }
799             attribute = new CLRuntimeInvisibleParameterAnnotationsAttribute(
800                     attributeNameIndex, attributeLength, (short) numParameters,
801                     parameterAnnotations);
802         } catch (IOException e) {
803             reportError("Error reading "
804                     + "RuntimeInvisibleParameterAnnotations_attribute"
805                     + " from file %s", className);
806         }
807         return attribute;
808     }
809 
810     /**
811      * Read a AnnotationDefault attribute from t he specified input stream, and
812      * return it.
813      * 
814      * @param in
815      *            input stream.
816      * @param attributeNameIndex
817      *            constant pool index of the attribute name.
818      * @param attributeLength
819      *            length of attribute.
820      * @return a AnnotationDefault attribute.
821      */
822 
823     private CLAnnotationDefaultAttribute readAnnotationDefaultAttribute(
824             CLInputStream in, int attributeNameIndex, long attributeLength) {
825         return new CLAnnotationDefaultAttribute(attributeNameIndex,
826                 attributeLength, readElementValue(in));
827     }
828 
829     /**
830      * Read an ElementValue from the specified input stream, and return it.
831      * 
832      * @param in
833      *            input stream.
834      * @return an ElemenvValue.
835      */
836 
837     private CLElementValue readElementValue(CLInputStream in) {
838         CLElementValue elementValue = null;
839         try {
840             int tag = in.readUnsignedByte();
841             switch (tag) {
842             case ELT_B:
843             case ELT_C:
844             case ELT_D:
845             case ELT_F:
846             case ELT_I:
847             case ELT_J:
848             case ELT_S:
849             case ELT_Z:
850             case ELT_s:
851                 elementValue = new CLElementValue((short) tag, in
852                         .readUnsignedShort());
853                 break;
854             case ELT_e:
855                 elementValue = new CLElementValue(in.readUnsignedShort(), in
856                         .readUnsignedShort());
857                 break;
858             case ELT_c:
859                 elementValue = new CLElementValue(in.readUnsignedShort());
860                 break;
861             case ELT_ANNOTATION:
862                 elementValue = new CLElementValue(readAnnotation(in));
863                 break;
864             case ELT_ARRAY:
865                 int numValues = in.readUnsignedShort();
866                 ArrayList<CLElementValue> values = new ArrayList<CLElementValue>();
867                 for (int i = 0; i < numValues; i++) {
868                     values.add(readElementValue(in));
869                 }
870                 elementValue = new CLElementValue(numValues, values);
871             }
872         } catch (IOException e) {
873             reportError("Error reading AnnotationDefault_attribute "
874                     + "from file %s", className);
875         }
876         return elementValue;
877     }
878 
879     /**
880      * Read an Annotation from the specified input stream, and return it.
881      * 
882      * @param in
883      *            input stream.
884      * @return an Annotation.
885      */
886 
887     private CLAnnotation readAnnotation(CLInputStream in) {
888         CLAnnotation annotation = null;
889         try {
890             int typeIndex = in.readUnsignedShort();
891             int numElementValuePairs = in.readUnsignedShort();
892             ArrayList<CLElementValuePair> elementValuePairs = new ArrayList<CLElementValuePair>();
893             for (int i = 0; i < numElementValuePairs; i++) {
894                 int elementNameIndex = in.readUnsignedShort();
895                 CLElementValue value = readElementValue(in);
896                 elementValuePairs.add(new CLElementValuePair(elementNameIndex,
897                         value));
898             }
899             annotation = new CLAnnotation(typeIndex, numElementValuePairs,
900                     elementValuePairs);
901         } catch (IOException e) {
902             reportError("Error reading Annotation from file %s", className);
903         }
904         return annotation;
905     }
906 
907     /**
908      * Construct a CLAbsorber object given the (fully-qualified) name of the
909      * class file to read.
910      * 
911      * @param className
912      *            fully qualified name of the input class file.
913      */
914 
915     public CLAbsorber(String className) {
916         try {
917             this.className = className;
918             CLPath classPath = new CLPath();
919             CLInputStream in = classPath.loadClass(className);
920             errorHasOccurred = false;
921             if (in == null) {
922                 reportError("Error loading %s", className);
923                 return;
924             }
925             classFile = new CLFile();
926 
927             // Read magic number (0xCAFEBABE)
928             long magic = in.readUnsignedInt();
929             if (magic != MAGIC) {
930                 reportWarning("%s has an invalid magic number", className);
931                 return;
932             }
933             classFile.magic = magic;
934 
935             // Read minor version, major version
936             classFile.minorVersion = in.readUnsignedShort();
937             classFile.majorVersion = in.readUnsignedShort();
938 
939             // Read constant pool count, constant pool
940             classFile.constantPoolCount = in.readUnsignedShort();
941             classFile.constantPool = readConstantPool(in);
942             if (errorHasOccurred()) {
943                 return;
944             }
945 
946             // Read access flags for the class
947             classFile.accessFlags = in.readUnsignedShort();
948 
949             // Read this class' constant pool index
950             classFile.thisClass = in.readUnsignedShort();
951 
952             // Read super class' constant pool index
953             classFile.superClass = in.readUnsignedShort();
954 
955             // Read interfaces (implemented by the class being
956             // read) count and interfaces (constant pool
957             // indices)
958             int interfacesCount = in.readUnsignedShort();
959             ArrayList<Integer> interfaces = new ArrayList<Integer>();
960             classFile.interfacesCount = interfacesCount;
961             for (int i = 0; i < interfacesCount; i++) {
962                 interfaces.add(in.readUnsignedShort());
963             }
964             classFile.interfaces = interfaces;
965 
966             // Read fields count and fields
967             classFile.fieldsCount = in.readUnsignedShort();
968             classFile.fields = readFields(in, classFile.fieldsCount);
969             if (errorHasOccurred()) {
970                 return;
971             }
972 
973             // Read methods count and methods
974             classFile.methodsCount = in.readUnsignedShort();
975             classFile.methods = readMethods(in, classFile.methodsCount);
976             if (errorHasOccurred()) {
977                 return;
978             }
979 
980             // Read class attributes
981             classFile.attributesCount = in.readUnsignedShort();
982             classFile.attributes = readAttributes(in, classFile.attributesCount);
983         } catch (EOFException e) {
984             reportError("Unexpected end of file %s", className);
985         } catch (IOException e) {
986             reportError("Error reading file %s", className);
987         }
988     }
989 
990     /**
991      * Return the CLFile representation of the class that was read.
992      * 
993      * @return the CLFile representation of the class.
994      */
995 
996     public CLFile classFile() {
997         return classFile;
998     }
999 
1000    /**
1001     * Return true if an error had occurred while reading the class; false
1002     * otherwise.
1003     * 
1004     * @return true or false.
1005     */
1006
1007    public boolean errorHasOccurred() {
1008        return errorHasOccurred;
1009    }
1010
1011    /**
1012     * Driver for CLAbsorber. It accepts the (fully-qualified) name of a class
1013     * file as command-line argument and dumps its (ClassFile) structure --
1014     * CLFile in our representation -- to STDOUT in a format similar to that of
1015     * javap.
1016     */
1017
1018    public static void main(String[] args) {
1019        String classFile = "";
1020        if (args.length == 1) {
1021            classFile = args[0];
1022        } else {
1023            String usage = "Usage: java jminusminus.CLAbsorber <class name>\n"
1024                    + "Where the class name must be fully qualified; "
1025                    + "eg, java/util/ArrayList";
1026            System.out.println(usage);
1027            System.exit(0);
1028        }
1029        CLAbsorber r = new CLAbsorber(classFile);
1030        if (!r.errorHasOccurred()) {
1031            CLFile c = r.classFile();
1032            c.writeToStdOut();
1033        }
1034    }
1035
1036}
1037
1038/**
1039 * Inherits from java.io.DataInputStream and provides an extra function for
1040 * reading unsigned int from the input stream, which is required for reading
1041 * Java class files.
1042 */
1043
1044class CLInputStream extends DataInputStream {
1045
1046    /**
1047     * Construct a CLInputStream object from the specified input stream.
1048     * 
1049     * @param in
1050     *            input stream.
1051     */
1052
1053    public CLInputStream(InputStream in) {
1054        super(in);
1055    }
1056
1057    /**
1058     * Read four input bytes and return a long value in the range 0 through
1059     * 4294967295. Let a, b, c, d be the four bytes. The value returned is:
1060     * 
1061     * <pre>
1062     *   ( b[ 0 ] &amp; 0xFF ) &lt;&lt; 24 ) | 
1063     *   ( ( b[ 1 ] &amp; 0xFF ) &lt;&lt; 16 ) | 
1064     *   ( ( b[ 2 ] &amp; 0xFF ) &lt;&lt; 8 ) | 
1065     *   ( b[ 3 ] &amp; 0xFF )
1066     * </pre>
1067     * 
1068     * @return the unsigned 32-bit value.
1069     * @exception EOFException
1070     *                if this stream reaches the end before reading all the
1071     *                bytes.
1072     * @exception IOException
1073     *                if an I/O error occurs.
1074     */
1075
1076    public final long readUnsignedInt() throws IOException {
1077        byte[] b = new byte[4];
1078        long mask = 0xFF, l;
1079        in.read(b);
1080        l = ((b[0] & mask) << 24) | ((b[1] & mask) << 16)
1081                | ((b[2] & mask) << 8) | (b[3] & mask);
1082        return l;
1083    }
1084
1085}
1086