1   
2   
3   
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  package org.apache.bcel.classfile;
20  
21  import java.io.ByteArrayOutputStream;
22  import java.io.DataOutputStream;
23  import java.io.File;
24  import java.io.FileOutputStream;
25  import java.io.IOException;
26  import java.io.OutputStream;
27  import java.util.ArrayList;
28  import java.util.Arrays;
29  import java.util.List;
30  import java.util.Objects;
31  import java.util.Set;
32  import java.util.StringTokenizer;
33  import java.util.TreeSet;
34  
35  import org.apache.bcel.Const;
36  import org.apache.bcel.generic.Type;
37  import org.apache.bcel.util.BCELComparator;
38  import org.apache.bcel.util.ClassQueue;
39  import org.apache.bcel.util.SyntheticRepository;
40  import org.apache.commons.lang3.ArrayUtils;
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  public class JavaClass extends AccessFlags implements Cloneable, Node, Comparable<JavaClass> {
51  
52      
53  
54  
55  
56  
57      public static final String EXTENSION = ".class";
58  
59      
60  
61  
62  
63  
64      public static final JavaClass[] EMPTY_ARRAY = {};
65  
66      public static final byte HEAP = 1;
67      public static final byte FILE = 2;
68      public static final byte ZIP = 3;
69      private static final boolean debug = Boolean.getBoolean("JavaClass.debug"); 
70  
71      private static BCELComparator<JavaClass> bcelComparator = new BCELComparator<JavaClass>() {
72  
73          @Override
74          public boolean equals(final JavaClass a, final JavaClass b) {
75              return a == b || a != null && b != null && Objects.equals(a.getClassName(), b.getClassName());
76          }
77  
78          @Override
79          public int hashCode(final JavaClass o) {
80              return o != null ? Objects.hashCode(o.getClassName()) : 0;
81          }
82      };
83  
84      
85  
86  
87      static void Debug(final String str) {
88          if (debug) {
89              System.out.println(str);
90          }
91      }
92  
93      
94  
95  
96      public static BCELComparator<JavaClass> getComparator() {
97          return bcelComparator;
98      }
99  
100     private static String indent(final Object obj) {
101         final StringTokenizer tokenizer = new StringTokenizer(obj.toString(), "\n");
102         final StringBuilder buf = new StringBuilder();
103         while (tokenizer.hasMoreTokens()) {
104             buf.append("\t").append(tokenizer.nextToken()).append("\n");
105         }
106         return buf.toString();
107     }
108 
109     
110 
111 
112     public static void setComparator(final BCELComparator<JavaClass> comparator) {
113         bcelComparator = comparator;
114     }
115 
116     private String fileName;
117     private final String packageName;
118     private String sourceFileName = "<Unknown>";
119     private int classNameIndex;
120     private int superclassNameIndex;
121     private String className;
122     private String superclassName;
123     private int major;
124     private int minor; 
125     private ConstantPool constantPool; 
126     private int[] interfaces; 
127     private String[] interfaceNames;
128     private Field[] fields; 
129     private Method[] methods; 
130     private Attribute[] attributes; 
131 
132     private AnnotationEntry[] annotations; 
133     private byte source = HEAP; 
134 
135     private boolean isAnonymous;
136 
137     private boolean isNested;
138     private boolean isRecord;
139 
140     private boolean computedNestedTypeStatus;
141     private boolean computedRecord;
142 
143     
144 
145 
146 
147     private transient org.apache.bcel.util.Repository repository = SyntheticRepository.getInstance();
148 
149     
150 
151 
152 
153 
154 
155 
156 
157 
158 
159 
160 
161 
162 
163 
164     public JavaClass(final int classNameIndex, final int superclassNameIndex, final String fileName, final int major, final int minor, final int accessFlags,
165         final ConstantPool constantPool, final int[] interfaces, final Field[] fields, final Method[] methods, final Attribute[] attributes) {
166         this(classNameIndex, superclassNameIndex, fileName, major, minor, accessFlags, constantPool, interfaces, fields, methods, attributes, HEAP);
167     }
168 
169     
170 
171 
172 
173 
174 
175 
176 
177 
178 
179 
180 
181 
182 
183 
184 
185 
186     public JavaClass(final int classNameIndex, final int superclassNameIndex, final String fileName, final int major, final int minor, final int accessFlags,
187         final ConstantPool constantPool, int[] interfaces, Field[] fields, Method[] methods, Attribute[] attributes, final byte source) {
188         super(accessFlags);
189         interfaces = ArrayUtils.nullToEmpty(interfaces);
190         if (attributes == null) {
191             attributes = Attribute.EMPTY_ARRAY;
192         }
193         if (fields == null) {
194             fields = Field.EMPTY_ARRAY;
195         }
196         if (methods == null) {
197             methods = Method.EMPTY_ARRAY;
198         }
199         this.classNameIndex = classNameIndex;
200         this.superclassNameIndex = superclassNameIndex;
201         this.fileName = fileName;
202         this.major = major;
203         this.minor = minor;
204         this.constantPool = constantPool;
205         this.interfaces = interfaces;
206         this.fields = fields;
207         this.methods = methods;
208         this.attributes = attributes;
209         this.source = source;
210         
211         for (final Attribute attribute : attributes) {
212             if (attribute instanceof SourceFile) {
213                 sourceFileName = ((SourceFile) attribute).getSourceFileName();
214                 break;
215             }
216         }
217         
218 
219 
220 
221         className = constantPool.getConstantString(classNameIndex, Const.CONSTANT_Class);
222         className = Utility.compactClassName(className, false);
223         final int index = className.lastIndexOf('.');
224         if (index < 0) {
225             packageName = "";
226         } else {
227             packageName = className.substring(0, index);
228         }
229         if (superclassNameIndex > 0) {
230             
231             superclassName = constantPool.getConstantString(superclassNameIndex, Const.CONSTANT_Class);
232             superclassName = Utility.compactClassName(superclassName, false);
233         } else {
234             superclassName = "java.lang.Object";
235         }
236         interfaceNames = new String[interfaces.length];
237         for (int i = 0; i < interfaces.length; i++) {
238             final String str = constantPool.getConstantString(interfaces[i], Const.CONSTANT_Class);
239             interfaceNames[i] = Utility.compactClassName(str, false);
240         }
241     }
242 
243     
244 
245 
246 
247 
248 
249     @Override
250     public void accept(final Visitor v) {
251         v.visitJavaClass(this);
252     }
253 
254     
255 
256 
257 
258 
259     @Override
260     public int compareTo(final JavaClass obj) {
261         return getClassName().compareTo(obj.getClassName());
262     }
263 
264     private void computeIsRecord() {
265         if (computedRecord) {
266             return;
267         }
268         for (final Attribute attribute : this.attributes) {
269             if (attribute instanceof Record) {
270                 isRecord = true;
271                 break;
272             }
273         }
274         this.computedRecord = true;
275     }
276 
277     private void computeNestedTypeStatus() {
278         if (computedNestedTypeStatus) {
279             return;
280         }
281         for (final Attribute attribute : this.attributes) {
282             if (attribute instanceof InnerClasses) {
283                 ((InnerClasses) attribute).forEach(innerClass ->  {
284                     boolean innerClassAttributeRefersToMe = false;
285                     String innerClassName = constantPool.getConstantString(innerClass.getInnerClassIndex(), Const.CONSTANT_Class);
286                     innerClassName = Utility.compactClassName(innerClassName, false);
287                     if (innerClassName.equals(getClassName())) {
288                         innerClassAttributeRefersToMe = true;
289                     }
290                     if (innerClassAttributeRefersToMe) {
291                         this.isNested = true;
292                         if (innerClass.getInnerNameIndex() == 0) {
293                             this.isAnonymous = true;
294                         }
295                     }
296                 });
297             }
298         }
299         this.computedNestedTypeStatus = true;
300     }
301 
302     
303 
304 
305     public JavaClass copy() {
306         try {
307             final JavaClass c = (JavaClass) clone();
308             c.constantPool = constantPool.copy();
309             c.interfaces = interfaces.clone();
310             c.interfaceNames = interfaceNames.clone();
311             c.fields = new Field[fields.length];
312             Arrays.setAll(c.fields, i -> fields[i].copy(c.constantPool));
313             c.methods = new Method[methods.length];
314             Arrays.setAll(c.methods, i -> methods[i].copy(c.constantPool));
315             c.attributes = new Attribute[attributes.length];
316             Arrays.setAll(c.attributes, i -> attributes[i].copy(c.constantPool));
317             return c;
318         } catch (final CloneNotSupportedException e) {
319             return null;
320         }
321     }
322 
323     
324 
325 
326 
327 
328 
329     public void dump(final DataOutputStream file) throws IOException {
330         file.writeInt(Const.JVM_CLASSFILE_MAGIC);
331         file.writeShort(minor);
332         file.writeShort(major);
333         constantPool.dump(file);
334         file.writeShort(super.getAccessFlags());
335         file.writeShort(classNameIndex);
336         file.writeShort(superclassNameIndex);
337         file.writeShort(interfaces.length);
338         for (final int interface1 : interfaces) {
339             file.writeShort(interface1);
340         }
341         file.writeShort(fields.length);
342         for (final Field field : fields) {
343             field.dump(file);
344         }
345         file.writeShort(methods.length);
346         for (final Method method : methods) {
347             method.dump(file);
348         }
349         if (attributes != null) {
350             file.writeShort(attributes.length);
351             for (final Attribute attribute : attributes) {
352                 attribute.dump(file);
353             }
354         } else {
355             file.writeShort(0);
356         }
357         file.flush();
358     }
359 
360     
361 
362 
363 
364 
365 
366     public void dump(final File file) throws IOException {
367         final String parent = file.getParent();
368         if (parent != null) {
369             final File dir = new File(parent);
370             if (!dir.mkdirs() && !dir.isDirectory()) {
371                 throw new IOException("Could not create the directory " + dir);
372             }
373         }
374         try (DataOutputStream dos = new DataOutputStream(new FileOutputStream(file))) {
375             dump(dos);
376         }
377     }
378 
379     
380 
381 
382 
383 
384 
385     public void dump(final OutputStream file) throws IOException {
386         dump(new DataOutputStream(file));
387     }
388 
389     
390 
391 
392 
393 
394 
395     public void dump(final String fileName) throws IOException {
396         dump(new File(fileName));
397     }
398 
399     
400 
401 
402 
403 
404 
405     @Override
406     public boolean equals(final Object obj) {
407         return obj instanceof JavaClass && bcelComparator.equals(this, (JavaClass) obj);
408     }
409 
410     
411 
412 
413 
414 
415 
416 
417 
418     public Field findField(final String fieldName, final Type fieldType) throws ClassNotFoundException {
419         for (final Field field : fields) {
420             if (field.getName().equals(fieldName)) {
421                 final Type fType = Type.getType(field.getSignature());
422                 
423 
424 
425                 if (fType.equals(fieldType)) {
426                     return field;
427                 }
428             }
429         }
430 
431         final JavaClass superclass = getSuperClass();
432         if (superclass != null && !"java.lang.Object".equals(superclass.getClassName())) {
433             final Field f = superclass.findField(fieldName, fieldType);
434             if (f != null && (f.isPublic() || f.isProtected() || !f.isPrivate() && packageName.equals(superclass.getPackageName()))) {
435                 return f;
436             }
437         }
438         final JavaClass[] implementedInterfaces = getInterfaces();
439         if (implementedInterfaces != null) {
440             for (final JavaClass implementedInterface : implementedInterfaces) {
441                 final Field f = implementedInterface.findField(fieldName, fieldType);
442                 if (f != null) {
443                     return f;
444                 }
445             }
446         }
447         return null;
448     }
449 
450     
451 
452 
453 
454 
455     public JavaClass[] getAllInterfaces() throws ClassNotFoundException {
456         final ClassQueue queue = new ClassQueue();
457         final Set<JavaClass> allInterfaces = new TreeSet<>();
458         queue.enqueue(this);
459         while (!queue.empty()) {
460             final JavaClass clazz = queue.dequeue();
461             final JavaClass souper = clazz.getSuperClass();
462             final JavaClass[] interfaces = clazz.getInterfaces();
463             if (clazz.isInterface()) {
464                 allInterfaces.add(clazz);
465             } else if (souper != null) {
466                 queue.enqueue(souper);
467             }
468             for (final JavaClass iface : interfaces) {
469                 queue.enqueue(iface);
470             }
471         }
472         return allInterfaces.toArray(EMPTY_ARRAY);
473     }
474 
475     
476 
477 
478 
479     public AnnotationEntry[] getAnnotationEntries() {
480         if (annotations == null) {
481             annotations = AnnotationEntry.createAnnotationEntries(getAttributes());
482         }
483 
484         return annotations;
485     }
486 
487     
488 
489 
490 
491 
492 
493     @SuppressWarnings("unchecked")
494     public final <T extends Attribute> T getAttribute(final byte tag) {
495         for (final Attribute attribute : getAttributes()) {
496             if (attribute.getTag() == tag) {
497                 return (T) attribute;
498             }
499         }
500         return null;
501     }
502 
503     
504 
505 
506     public Attribute[] getAttributes() {
507         return attributes;
508     }
509 
510     
511 
512 
513     public byte[] getBytes() {
514         final ByteArrayOutputStream baos = new ByteArrayOutputStream();
515         try (DataOutputStream dos = new DataOutputStream(baos)) {
516             dump(dos);
517         } catch (final IOException e) {
518             e.printStackTrace();
519         }
520         return baos.toByteArray();
521     }
522 
523     
524 
525 
526     public String getClassName() {
527         return className;
528     }
529 
530     
531 
532 
533     public int getClassNameIndex() {
534         return classNameIndex;
535     }
536 
537     
538 
539 
540     public ConstantPool getConstantPool() {
541         return constantPool;
542     }
543 
544     
545 
546 
547 
548     public Field[] getFields() {
549         return fields;
550     }
551 
552     
553 
554 
555     public String getFileName() {
556         return fileName;
557     }
558 
559     
560 
561 
562     public int[] getInterfaceIndices() {
563         return interfaces;
564     }
565 
566     
567 
568 
569     public String[] getInterfaceNames() {
570         return interfaceNames;
571     }
572 
573     
574 
575 
576 
577 
578     public JavaClass[] getInterfaces() throws ClassNotFoundException {
579         final String[] interfaces = getInterfaceNames();
580         final JavaClass[] classes = new JavaClass[interfaces.length];
581         for (int i = 0; i < interfaces.length; i++) {
582             classes[i] = repository.loadClass(interfaces[i]);
583         }
584         return classes;
585     }
586 
587     
588 
589 
590     public int getMajor() {
591         return major;
592     }
593 
594     
595 
596 
597     public Method getMethod(final java.lang.reflect.Method m) {
598         for (final Method method : methods) {
599             if (m.getName().equals(method.getName()) && m.getModifiers() == method.getModifiers() && Type.getSignature(m).equals(method.getSignature())) {
600                 return method;
601             }
602         }
603         return null;
604     }
605 
606     
607 
608 
609     public Method[] getMethods() {
610         return methods;
611     }
612 
613     
614 
615 
616     public int getMinor() {
617         return minor;
618     }
619 
620     
621 
622 
623     public String getPackageName() {
624         return packageName;
625     }
626 
627     
628 
629 
630 
631     public org.apache.bcel.util.Repository getRepository() {
632         return repository;
633     }
634 
635     
636 
637 
638     public final byte getSource() {
639         return source;
640     }
641 
642     
643 
644 
645     public String getSourceFileName() {
646         return sourceFileName;
647     }
648 
649     
650 
651 
652 
653 
654 
655     public String getSourceFilePath() {
656         final StringBuilder outFileName = new StringBuilder();
657         if (!packageName.isEmpty()) {
658             outFileName.append(Utility.packageToPath(packageName));
659             outFileName.append('/');
660         }
661         outFileName.append(sourceFileName);
662         return outFileName.toString();
663     }
664 
665     
666 
667 
668 
669     public JavaClass getSuperClass() throws ClassNotFoundException {
670         if ("java.lang.Object".equals(getClassName())) {
671             return null;
672         }
673         return repository.loadClass(getSuperclassName());
674     }
675 
676     
677 
678 
679 
680     public JavaClass[] getSuperClasses() throws ClassNotFoundException {
681         JavaClass clazz = this;
682         final List<JavaClass> allSuperClasses = new ArrayList<>();
683         for (clazz = clazz.getSuperClass(); clazz != null; clazz = clazz.getSuperClass()) {
684             allSuperClasses.add(clazz);
685         }
686         return allSuperClasses.toArray(EMPTY_ARRAY);
687     }
688 
689     
690 
691 
692 
693 
694 
695     public String getSuperclassName() {
696         return superclassName;
697     }
698 
699     
700 
701 
702     public int getSuperclassNameIndex() {
703         return superclassNameIndex;
704     }
705 
706     
707 
708 
709 
710 
711     @Override
712     public int hashCode() {
713         return bcelComparator.hashCode(this);
714     }
715 
716     
717 
718 
719 
720     public boolean implementationOf(final JavaClass inter) throws ClassNotFoundException {
721         if (!inter.isInterface()) {
722             throw new IllegalArgumentException(inter.getClassName() + " is no interface");
723         }
724         if (equals(inter)) {
725             return true;
726         }
727         final JavaClass[] superInterfaces = getAllInterfaces();
728         for (final JavaClass superInterface : superInterfaces) {
729             if (superInterface.equals(inter)) {
730                 return true;
731             }
732         }
733         return false;
734     }
735 
736     
737 
738 
739 
740 
741 
742     public final boolean instanceOf(final JavaClass superclass) throws ClassNotFoundException {
743         if (equals(superclass)) {
744             return true;
745         }
746         for (final JavaClass clazz : getSuperClasses()) {
747             if (clazz.equals(superclass)) {
748                 return true;
749             }
750         }
751         if (superclass.isInterface()) {
752             return implementationOf(superclass);
753         }
754         return false;
755     }
756 
757     
758 
759 
760     public final boolean isAnonymous() {
761         computeNestedTypeStatus();
762         return this.isAnonymous;
763     }
764 
765     public final boolean isClass() {
766         return (super.getAccessFlags() & Const.ACC_INTERFACE) == 0;
767     }
768 
769     
770 
771 
772     public final boolean isNested() {
773         computeNestedTypeStatus();
774         return this.isNested;
775     }
776 
777     
778 
779 
780 
781 
782 
783     public boolean isRecord() {
784         computeIsRecord();
785         return this.isRecord;
786     }
787 
788     public final boolean isSuper() {
789         return (super.getAccessFlags() & Const.ACC_SUPER) != 0;
790     }
791 
792     
793 
794 
795     public void setAttributes(final Attribute[] attributes) {
796         this.attributes = attributes != null ? attributes : Attribute.EMPTY_ARRAY;
797     }
798 
799     
800 
801 
802     public void setClassName(final String className) {
803         this.className = className;
804     }
805 
806     
807 
808 
809     public void setClassNameIndex(final int classNameIndex) {
810         this.classNameIndex = classNameIndex;
811     }
812 
813     
814 
815 
816     public void setConstantPool(final ConstantPool constantPool) {
817         this.constantPool = constantPool;
818     }
819 
820     
821 
822 
823     public void setFields(final Field[] fields) {
824         this.fields = fields != null ? fields : Field.EMPTY_ARRAY;
825     }
826 
827     
828 
829 
830     public void setFileName(final String fileName) {
831         this.fileName = fileName;
832     }
833 
834     
835 
836 
837     public void setInterfaceNames(final String[] interfaceNames) {
838         this.interfaceNames = ArrayUtils.nullToEmpty(interfaceNames);
839     }
840 
841     
842 
843 
844     public void setInterfaces(final int[] interfaces) {
845         this.interfaces = ArrayUtils.nullToEmpty(interfaces);
846     }
847 
848     
849 
850 
851     public void setMajor(final int major) {
852         this.major = major;
853     }
854 
855     
856 
857 
858     public void setMethods(final Method[] methods) {
859         this.methods = methods != null ? methods : Method.EMPTY_ARRAY;
860     }
861 
862     
863 
864 
865     public void setMinor(final int minor) {
866         this.minor = minor;
867     }
868 
869     
870 
871 
872     public void setRepository(final org.apache.bcel.util.Repository repository) { 
873         this.repository = repository;
874     }
875 
876     
877 
878 
879     public void setSourceFileName(final String sourceFileName) {
880         this.sourceFileName = sourceFileName;
881     }
882 
883     
884 
885 
886     public void setSuperclassName(final String superclassName) {
887         this.superclassName = superclassName;
888     }
889 
890     
891 
892 
893     public void setSuperclassNameIndex(final int superclassNameIndex) {
894         this.superclassNameIndex = superclassNameIndex;
895     }
896 
897     
898 
899 
900     @Override
901     public String toString() {
902         String access = Utility.accessToString(super.getAccessFlags(), true);
903         access = access.isEmpty() ? "" : access + " ";
904         final StringBuilder buf = new StringBuilder(128);
905         buf.append(access).append(Utility.classOrInterface(super.getAccessFlags())).append(" ").append(className).append(" extends ")
906             .append(Utility.compactClassName(superclassName, false)).append('\n');
907         final int size = interfaces.length;
908         if (size > 0) {
909             buf.append("implements\t\t");
910             for (int i = 0; i < size; i++) {
911                 buf.append(interfaceNames[i]);
912                 if (i < size - 1) {
913                     buf.append(", ");
914                 }
915             }
916             buf.append('\n');
917         }
918         buf.append("file name\t\t").append(fileName).append('\n');
919         buf.append("compiled from\t\t").append(sourceFileName).append('\n');
920         buf.append("compiler version\t").append(major).append(".").append(minor).append('\n');
921         buf.append("access flags\t\t").append(super.getAccessFlags()).append('\n');
922         buf.append("constant pool\t\t").append(constantPool.getLength()).append(" entries\n");
923         buf.append("ACC_SUPER flag\t\t").append(isSuper()).append("\n");
924         if (attributes.length > 0) {
925             buf.append("\nAttribute(s):\n");
926             for (final Attribute attribute : attributes) {
927                 buf.append(indent(attribute));
928             }
929         }
930         final AnnotationEntry[] annotations = getAnnotationEntries();
931         if (annotations != null && annotations.length > 0) {
932             buf.append("\nAnnotation(s):\n");
933             for (final AnnotationEntry annotation : annotations) {
934                 buf.append(indent(annotation));
935             }
936         }
937         if (fields.length > 0) {
938             buf.append("\n").append(fields.length).append(" fields:\n");
939             for (final Field field : fields) {
940                 buf.append("\t").append(field).append('\n');
941             }
942         }
943         if (methods.length > 0) {
944             buf.append("\n").append(methods.length).append(" methods:\n");
945             for (final Method method : methods) {
946                 buf.append("\t").append(method).append('\n');
947             }
948         }
949         return buf.toString();
950     }
951 }