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