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