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 }