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(JavaClass.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 public Attribute[] getAttributes() {
489 return attributes;
490 }
491
492
493
494
495 public byte[] getBytes() {
496 final ByteArrayOutputStream baos = new ByteArrayOutputStream();
497 try (DataOutputStream dos = new DataOutputStream(baos)) {
498 dump(dos);
499 } catch (final IOException e) {
500 e.printStackTrace();
501 }
502 return baos.toByteArray();
503 }
504
505
506
507
508 public String getClassName() {
509 return className;
510 }
511
512
513
514
515 public int getClassNameIndex() {
516 return classNameIndex;
517 }
518
519
520
521
522 public ConstantPool getConstantPool() {
523 return constantPool;
524 }
525
526
527
528
529
530 public Field[] getFields() {
531 return fields;
532 }
533
534
535
536
537 public String getFileName() {
538 return fileName;
539 }
540
541
542
543
544 public int[] getInterfaceIndices() {
545 return interfaces;
546 }
547
548
549
550
551 public String[] getInterfaceNames() {
552 return interfaceNames;
553 }
554
555
556
557
558
559
560 public JavaClass[] getInterfaces() throws ClassNotFoundException {
561 final String[] interfaces = getInterfaceNames();
562 final JavaClass[] classes = new JavaClass[interfaces.length];
563 for (int i = 0; i < interfaces.length; i++) {
564 classes[i] = repository.loadClass(interfaces[i]);
565 }
566 return classes;
567 }
568
569
570
571
572 public int getMajor() {
573 return major;
574 }
575
576
577
578
579 public Method getMethod(final java.lang.reflect.Method m) {
580 for (final Method method : methods) {
581 if (m.getName().equals(method.getName()) && m.getModifiers() == method.getModifiers() && Type.getSignature(m).equals(method.getSignature())) {
582 return method;
583 }
584 }
585 return null;
586 }
587
588
589
590
591 public Method[] getMethods() {
592 return methods;
593 }
594
595
596
597
598 public int getMinor() {
599 return minor;
600 }
601
602
603
604
605 public String getPackageName() {
606 return packageName;
607 }
608
609
610
611
612
613 public org.apache.bcel.util.Repository getRepository() {
614 return repository;
615 }
616
617
618
619
620 public final byte getSource() {
621 return source;
622 }
623
624
625
626
627 public String getSourceFileName() {
628 return sourceFileName;
629 }
630
631
632
633
634
635
636
637 public String getSourceFilePath() {
638 final StringBuilder outFileName = new StringBuilder();
639 if (!packageName.isEmpty()) {
640 outFileName.append(Utility.packageToPath(packageName));
641 outFileName.append('/');
642 }
643 outFileName.append(sourceFileName);
644 return outFileName.toString();
645 }
646
647
648
649
650
651 public JavaClass getSuperClass() throws ClassNotFoundException {
652 if ("java.lang.Object".equals(getClassName())) {
653 return null;
654 }
655 return repository.loadClass(getSuperclassName());
656 }
657
658
659
660
661
662 public JavaClass[] getSuperClasses() throws ClassNotFoundException {
663 JavaClass clazz = this;
664 final List<JavaClass> allSuperClasses = new ArrayList<>();
665 for (clazz = clazz.getSuperClass(); clazz != null; clazz = clazz.getSuperClass()) {
666 allSuperClasses.add(clazz);
667 }
668 return allSuperClasses.toArray(JavaClass.EMPTY_ARRAY);
669 }
670
671
672
673
674
675
676
677 public String getSuperclassName() {
678 return superclassName;
679 }
680
681
682
683
684 public int getSuperclassNameIndex() {
685 return superclassNameIndex;
686 }
687
688
689
690
691
692
693 @Override
694 public int hashCode() {
695 return bcelComparator.hashCode(this);
696 }
697
698
699
700
701
702 public boolean implementationOf(final JavaClass inter) throws ClassNotFoundException {
703 if (!inter.isInterface()) {
704 throw new IllegalArgumentException(inter.getClassName() + " is no interface");
705 }
706 if (this.equals(inter)) {
707 return true;
708 }
709 final JavaClass[] superInterfaces = getAllInterfaces();
710 for (final JavaClass superInterface : superInterfaces) {
711 if (superInterface.equals(inter)) {
712 return true;
713 }
714 }
715 return false;
716 }
717
718
719
720
721
722
723
724 public final boolean instanceOf(final JavaClass superclass) throws ClassNotFoundException {
725 if (this.equals(superclass)) {
726 return true;
727 }
728 for (final JavaClass clazz : getSuperClasses()) {
729 if (clazz.equals(superclass)) {
730 return true;
731 }
732 }
733 if (superclass.isInterface()) {
734 return implementationOf(superclass);
735 }
736 return false;
737 }
738
739
740
741
742 public final boolean isAnonymous() {
743 computeNestedTypeStatus();
744 return this.isAnonymous;
745 }
746
747 public final boolean isClass() {
748 return (super.getAccessFlags() & Const.ACC_INTERFACE) == 0;
749 }
750
751
752
753
754 public final boolean isNested() {
755 computeNestedTypeStatus();
756 return this.isNested;
757 }
758
759
760
761
762
763
764
765 public boolean isRecord() {
766 computeIsRecord();
767 return this.isRecord;
768 }
769
770 public final boolean isSuper() {
771 return (super.getAccessFlags() & Const.ACC_SUPER) != 0;
772 }
773
774
775
776
777 public void setAttributes(final Attribute[] attributes) {
778 this.attributes = attributes != null ? attributes : Attribute.EMPTY_ARRAY;
779 }
780
781
782
783
784 public void setClassName(final String className) {
785 this.className = className;
786 }
787
788
789
790
791 public void setClassNameIndex(final int classNameIndex) {
792 this.classNameIndex = classNameIndex;
793 }
794
795
796
797
798 public void setConstantPool(final ConstantPool constantPool) {
799 this.constantPool = constantPool;
800 }
801
802
803
804
805 public void setFields(final Field[] fields) {
806 this.fields = fields != null ? fields : Field.EMPTY_ARRAY;
807 }
808
809
810
811
812 public void setFileName(final String fileName) {
813 this.fileName = fileName;
814 }
815
816
817
818
819 public void setInterfaceNames(final String[] interfaceNames) {
820 this.interfaceNames = ArrayUtils.nullToEmpty(interfaceNames);
821 }
822
823
824
825
826 public void setInterfaces(final int[] interfaces) {
827 this.interfaces = ArrayUtils.nullToEmpty(interfaces);
828 }
829
830
831
832
833 public void setMajor(final int major) {
834 this.major = major;
835 }
836
837
838
839
840 public void setMethods(final Method[] methods) {
841 this.methods = methods != null ? methods : Method.EMPTY_ARRAY;
842 }
843
844
845
846
847 public void setMinor(final int minor) {
848 this.minor = minor;
849 }
850
851
852
853
854 public void setRepository(final org.apache.bcel.util.Repository repository) {
855 this.repository = repository;
856 }
857
858
859
860
861 public void setSourceFileName(final String sourceFileName) {
862 this.sourceFileName = sourceFileName;
863 }
864
865
866
867
868 public void setSuperclassName(final String superclassName) {
869 this.superclassName = superclassName;
870 }
871
872
873
874
875 public void setSuperclassNameIndex(final int superclassNameIndex) {
876 this.superclassNameIndex = superclassNameIndex;
877 }
878
879
880
881
882 @Override
883 public String toString() {
884 String access = Utility.accessToString(super.getAccessFlags(), true);
885 access = access.isEmpty() ? "" : access + " ";
886 final StringBuilder buf = new StringBuilder(128);
887 buf.append(access).append(Utility.classOrInterface(super.getAccessFlags())).append(" ").append(className).append(" extends ")
888 .append(Utility.compactClassName(superclassName, false)).append('\n');
889 final int size = interfaces.length;
890 if (size > 0) {
891 buf.append("implements\t\t");
892 for (int i = 0; i < size; i++) {
893 buf.append(interfaceNames[i]);
894 if (i < size - 1) {
895 buf.append(", ");
896 }
897 }
898 buf.append('\n');
899 }
900 buf.append("file name\t\t").append(fileName).append('\n');
901 buf.append("compiled from\t\t").append(sourceFileName).append('\n');
902 buf.append("compiler version\t").append(major).append(".").append(minor).append('\n');
903 buf.append("access flags\t\t").append(super.getAccessFlags()).append('\n');
904 buf.append("constant pool\t\t").append(constantPool.getLength()).append(" entries\n");
905 buf.append("ACC_SUPER flag\t\t").append(isSuper()).append("\n");
906 if (attributes.length > 0) {
907 buf.append("\nAttribute(s):\n");
908 for (final Attribute attribute : attributes) {
909 buf.append(indent(attribute));
910 }
911 }
912 final AnnotationEntry[] annotations = getAnnotationEntries();
913 if (annotations != null && annotations.length > 0) {
914 buf.append("\nAnnotation(s):\n");
915 for (final AnnotationEntry annotation : annotations) {
916 buf.append(indent(annotation));
917 }
918 }
919 if (fields.length > 0) {
920 buf.append("\n").append(fields.length).append(" fields:\n");
921 for (final Field field : fields) {
922 buf.append("\t").append(field).append('\n');
923 }
924 }
925 if (methods.length > 0) {
926 buf.append("\n").append(methods.length).append(" methods:\n");
927 for (final Method method : methods) {
928 buf.append("\t").append(method).append('\n');
929 }
930 }
931 return buf.toString();
932 }
933 }