|
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
package com.sun.org.apache.bcel.internal.classfile; |
|
|
|
import java.io.ByteArrayOutputStream; |
|
import java.io.DataOutputStream; |
|
import java.io.File; |
|
import java.io.FileOutputStream; |
|
import java.io.IOException; |
|
import java.io.OutputStream; |
|
import java.util.ArrayList; |
|
import java.util.List; |
|
import java.util.Set; |
|
import java.util.StringTokenizer; |
|
import java.util.TreeSet; |
|
|
|
import com.sun.org.apache.bcel.internal.Const; |
|
import com.sun.org.apache.bcel.internal.generic.Type; |
|
import com.sun.org.apache.bcel.internal.util.BCELComparator; |
|
import com.sun.org.apache.bcel.internal.util.ClassQueue; |
|
import com.sun.org.apache.bcel.internal.util.SyntheticRepository; |
|
import jdk.xml.internal.SecuritySupport; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public class JavaClass extends AccessFlags implements Cloneable, Node, Comparable<JavaClass> { |
|
|
|
private String file_name; |
|
private String package_name; |
|
private String source_file_name = "<Unknown>"; |
|
private int class_name_index; |
|
private int superclass_name_index; |
|
private String class_name; |
|
private String superclass_name; |
|
private int major; |
|
private int minor; |
|
private ConstantPool constant_pool; |
|
private int[] interfaces; |
|
private String[] interface_names; |
|
private Field[] fields; |
|
private Method[] methods; |
|
private Attribute[] attributes; |
|
private AnnotationEntry[] annotations; |
|
private byte source = HEAP; |
|
private boolean isAnonymous = false; |
|
private boolean isNested = false; |
|
private boolean computedNestedTypeStatus = false; |
|
public static final byte HEAP = 1; |
|
public static final byte FILE = 2; |
|
public static final byte ZIP = 3; |
|
|
|
private static BCELComparator bcelComparator = new BCELComparator() { |
|
|
|
@Override |
|
public boolean equals(final Object o1, final Object o2) { |
|
final JavaClass THIS = (JavaClass) o1; |
|
final JavaClass THAT = (JavaClass) o2; |
|
return THIS.getClassName().equals(THAT.getClassName()); |
|
} |
|
|
|
@Override |
|
public int hashCode(final Object o) { |
|
final JavaClass THIS = (JavaClass) o; |
|
return THIS.getClassName().hashCode(); |
|
} |
|
}; |
|
|
|
|
|
|
|
*/ |
|
private transient com.sun.org.apache.bcel.internal.util.Repository repository |
|
= SyntheticRepository.getInstance(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public JavaClass(final int class_name_index, final int superclass_name_index, |
|
final String file_name, final int major, final int minor, final int access_flags, |
|
final ConstantPool constant_pool, int[] interfaces, Field[] fields, |
|
Method[] methods, Attribute[] attributes, final byte source) { |
|
super(access_flags); |
|
if (interfaces == null) { |
|
interfaces = new int[0]; |
|
} |
|
if (attributes == null) { |
|
attributes = new Attribute[0]; |
|
} |
|
if (fields == null) { |
|
fields = new Field[0]; |
|
} |
|
if (methods == null) { |
|
methods = new Method[0]; |
|
} |
|
this.class_name_index = class_name_index; |
|
this.superclass_name_index = superclass_name_index; |
|
this.file_name = file_name; |
|
this.major = major; |
|
this.minor = minor; |
|
this.constant_pool = constant_pool; |
|
this.interfaces = interfaces; |
|
this.fields = fields; |
|
this.methods = methods; |
|
this.attributes = attributes; |
|
this.source = source; |
|
|
|
for (final Attribute attribute : attributes) { |
|
if (attribute instanceof SourceFile) { |
|
source_file_name = ((SourceFile) attribute).getSourceFileName(); |
|
break; |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
class_name = constant_pool.getConstantString(class_name_index, Const.CONSTANT_Class); |
|
class_name = Utility.compactClassName(class_name, false); |
|
final int index = class_name.lastIndexOf('.'); |
|
if (index < 0) { |
|
package_name = ""; |
|
} else { |
|
package_name = class_name.substring(0, index); |
|
} |
|
if (superclass_name_index > 0) { |
|
|
|
superclass_name = constant_pool.getConstantString(superclass_name_index, |
|
Const.CONSTANT_Class); |
|
superclass_name = Utility.compactClassName(superclass_name, false); |
|
} else { |
|
superclass_name = "java.lang.Object"; |
|
} |
|
interface_names = new String[interfaces.length]; |
|
for (int i = 0; i < interfaces.length; i++) { |
|
final String str = constant_pool.getConstantString(interfaces[i], Const.CONSTANT_Class); |
|
interface_names[i] = Utility.compactClassName(str, false); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public JavaClass(final int class_name_index, final int superclass_name_index, |
|
final String file_name, final int major, final int minor, final int access_flags, |
|
final ConstantPool constant_pool, final int[] interfaces, final Field[] fields, |
|
final Method[] methods, final Attribute[] attributes) { |
|
this(class_name_index, superclass_name_index, file_name, major, minor, access_flags, |
|
constant_pool, interfaces, fields, methods, attributes, HEAP); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public void accept(final Visitor v) { |
|
v.visitJavaClass(this); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public void dump(final File file) throws IOException { |
|
final String parent = file.getParent(); |
|
if (parent != null) { |
|
final File dir = new File(parent); |
|
if (!dir.mkdirs()) { |
|
if (!SecuritySupport.isDirectory(dir)) { |
|
throw new IOException("Could not create the directory " + dir); |
|
} |
|
} |
|
} |
|
try (DataOutputStream dos = new DataOutputStream(new FileOutputStream(file))) { |
|
dump(dos); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public void dump(final String _file_name) throws IOException { |
|
dump(new File(_file_name)); |
|
} |
|
|
|
|
|
|
|
*/ |
|
public byte[] getBytes() { |
|
final ByteArrayOutputStream s = new ByteArrayOutputStream(); |
|
final DataOutputStream ds = new DataOutputStream(s); |
|
try { |
|
dump(ds); |
|
} catch (final IOException e) { |
|
System.err.println("Error dumping class: " + e.getMessage()); |
|
} finally { |
|
try { |
|
ds.close(); |
|
} catch (final IOException e2) { |
|
System.err.println("Error dumping class: " + e2.getMessage()); |
|
} |
|
} |
|
return s.toByteArray(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public void dump(final OutputStream file) throws IOException { |
|
dump(new DataOutputStream(file)); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private void dump(final DataOutputStream file) throws IOException { |
|
file.writeInt(Const.JVM_CLASSFILE_MAGIC); |
|
file.writeShort(minor); |
|
file.writeShort(major); |
|
constant_pool.dump(file); |
|
file.writeShort(super.getAccessFlags()); |
|
file.writeShort(class_name_index); |
|
file.writeShort(superclass_name_index); |
|
file.writeShort(interfaces.length); |
|
for (final int interface1 : interfaces) { |
|
file.writeShort(interface1); |
|
} |
|
file.writeShort(fields.length); |
|
for (final Field field : fields) { |
|
field.dump(file); |
|
} |
|
file.writeShort(methods.length); |
|
for (final Method method : methods) { |
|
method.dump(file); |
|
} |
|
if (attributes != null) { |
|
file.writeShort(attributes.length); |
|
for (final Attribute attribute : attributes) { |
|
attribute.dump(file); |
|
} |
|
} else { |
|
file.writeShort(0); |
|
} |
|
file.flush(); |
|
} |
|
|
|
|
|
|
|
*/ |
|
public Attribute[] getAttributes() { |
|
return attributes; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public AnnotationEntry[] getAnnotationEntries() { |
|
if (annotations == null) { |
|
annotations = AnnotationEntry.createAnnotationEntries(getAttributes()); |
|
} |
|
|
|
return annotations; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public String getClassName() { |
|
return class_name; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public String getPackageName() { |
|
return package_name; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public int getClassNameIndex() { |
|
return class_name_index; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public ConstantPool getConstantPool() { |
|
return constant_pool; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public Field[] getFields() { |
|
return fields; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public String getFileName() { |
|
return file_name; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public String[] getInterfaceNames() { |
|
return interface_names; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public int[] getInterfaceIndices() { |
|
return interfaces; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public int getMajor() { |
|
return major; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public Method[] getMethods() { |
|
return methods; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public Method getMethod(final java.lang.reflect.Method m) { |
|
for (final Method method : methods) { |
|
if (m.getName().equals(method.getName()) && (m.getModifiers() == method.getModifiers()) |
|
&& Type.getSignature(m).equals(method.getSignature())) { |
|
return method; |
|
} |
|
} |
|
return null; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public int getMinor() { |
|
return minor; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public String getSourceFileName() { |
|
return source_file_name; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public String getSuperclassName() { |
|
return superclass_name; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public int getSuperclassNameIndex() { |
|
return superclass_name_index; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public void setAttributes(final Attribute[] attributes) { |
|
this.attributes = attributes; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public void setClassName(final String class_name) { |
|
this.class_name = class_name; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public void setClassNameIndex(final int class_name_index) { |
|
this.class_name_index = class_name_index; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public void setConstantPool(final ConstantPool constant_pool) { |
|
this.constant_pool = constant_pool; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public void setFields(final Field[] fields) { |
|
this.fields = fields; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public void setFileName(final String file_name) { |
|
this.file_name = file_name; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public void setInterfaceNames(final String[] interface_names) { |
|
this.interface_names = interface_names; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public void setInterfaces(final int[] interfaces) { |
|
this.interfaces = interfaces; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public void setMajor(final int major) { |
|
this.major = major; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public void setMethods(final Method[] methods) { |
|
this.methods = methods; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public void setMinor(final int minor) { |
|
this.minor = minor; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public void setSourceFileName(final String source_file_name) { |
|
this.source_file_name = source_file_name; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public void setSuperclassName(final String superclass_name) { |
|
this.superclass_name = superclass_name; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public void setSuperclassNameIndex(final int superclass_name_index) { |
|
this.superclass_name_index = superclass_name_index; |
|
} |
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public String toString() { |
|
String access = Utility.accessToString(super.getAccessFlags(), true); |
|
access = access.isEmpty() ? "" : (access + " "); |
|
final StringBuilder buf = new StringBuilder(128); |
|
buf.append(access).append(Utility.classOrInterface(super.getAccessFlags())).append(" ").append( |
|
class_name).append(" extends ").append( |
|
Utility.compactClassName(superclass_name, false)).append('\n'); |
|
final int size = interfaces.length; |
|
if (size > 0) { |
|
buf.append("implements\t\t"); |
|
for (int i = 0; i < size; i++) { |
|
buf.append(interface_names[i]); |
|
if (i < size - 1) { |
|
buf.append(", "); |
|
} |
|
} |
|
buf.append('\n'); |
|
} |
|
buf.append("filename\t\t").append(file_name).append('\n'); |
|
buf.append("compiled from\t\t").append(source_file_name).append('\n'); |
|
buf.append("compiler version\t").append(major).append(".").append(minor).append('\n'); |
|
buf.append("access flags\t\t").append(super.getAccessFlags()).append('\n'); |
|
buf.append("constant pool\t\t").append(constant_pool.getLength()).append(" entries\n"); |
|
buf.append("ACC_SUPER flag\t\t").append(isSuper()).append("\n"); |
|
if (attributes.length > 0) { |
|
buf.append("\nAttribute(s):\n"); |
|
for (final Attribute attribute : attributes) { |
|
buf.append(indent(attribute)); |
|
} |
|
} |
|
final AnnotationEntry[] annotations = getAnnotationEntries(); |
|
if (annotations != null && annotations.length > 0) { |
|
buf.append("\nAnnotation(s):\n"); |
|
for (final AnnotationEntry annotation : annotations) { |
|
buf.append(indent(annotation)); |
|
} |
|
} |
|
if (fields.length > 0) { |
|
buf.append("\n").append(fields.length).append(" fields:\n"); |
|
for (final Field field : fields) { |
|
buf.append("\t").append(field).append('\n'); |
|
} |
|
} |
|
if (methods.length > 0) { |
|
buf.append("\n").append(methods.length).append(" methods:\n"); |
|
for (final Method method : methods) { |
|
buf.append("\t").append(method).append('\n'); |
|
} |
|
} |
|
return buf.toString(); |
|
} |
|
|
|
private static String indent(final Object obj) { |
|
final StringTokenizer tok = new StringTokenizer(obj.toString(), "\n"); |
|
final StringBuilder buf = new StringBuilder(); |
|
while (tok.hasMoreTokens()) { |
|
buf.append("\t").append(tok.nextToken()).append("\n"); |
|
} |
|
return buf.toString(); |
|
} |
|
|
|
|
|
|
|
*/ |
|
public JavaClass copy() { |
|
JavaClass c = null; |
|
try { |
|
c = (JavaClass) clone(); |
|
c.constant_pool = constant_pool.copy(); |
|
c.interfaces = interfaces.clone(); |
|
c.interface_names = interface_names.clone(); |
|
c.fields = new Field[fields.length]; |
|
for (int i = 0; i < fields.length; i++) { |
|
c.fields[i] = fields[i].copy(c.constant_pool); |
|
} |
|
c.methods = new Method[methods.length]; |
|
for (int i = 0; i < methods.length; i++) { |
|
c.methods[i] = methods[i].copy(c.constant_pool); |
|
} |
|
c.attributes = new Attribute[attributes.length]; |
|
for (int i = 0; i < attributes.length; i++) { |
|
c.attributes[i] = attributes[i].copy(c.constant_pool); |
|
} |
|
} catch (final CloneNotSupportedException e) { |
|
// TODO should this throw? |
|
} |
|
return c; |
|
} |
|
|
|
public final boolean isSuper() { |
|
return (super.getAccessFlags() & Const.ACC_SUPER) != 0; |
|
} |
|
|
|
public final boolean isClass() { |
|
return (super.getAccessFlags() & Const.ACC_INTERFACE) == 0; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public final boolean isAnonymous() { |
|
computeNestedTypeStatus(); |
|
return this.isAnonymous; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public final boolean isNested() { |
|
computeNestedTypeStatus(); |
|
return this.isNested; |
|
} |
|
|
|
private void computeNestedTypeStatus() { |
|
if (computedNestedTypeStatus) { |
|
return; |
|
} |
|
for (final Attribute attribute : this.attributes) { |
|
if (attribute instanceof InnerClasses) { |
|
final InnerClass[] innerClasses = ((InnerClasses) attribute).getInnerClasses(); |
|
for (final InnerClass innerClasse : innerClasses) { |
|
boolean innerClassAttributeRefersToMe = false; |
|
String inner_class_name = constant_pool.getConstantString(innerClasse.getInnerClassIndex(), |
|
Const.CONSTANT_Class); |
|
inner_class_name = Utility.compactClassName(inner_class_name); |
|
if (inner_class_name.equals(getClassName())) { |
|
innerClassAttributeRefersToMe = true; |
|
} |
|
if (innerClassAttributeRefersToMe) { |
|
this.isNested = true; |
|
if (innerClasse.getInnerNameIndex() == 0) { |
|
this.isAnonymous = true; |
|
} |
|
} |
|
} |
|
} |
|
} |
|
this.computedNestedTypeStatus = true; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public final byte getSource() { |
|
return source; |
|
} |
|
|
|
/** |
|
* ******************* New repository functionality ******************** |
|
*/ |
|
|
|
|
|
|
|
*/ |
|
public com.sun.org.apache.bcel.internal.util.Repository getRepository() { |
|
return repository; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public void setRepository(final com.sun.org.apache.bcel.internal.util.Repository repository) { |
|
this.repository = repository; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public final boolean instanceOf(final JavaClass super_class) throws ClassNotFoundException { |
|
if (this.equals(super_class)) { |
|
return true; |
|
} |
|
final JavaClass[] super_classes = getSuperClasses(); |
|
for (final JavaClass super_classe : super_classes) { |
|
if (super_classe.equals(super_class)) { |
|
return true; |
|
} |
|
} |
|
if (super_class.isInterface()) { |
|
return implementationOf(super_class); |
|
} |
|
return false; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public boolean implementationOf(final JavaClass inter) throws ClassNotFoundException { |
|
if (!inter.isInterface()) { |
|
throw new IllegalArgumentException(inter.getClassName() + " is no interface"); |
|
} |
|
if (this.equals(inter)) { |
|
return true; |
|
} |
|
final JavaClass[] super_interfaces = getAllInterfaces(); |
|
for (final JavaClass super_interface : super_interfaces) { |
|
if (super_interface.equals(inter)) { |
|
return true; |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public JavaClass getSuperClass() throws ClassNotFoundException { |
|
if ("java.lang.Object".equals(getClassName())) { |
|
return null; |
|
} |
|
return repository.loadClass(getSuperclassName()); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public JavaClass[] getSuperClasses() throws ClassNotFoundException { |
|
JavaClass clazz = this; |
|
final List<JavaClass> allSuperClasses = new ArrayList<>(); |
|
for (clazz = clazz.getSuperClass(); clazz != null; clazz = clazz.getSuperClass()) { |
|
allSuperClasses.add(clazz); |
|
} |
|
return allSuperClasses.toArray(new JavaClass[allSuperClasses.size()]); |
|
} |
|
|
|
|
|
|
|
*/ |
|
public JavaClass[] getInterfaces() throws ClassNotFoundException { |
|
final String[] _interfaces = getInterfaceNames(); |
|
final JavaClass[] classes = new JavaClass[_interfaces.length]; |
|
for (int i = 0; i < _interfaces.length; i++) { |
|
classes[i] = repository.loadClass(_interfaces[i]); |
|
} |
|
return classes; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public JavaClass[] getAllInterfaces() throws ClassNotFoundException { |
|
final ClassQueue queue = new ClassQueue(); |
|
final Set<JavaClass> allInterfaces = new TreeSet<>(); |
|
queue.enqueue(this); |
|
while (!queue.empty()) { |
|
final JavaClass clazz = queue.dequeue(); |
|
final JavaClass souper = clazz.getSuperClass(); |
|
final JavaClass[] _interfaces = clazz.getInterfaces(); |
|
if (clazz.isInterface()) { |
|
allInterfaces.add(clazz); |
|
} else { |
|
if (souper != null) { |
|
queue.enqueue(souper); |
|
} |
|
} |
|
for (final JavaClass _interface : _interfaces) { |
|
queue.enqueue(_interface); |
|
} |
|
} |
|
return allInterfaces.toArray(new JavaClass[allInterfaces.size()]); |
|
} |
|
|
|
|
|
|
|
*/ |
|
public static BCELComparator getComparator() { |
|
return bcelComparator; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public static void setComparator(final BCELComparator comparator) { |
|
bcelComparator = comparator; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public boolean equals(final Object obj) { |
|
return bcelComparator.equals(this, obj); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public int compareTo(final JavaClass obj) { |
|
return getClassName().compareTo(obj.getClassName()); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public int hashCode() { |
|
return bcelComparator.hashCode(this); |
|
} |
|
} |