de.spricom.dessert.classfile.ClassFile Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of dessert-core Show documentation
Show all versions of dessert-core Show documentation
A library for unit-tests to check the dependencies between classes.
The newest version!
package de.spricom.dessert.classfile;
/*-
* #%L
* Dessert Dependency Assertion Library for Java
* %%
* Copyright (C) 2017 - 2023 Hans Jörg Heßmann
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import de.spricom.dessert.classfile.attribute.*;
import de.spricom.dessert.classfile.attribute.AttributeInfo.AttributeContext;
import de.spricom.dessert.classfile.constpool.ConstantPool;
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
/**
* Wraps the information contained in a .class file according
* to the
* Java Virtual Machine Specification.
*/
public class ClassFile {
public static final int MAGIC = 0xCAFEBABE;
public static final int ACC_PUBLIC = 0x0001; // Declared public; may be accessed from outside its package.
public static final int ACC_FINAL = 0x0010; // Declared final; no subclasses allowed.
public static final int ACC_SUPER = 0x0020; // Treat superclass methods specially when invoked by the invoke-special instruction.
public static final int ACC_INTERFACE = 0x0200; // Is an interface, not a class.
public static final int ACC_ABSTRACT = 0x0400; // Declared abstract; must not be instantiated.
public static final int ACC_SYNTHETIC = 0x1000; // Declared synthetic; not present in the source code.
public static final int ACC_ANNOTATION = 0x2000; // Declared as an annotation type.
public static final int ACC_ENUM = 0x4000; // Declared as an enum type.
public static final int ACC_MODULE = 0x8000; // Is a module, not a class or interface.
private final int minorVersion;
private final int majorVersion;
private final ConstantPool constantPool;
private final int accessFlags;
private final String thisClass;
private final String superClass;
private String[] interfaces;
private FieldInfo[] fields;
private MethodInfo[] methods;
private final AttributeInfo[] attributes;
public ClassFile(Class> clazz) throws IOException {
this(open(clazz));
}
private static InputStream open(Class> clazz) {
InputStream is = clazz.getResourceAsStream("/" + clazz.getName().replace('.', '/') + ".class");
assert is != null : "No file found for " + clazz;
return is;
}
public ClassFile(InputStream in) throws IOException {
BufferedInputStream bi = new BufferedInputStream(in);
try {
DataInputStream is = new DataInputStream(bi);
if (ClassFile.MAGIC != is.readInt()) {
throw new IOException("Not a class file.");
}
minorVersion = is.readUnsignedShort();
majorVersion = is.readUnsignedShort();
constantPool = new ConstantPool(is);
accessFlags = is.readUnsignedShort();
thisClass = constantPool.getConstantClassName(is.readUnsignedShort());
superClass = constantPool.getConstantClassName(is.readUnsignedShort());
readInterfaces(is);
readFields(is);
readMethods(is);
attributes = AttributeInfo.readAttributes(is, constantPool, AttributeContext.CLASS);
if (is.read() != -1) {
throw new IOException("EOF not reached!");
}
} finally {
bi.close();
}
}
private void readInterfaces(DataInputStream is) throws IOException {
int interfacesCount = is.readUnsignedShort();
interfaces = new String[interfacesCount];
for (int i = 0; i < interfacesCount; i++) {
interfaces[i] = constantPool.getConstantClassName(is.readUnsignedShort());
}
}
private void readFields(DataInputStream is) throws IOException {
int fieldCount = is.readUnsignedShort();
fields = new FieldInfo[fieldCount];
for (int i = 0; i < fieldCount; i++) {
fields[i] = new FieldInfo();
readMember(fields[i], is, AttributeContext.FIELD);
}
}
private void readMethods(DataInputStream is) throws IOException {
int methodCount = is.readUnsignedShort();
methods = new MethodInfo[methodCount];
for (int i = 0; i < methodCount; i++) {
methods[i] = new MethodInfo();
readMember(methods[i], is, AttributeContext.METHOD);
}
}
private void readMember(MemberInfo member, DataInputStream is, AttributeContext context) throws IOException {
member.setAccessFlags(is.readUnsignedShort());
member.setName(readString(is));
member.setDescriptor(readString(is));
member.setAttributes(AttributeInfo.readAttributes(is, constantPool, context));
}
private String readString(DataInputStream is) throws IOException {
return constantPool.getUtf8String(is.readUnsignedShort());
}
public Set getDependentClasses() {
Set classNames = new TreeSet();
for (FieldInfo fieldInfo : fields) {
fieldInfo.addDependentClassNames(classNames);
}
for (MethodInfo methodInfo : methods) {
methodInfo.addDependentClassNames(classNames);
}
for (AttributeInfo attribute : attributes) {
attribute.addDependentClassNames(classNames);
}
constantPool.addDependentClassNames(classNames);
classNames.remove(thisClass);
return classNames;
}
public String dumpConstantPool() {
return constantPool.dumpConstantPool();
}
/**
* Produces a dump similar to javap -verbose.
*
* @return the dump
*/
public String dump() {
StringBuilder sb = new StringBuilder();
sb.append(constantPool.dumpConstantPool());
String indent = "\t";
sb.append("Interfaces:\n");
for (String ifc : getInterfaces()) {
sb.append(indent).append(ifc).append("\n");
}
sb.append("Fields:\n");
for (FieldInfo field : getFields()) {
sb.append(indent).append(field).append("\n");
dump(field.getAttributes(), sb, indent + indent);
}
sb.append("Methods:\n");
for (MethodInfo method : getMethods()) {
sb.append(indent).append(method).append("\n");
dump(method.getAttributes(), sb, indent + indent);
}
sb.append("Class attributes:\n");
dump(getAttributes(), sb, indent);
sb.append("Dependent classes:\n");
for (String dependentClass : getDependentClasses()) {
sb.append(" ").append(dependentClass).append("\n");
}
return sb.toString();
}
private void dump(AttributeInfo[] attributes, StringBuilder sb, String indent) {
for (AttributeInfo attribute : attributes) {
sb.append(indent).append("attribute: ").append(attribute).append("\n");
}
}
public int getMinorVersion() {
return minorVersion;
}
public int getMajorVersion() {
return majorVersion;
}
public int getAccessFlags() {
return accessFlags;
}
public String getThisClass() {
return thisClass;
}
public String getSuperClass() {
return superClass;
}
public String[] getInterfaces() {
return interfaces;
}
public FieldInfo[] getFields() {
return fields;
}
public MethodInfo[] getMethods() {
return methods;
}
public AttributeInfo[] getAttributes() {
return attributes;
}
public boolean isPublic() {
return (accessFlags & ACC_PUBLIC) != 0;
}
public boolean isFinal() {
return (accessFlags & ACC_FINAL) != 0;
}
public boolean isSuper() {
return (accessFlags & ACC_SUPER) != 0;
}
public boolean isInterface() {
return (accessFlags & ACC_INTERFACE) != 0;
}
public boolean isAbstract() {
return (accessFlags & ACC_ABSTRACT) != 0;
}
public boolean isSynthetic() {
return (accessFlags & ACC_SYNTHETIC) != 0;
}
public boolean isAnnotation() {
return (accessFlags & ACC_ANNOTATION) != 0;
}
public boolean isEnum() {
return (accessFlags & ACC_ENUM) != 0;
}
public boolean isModule() {
return (accessFlags & ACC_MODULE) != 0;
}
public String getSimpleName() {
List innerClassesAttributes = Attributes.filter(attributes, InnerClassesAttribute.class);
if (!innerClassesAttributes.isEmpty()) {
for (InnerClass innerClass : innerClassesAttributes.get(0).getInnerClasses()) {
if (thisClass.equals(innerClass.getInnerClassName())) {
String simpleName = innerClass.getSimpleName();
return simpleName == null ? "" : simpleName;
}
}
}
return thisClass.substring(thisClass.lastIndexOf('.') + 1);
}
public boolean isInnerClass() {
if (thisClass.indexOf('$') == -1) {
return false;
}
if (majorVersion >= 55) {
return !Attributes.filter(attributes, NestHostAttribute.class).isEmpty();
}
if (!Attributes.filter(attributes, EnclosingMethodAttribute.class).isEmpty()) {
return true;
}
List innerClassesAttributes =
Attributes.filter(attributes, InnerClassesAttribute.class);
if (innerClassesAttributes.isEmpty()) {
return false;
}
for (InnerClass innerClass : innerClassesAttributes.get(0).getInnerClasses()) {
if (thisClass.equals(innerClass.getInnerClassName())) {
return true;
}
}
return false;
}
public boolean isNestHost() {
if (majorVersion >= 55) {
List nestMembersAttributes =
Attributes.filter(attributes, NestMembersAttribute.class);
return !nestMembersAttributes.isEmpty();
}
List innerClassesAttributes =
Attributes.filter(attributes, InnerClassesAttribute.class);
if (innerClassesAttributes.isEmpty()) {
return false;
}
int dollarIndex = thisClass.indexOf('$');
boolean hasInnerClass = false;
for (InnerClass innerClass : innerClassesAttributes.get(0).getInnerClasses()) {
if (this.thisClass.equals(innerClass.getInnerClassName())) {
return false;
}
if (innerClass.getInnerClassName().startsWith(thisClass)) {
if (dollarIndex == -1) {
return true;
}
hasInnerClass = true;
}
}
return hasInnerClass;
}
public String getNestHost() {
if (majorVersion >= 55) {
List nestHostAttributes = Attributes.filter(attributes, NestHostAttribute.class);
return nestHostAttributes.isEmpty() ? null : nestHostAttributes.get(0).getHostClassName();
}
List innerClassesAttributes =
Attributes.filter(attributes, InnerClassesAttribute.class);
if (innerClassesAttributes.isEmpty()) {
return null;
}
List enclosingMethodAttributes =
Attributes.filter(attributes, EnclosingMethodAttribute.class);
String outerName = enclosingMethodAttributes.isEmpty()
? thisClass
: enclosingMethodAttributes.get(0).getEnclosingClassname();
InnerClass[] innerClasses = innerClassesAttributes.get(0).getInnerClasses();
boolean outerMost = false;
while (!outerMost) {
outerMost = true;
for (InnerClass innerClass : innerClasses) {
if (outerName.equals(innerClass.getInnerClassName())
&& innerClass.getOuterClassName() != null) {
outerName = innerClass.getOuterClassName();
outerMost = false;
}
}
}
if (thisClass.equals(outerName)) {
for (InnerClass innerClass : innerClasses) {
if (thisClass.equals(innerClass.getOuterClassName())
|| innerClass.getInnerClassName().startsWith(thisClass)) {
return thisClass;
}
}
return null;
}
return outerName;
}
public List getNestMembers() {
if (majorVersion >= 55) {
List nestMembersAttributes =
Attributes.filter(attributes, NestMembersAttribute.class);
if (nestMembersAttributes.isEmpty()) {
return Collections.emptyList();
}
return Arrays.asList(nestMembersAttributes.get(0).getMembers());
}
List innerClassesAttributes =
Attributes.filter(attributes, InnerClassesAttribute.class);
if (innerClassesAttributes.isEmpty()) {
return Collections.emptyList();
}
InnerClass[] innerClasses = innerClassesAttributes.get(0).getInnerClasses();
List nestMembers = new ArrayList(innerClasses.length);
for (InnerClass innerClass : innerClasses) {
if (innerClass.getInnerClassName().startsWith(thisClass)) {
nestMembers.add(innerClass.getInnerClassName());
}
}
return nestMembers;
}
public boolean isDeprecated() {
return !Attributes.filter(attributes, DeprecatedAttribute.class).isEmpty();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy