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
137 private boolean computedNestedTypeStatus;
138
139
140
141
142
143 private transient org.apache.bcel.util.Repository repository = SyntheticRepository.getInstance();
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160 public JavaClass(final int classNameIndex, final int superclassNameIndex, final String fileName, final int major, final int minor, final int accessFlags,
161 final ConstantPool constantPool, final int[] interfaces, final Field[] fields, final Method[] methods, final Attribute[] attributes) {
162 this(classNameIndex, superclassNameIndex, fileName, major, minor, accessFlags, constantPool, interfaces, fields, methods, attributes, HEAP);
163 }
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182 public JavaClass(final int classNameIndex, final int superclassNameIndex, final String fileName, final int major, final int minor, final int accessFlags,
183 final ConstantPool constantPool, int[] interfaces, Field[] fields, Method[] methods, Attribute[] attributes, final byte source) {
184 super(accessFlags);
185 if (interfaces == null) {
186 interfaces = ArrayUtils.EMPTY_INT_ARRAY;
187 }
188 if (attributes == null) {
189 attributes = Attribute.EMPTY_ARRAY;
190 }
191 if (fields == null) {
192 fields = Field.EMPTY_FIELD_ARRAY;
193 }
194 if (methods == null) {
195 methods = Method.EMPTY_METHOD_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 computeNestedTypeStatus() {
263 if (computedNestedTypeStatus) {
264 return;
265 }
266 for (final Attribute attribute : this.attributes) {
267 if (attribute instanceof InnerClasses) {
268 ((InnerClasses) attribute).forEach(innerClass -> {
269 boolean innerClassAttributeRefersToMe = false;
270 String innerClassName = constantPool.getConstantString(innerClass.getInnerClassIndex(), Const.CONSTANT_Class);
271 innerClassName = Utility.compactClassName(innerClassName, false);
272 if (innerClassName.equals(getClassName())) {
273 innerClassAttributeRefersToMe = true;
274 }
275 if (innerClassAttributeRefersToMe) {
276 this.isNested = true;
277 if (innerClass.getInnerNameIndex() == 0) {
278 this.isAnonymous = true;
279 }
280 }
281 });
282 }
283 }
284 this.computedNestedTypeStatus = true;
285 }
286
287
288
289
290 public JavaClass copy() {
291 try {
292 final JavaClass c = (JavaClass) clone();
293 c.constantPool = constantPool.copy();
294 c.interfaces = interfaces.clone();
295 c.interfaceNames = interfaceNames.clone();
296 c.fields = new Field[fields.length];
297 Arrays.setAll(c.fields, i -> fields[i].copy(c.constantPool));
298 c.methods = new Method[methods.length];
299 Arrays.setAll(c.methods, i -> methods[i].copy(c.constantPool));
300 c.attributes = new Attribute[attributes.length];
301 Arrays.setAll(c.attributes, i -> attributes[i].copy(c.constantPool));
302 return c;
303 } catch (final CloneNotSupportedException e) {
304 return null;
305 }
306 }
307
308
309
310
311
312
313
314 public void dump(final DataOutputStream file) throws IOException {
315 file.writeInt(Const.JVM_CLASSFILE_MAGIC);
316 file.writeShort(minor);
317 file.writeShort(major);
318 constantPool.dump(file);
319 file.writeShort(super.getAccessFlags());
320 file.writeShort(classNameIndex);
321 file.writeShort(superclassNameIndex);
322 file.writeShort(interfaces.length);
323 for (final int interface1 : interfaces) {
324 file.writeShort(interface1);
325 }
326 file.writeShort(fields.length);
327 for (final Field field : fields) {
328 field.dump(file);
329 }
330 file.writeShort(methods.length);
331 for (final Method method : methods) {
332 method.dump(file);
333 }
334 if (attributes != null) {
335 file.writeShort(attributes.length);
336 for (final Attribute attribute : attributes) {
337 attribute.dump(file);
338 }
339 } else {
340 file.writeShort(0);
341 }
342 file.flush();
343 }
344
345
346
347
348
349
350
351 public void dump(final File file) throws IOException {
352 final String parent = file.getParent();
353 if (parent != null) {
354 final File dir = new File(parent);
355 if (!dir.mkdirs() && !dir.isDirectory()) {
356 throw new IOException("Could not create the directory " + dir);
357 }
358 }
359 try (DataOutputStream dos = new DataOutputStream(new FileOutputStream(file))) {
360 dump(dos);
361 }
362 }
363
364
365
366
367
368
369
370 public void dump(final OutputStream file) throws IOException {
371 dump(new DataOutputStream(file));
372 }
373
374
375
376
377
378
379
380 public void dump(final String fileName) throws IOException {
381 dump(new File(fileName));
382 }
383
384
385
386
387
388
389
390 @Override
391 public boolean equals(final Object obj) {
392 return obj instanceof JavaClass && bcelComparator.equals(this, (JavaClass) obj);
393 }
394
395
396
397
398
399
400
401
402
403 public Field findField(final String fieldName, final Type fieldType) throws ClassNotFoundException {
404 for (final Field field : fields) {
405 if (field.getName().equals(fieldName)) {
406 final Type fType = Type.getType(field.getSignature());
407
408
409
410 if (fType.equals(fieldType)) {
411 return field;
412 }
413 }
414 }
415
416 final JavaClass superclass = getSuperClass();
417 if (superclass != null && !"java.lang.Object".equals(superclass.getClassName())) {
418 final Field f = superclass.findField(fieldName, fieldType);
419 if (f != null && (f.isPublic() || f.isProtected() || !f.isPrivate() && packageName.equals(superclass.getPackageName()))) {
420 return f;
421 }
422 }
423 final JavaClass[] implementedInterfaces = getInterfaces();
424 if (implementedInterfaces != null) {
425 for (final JavaClass implementedInterface : implementedInterfaces) {
426 final Field f = implementedInterface.findField(fieldName, fieldType);
427 if (f != null) {
428 return f;
429 }
430 }
431 }
432 return null;
433 }
434
435
436
437
438
439
440 public JavaClass[] getAllInterfaces() throws ClassNotFoundException {
441 final ClassQueue queue = new ClassQueue();
442 final Set<JavaClass> allInterfaces = new TreeSet<>();
443 queue.enqueue(this);
444 while (!queue.empty()) {
445 final JavaClass clazz = queue.dequeue();
446 final JavaClass souper = clazz.getSuperClass();
447 final JavaClass[] interfaces = clazz.getInterfaces();
448 if (clazz.isInterface()) {
449 allInterfaces.add(clazz);
450 } else if (souper != null) {
451 queue.enqueue(souper);
452 }
453 for (final JavaClass iface : interfaces) {
454 queue.enqueue(iface);
455 }
456 }
457 return allInterfaces.toArray(JavaClass.EMPTY_ARRAY);
458 }
459
460
461
462
463
464 public AnnotationEntry[] getAnnotationEntries() {
465 if (annotations == null) {
466 annotations = AnnotationEntry.createAnnotationEntries(getAttributes());
467 }
468
469 return annotations;
470 }
471
472
473
474
475 public Attribute[] getAttributes() {
476 return attributes;
477 }
478
479
480
481
482 public byte[] getBytes() {
483 final ByteArrayOutputStream baos = new ByteArrayOutputStream();
484 try (DataOutputStream dos = new DataOutputStream(baos)) {
485 dump(dos);
486 } catch (final IOException e) {
487 e.printStackTrace();
488 }
489 return baos.toByteArray();
490 }
491
492
493
494
495 public String getClassName() {
496 return className;
497 }
498
499
500
501
502 public int getClassNameIndex() {
503 return classNameIndex;
504 }
505
506
507
508
509 public ConstantPool getConstantPool() {
510 return constantPool;
511 }
512
513
514
515
516
517 public Field[] getFields() {
518 return fields;
519 }
520
521
522
523
524 public String getFileName() {
525 return fileName;
526 }
527
528
529
530
531 public int[] getInterfaceIndices() {
532 return interfaces;
533 }
534
535
536
537
538 public String[] getInterfaceNames() {
539 return interfaceNames;
540 }
541
542
543
544
545
546
547 public JavaClass[] getInterfaces() throws ClassNotFoundException {
548 final String[] interfaces = getInterfaceNames();
549 final JavaClass[] classes = new JavaClass[interfaces.length];
550 for (int i = 0; i < interfaces.length; i++) {
551 classes[i] = repository.loadClass(interfaces[i]);
552 }
553 return classes;
554 }
555
556
557
558
559 public int getMajor() {
560 return major;
561 }
562
563
564
565
566 public Method getMethod(final java.lang.reflect.Method m) {
567 for (final Method method : methods) {
568 if (m.getName().equals(method.getName()) && m.getModifiers() == method.getModifiers() && Type.getSignature(m).equals(method.getSignature())) {
569 return method;
570 }
571 }
572 return null;
573 }
574
575
576
577
578 public Method[] getMethods() {
579 return methods;
580 }
581
582
583
584
585 public int getMinor() {
586 return minor;
587 }
588
589
590
591
592 public String getPackageName() {
593 return packageName;
594 }
595
596
597
598
599
600 public org.apache.bcel.util.Repository getRepository() {
601 return repository;
602 }
603
604
605
606
607 public final byte getSource() {
608 return source;
609 }
610
611
612
613
614 public String getSourceFileName() {
615 return sourceFileName;
616 }
617
618
619
620
621
622
623
624 public String getSourceFilePath() {
625 final StringBuilder outFileName = new StringBuilder();
626 if (!packageName.isEmpty()) {
627 outFileName.append(Utility.packageToPath(packageName));
628 outFileName.append('/');
629 }
630 outFileName.append(sourceFileName);
631 return outFileName.toString();
632 }
633
634
635
636
637
638 public JavaClass getSuperClass() throws ClassNotFoundException {
639 if ("java.lang.Object".equals(getClassName())) {
640 return null;
641 }
642 return repository.loadClass(getSuperclassName());
643 }
644
645
646
647
648
649 public JavaClass[] getSuperClasses() throws ClassNotFoundException {
650 JavaClass clazz = this;
651 final List<JavaClass> allSuperClasses = new ArrayList<>();
652 for (clazz = clazz.getSuperClass(); clazz != null; clazz = clazz.getSuperClass()) {
653 allSuperClasses.add(clazz);
654 }
655 return allSuperClasses.toArray(JavaClass.EMPTY_ARRAY);
656 }
657
658
659
660
661
662
663
664 public String getSuperclassName() {
665 return superclassName;
666 }
667
668
669
670
671 public int getSuperclassNameIndex() {
672 return superclassNameIndex;
673 }
674
675
676
677
678
679
680 @Override
681 public int hashCode() {
682 return bcelComparator.hashCode(this);
683 }
684
685
686
687
688
689 public boolean implementationOf(final JavaClass inter) throws ClassNotFoundException {
690 if (!inter.isInterface()) {
691 throw new IllegalArgumentException(inter.getClassName() + " is no interface");
692 }
693 if (this.equals(inter)) {
694 return true;
695 }
696 final JavaClass[] superInterfaces = getAllInterfaces();
697 for (final JavaClass superInterface : superInterfaces) {
698 if (superInterface.equals(inter)) {
699 return true;
700 }
701 }
702 return false;
703 }
704
705
706
707
708
709
710
711 public final boolean instanceOf(final JavaClass superclass) throws ClassNotFoundException {
712 if (this.equals(superclass)) {
713 return true;
714 }
715 for (final JavaClass clazz : getSuperClasses()) {
716 if (clazz.equals(superclass)) {
717 return true;
718 }
719 }
720 if (superclass.isInterface()) {
721 return implementationOf(superclass);
722 }
723 return false;
724 }
725
726
727
728
729 public final boolean isAnonymous() {
730 computeNestedTypeStatus();
731 return this.isAnonymous;
732 }
733
734 public final boolean isClass() {
735 return (super.getAccessFlags() & Const.ACC_INTERFACE) == 0;
736 }
737
738
739
740
741 public final boolean isNested() {
742 computeNestedTypeStatus();
743 return this.isNested;
744 }
745
746 public final boolean isSuper() {
747 return (super.getAccessFlags() & Const.ACC_SUPER) != 0;
748 }
749
750
751
752
753 public void setAttributes(final Attribute[] attributes) {
754 this.attributes = attributes;
755 }
756
757
758
759
760 public void setClassName(final String className) {
761 this.className = className;
762 }
763
764
765
766
767 public void setClassNameIndex(final int classNameIndex) {
768 this.classNameIndex = classNameIndex;
769 }
770
771
772
773
774 public void setConstantPool(final ConstantPool constantPool) {
775 this.constantPool = constantPool;
776 }
777
778
779
780
781 public void setFields(final Field[] fields) {
782 this.fields = fields;
783 }
784
785
786
787
788 public void setFileName(final String fileName) {
789 this.fileName = fileName;
790 }
791
792
793
794
795 public void setInterfaceNames(final String[] interfaceNames) {
796 this.interfaceNames = interfaceNames;
797 }
798
799
800
801
802 public void setInterfaces(final int[] interfaces) {
803 this.interfaces = interfaces;
804 }
805
806
807
808
809 public void setMajor(final int major) {
810 this.major = major;
811 }
812
813
814
815
816 public void setMethods(final Method[] methods) {
817 this.methods = methods;
818 }
819
820
821
822
823 public void setMinor(final int minor) {
824 this.minor = minor;
825 }
826
827
828
829
830 public void setRepository(final org.apache.bcel.util.Repository repository) {
831 this.repository = repository;
832 }
833
834
835
836
837 public void setSourceFileName(final String sourceFileName) {
838 this.sourceFileName = sourceFileName;
839 }
840
841
842
843
844 public void setSuperclassName(final String superclassName) {
845 this.superclassName = superclassName;
846 }
847
848
849
850
851 public void setSuperclassNameIndex(final int superclassNameIndex) {
852 this.superclassNameIndex = superclassNameIndex;
853 }
854
855
856
857
858 @Override
859 public String toString() {
860 String access = Utility.accessToString(super.getAccessFlags(), true);
861 access = access.isEmpty() ? "" : access + " ";
862 final StringBuilder buf = new StringBuilder(128);
863 buf.append(access).append(Utility.classOrInterface(super.getAccessFlags())).append(" ").append(className).append(" extends ")
864 .append(Utility.compactClassName(superclassName, false)).append('\n');
865 final int size = interfaces.length;
866 if (size > 0) {
867 buf.append("implements\t\t");
868 for (int i = 0; i < size; i++) {
869 buf.append(interfaceNames[i]);
870 if (i < size - 1) {
871 buf.append(", ");
872 }
873 }
874 buf.append('\n');
875 }
876 buf.append("file name\t\t").append(fileName).append('\n');
877 buf.append("compiled from\t\t").append(sourceFileName).append('\n');
878 buf.append("compiler version\t").append(major).append(".").append(minor).append('\n');
879 buf.append("access flags\t\t").append(super.getAccessFlags()).append('\n');
880 buf.append("constant pool\t\t").append(constantPool.getLength()).append(" entries\n");
881 buf.append("ACC_SUPER flag\t\t").append(isSuper()).append("\n");
882 if (attributes.length > 0) {
883 buf.append("\nAttribute(s):\n");
884 for (final Attribute attribute : attributes) {
885 buf.append(indent(attribute));
886 }
887 }
888 final AnnotationEntry[] annotations = getAnnotationEntries();
889 if (annotations != null && annotations.length > 0) {
890 buf.append("\nAnnotation(s):\n");
891 for (final AnnotationEntry annotation : annotations) {
892 buf.append(indent(annotation));
893 }
894 }
895 if (fields.length > 0) {
896 buf.append("\n").append(fields.length).append(" fields:\n");
897 for (final Field field : fields) {
898 buf.append("\t").append(field).append('\n');
899 }
900 }
901 if (methods.length > 0) {
902 buf.append("\n").append(methods.length).append(" methods:\n");
903 for (final Method method : methods) {
904 buf.append("\t").append(method).append('\n');
905 }
906 }
907 return buf.toString();
908 }
909 }