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