package org.aspectj.apache.bcel.classfile; /* ==================================================================== * The Apache Software License, Version 1.1 * * Copyright (c) 2001 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, * if any, must include the following acknowledgment: * "This product includes software developed by the * Apache Software Foundation (https://www.apache.org/)." * Alternately, this acknowledgment may appear in the software itself, * if and wherever such third-party acknowledgments normally appear. * * 4. The names "Apache" and "Apache Software Foundation" and * "Apache BCEL" must not be used to endorse or promote products * derived from this software without prior written permission. For * written permission, please contact apache@apache.org. * * 5. Products derived from this software may not be called "Apache", * "Apache BCEL", nor may "Apache" appear in their name, without * prior written permission of the Apache Software Foundation. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . */ 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.Collection; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Queue; import java.util.StringTokenizer; import org.aspectj.apache.bcel.Constants; import org.aspectj.apache.bcel.classfile.annotation.AnnotationGen; import org.aspectj.apache.bcel.classfile.annotation.RuntimeAnnos; import org.aspectj.apache.bcel.generic.Type; import org.aspectj.apache.bcel.util.SyntheticRepository; /** * Represents a Java class, i.e., the data structures, constant pool, fields, methods and commands contained in a Java .class file. * See JVM specification for details. * * The intent of this class is to represent a parsed or otherwise existing class file. Those interested in programatically * generating classes should see the ClassGen class. * * @version $Id: JavaClass.java,v 1.22 2009/09/15 19:40:14 aclement Exp $ * @see org.aspectj.apache.bcel.generic.ClassGen * @author M. Dahm */ public class JavaClass extends Modifiers implements Cloneable, Node { private static final String[] NoInterfaceNames = new String[0]; private static final int[] NoInterfaceIndices = new int[0]; private String fileName; private String packageName; private String sourcefileName; private int classnameIdx; private int superclassnameIdx; private String classname; private String superclassname; private int major, minor; private ConstantPool cpool; private int[] interfaces; private String[] interfacenames; private Field[] fields; private Method[] methods; private Attribute[] attributes; private AnnotationGen[] annotations; private boolean isGeneric = false; private boolean isAnonymous = false; private boolean isNested = false; private boolean computedNestedTypeStatus = false; // Annotations are collected from certain attributes, don't do it more than necessary! private boolean annotationsOutOfDate = true; // state for dealing with generic signature string private String signatureAttributeString = null; private Signature signatureAttribute = null; private boolean searchedForSignatureAttribute = false; /** * In cases where we go ahead and create something, use the default SyntheticRepository, because we don't know any better. */ private transient org.aspectj.apache.bcel.util.Repository repository = null; public JavaClass(int classnameIndex, int superclassnameIndex, String filename, int major, int minor, int access_flags, ConstantPool cpool, int[] interfaces, Field[] fields, Method[] methods, Attribute[] attributes) { if (interfaces == null) { interfaces = NoInterfaceIndices; } this.classnameIdx = classnameIndex; this.superclassnameIdx = superclassnameIndex; this.fileName = filename; this.major = major; this.minor = minor; this.modifiers = access_flags; this.cpool = cpool; this.interfaces = interfaces; this.fields = (fields == null ? Field.NoFields : fields); this.methods = (methods == null ? Method.NoMethods : methods); this.attributes = (attributes == null ? Attribute.NoAttributes : attributes); annotationsOutOfDate = true; // Get source file name if available SourceFile sfAttribute = AttributeUtils.getSourceFileAttribute(attributes); sourcefileName = sfAttribute == null ? "" : sfAttribute.getSourceFileName(); /* * According to the specification the following entries must be of type `ConstantClass' but we check that anyway via the * `ConstPool.getConstant' method. */ classname = cpool.getConstantString(classnameIndex, Constants.CONSTANT_Class); classname = Utility.compactClassName(classname, false); int index = classname.lastIndexOf('.'); if (index < 0) { packageName = ""; } else { packageName = classname.substring(0, index); } if (superclassnameIndex > 0) { // May be zero -> class is java.lang.Object superclassname = cpool.getConstantString(superclassnameIndex, Constants.CONSTANT_Class); superclassname = Utility.compactClassName(superclassname, false); } else { superclassname = "java.lang.Object"; } if (interfaces.length == 0) { interfacenames = NoInterfaceNames; } else { interfacenames = new String[interfaces.length]; for (int i = 0; i < interfaces.length; i++) { String str = cpool.getConstantString(interfaces[i], Constants.CONSTANT_Class); interfacenames[i] = Utility.compactClassName(str, false); } } } /** * Called by objects that are traversing the nodes of the tree implicitely defined by the contents of a Java class. I.e., the * hierarchy of methods, fields, attributes, etc. spawns a tree of objects. * * @param v Visitor object */ public void accept(ClassVisitor v) { v.visitJavaClass(this); } /** * Dump class to a file. * * @param file Output file * @throws IOException */ public void dump(File file) throws IOException { String parent = file.getParent(); if (parent != null) { File dir = new File(parent); dir.mkdirs(); } dump(new DataOutputStream(new FileOutputStream(file))); } /** * Dump class to a file named file_name. * * @param file_name Output file name * @exception IOException */ public void dump(String file_name) throws IOException { dump(new File(file_name)); } /** * @return class in binary format */ public byte[] getBytes() { ByteArrayOutputStream s = new ByteArrayOutputStream(); DataOutputStream ds = new DataOutputStream(s); try { dump(ds); } catch (IOException e) { e.printStackTrace(); } finally { try { ds.close(); } catch (IOException e2) { e2.printStackTrace(); } } return s.toByteArray(); } /** * Dump Java class to output stream in binary format. */ public void dump(OutputStream file) throws IOException { dump(new DataOutputStream(file)); } /** * Dump Java class to output stream in binary format. */ public void dump(DataOutputStream file) throws IOException { file.writeInt(0xcafebabe); file.writeShort(minor); file.writeShort(major); cpool.dump(file); file.writeShort(modifiers); file.writeShort(classnameIdx); file.writeShort(superclassnameIdx); file.writeShort(interfaces.length); for (int anInterface : interfaces) { file.writeShort(anInterface); } file.writeShort(fields.length); for (Field field : fields) { field.dump(file); } file.writeShort(methods.length); for (Method method : methods) { method.dump(file); } AttributeUtils.writeAttributes(attributes, file); file.close(); } public Attribute[] getAttributes() { return attributes; } public AnnotationGen[] getAnnotations() { if (annotationsOutOfDate) { // Find attributes that contain annotation data List accumulatedAnnotations = new ArrayList<>(); for (Attribute attribute : attributes) { if (attribute instanceof RuntimeAnnos) { RuntimeAnnos runtimeAnnotations = (RuntimeAnnos) attribute; accumulatedAnnotations.addAll(runtimeAnnotations.getAnnotations()); } } annotations = accumulatedAnnotations.toArray(AnnotationGen.NO_ANNOTATIONS); annotationsOutOfDate = false; } return annotations; } /** * @return Class name. */ public String getClassName() { return classname; } /** * @return Package name. */ public String getPackageName() { return packageName; } public int getClassNameIndex() { return classnameIdx; } public ConstantPool getConstantPool() { return cpool; } /** * @return Fields, i.e., variables of the class. Like the JVM spec mandates for the classfile format, these fields are those * specific to this class, and not those of the superclass or superinterfaces. */ public Field[] getFields() { return fields; } /** * @return File name of class, aka SourceFile attribute value */ public String getFileName() { return fileName; } /** * @return Names of implemented interfaces. */ public String[] getInterfaceNames() { return interfacenames; } /** * @return Indices in constant pool of implemented interfaces. */ public int[] getInterfaceIndices() { return interfaces; } public int getMajor() { return major; } /** * @return Methods of the class. */ public Method[] getMethods() { return methods; } /** * @return A org.aspectj.apache.bcel.classfile.Method corresponding to java.lang.reflect.Method if any */ public Method getMethod(java.lang.reflect.Method m) { for (Method method : methods) { if (m.getName().equals(method.getName()) && m.getModifiers() == method.getModifiers() && Type.getSignature(m).equals(method.getSignature())) { return method; } } return null; } public Method getMethod(java.lang.reflect.Constructor c) { for (Method method : methods) { if (method.getName().equals("") && c.getModifiers() == method.getModifiers() && Type.getSignature(c).equals(method.getSignature())) { return method; } } return null; } public Field getField(java.lang.reflect.Field field) { String fieldName = field.getName(); for (Field f : fields) { if (f.getName().equals(fieldName)) { return f; } } return null; } /** * @return Minor number of class file version. */ public int getMinor() { return minor; } /** * @return absolute path to file where this class was read from */ public String getSourceFileName() { return sourcefileName; } /** * @return Superclass name. */ public String getSuperclassName() { return superclassname; } /** * @return Class name index. */ public int getSuperclassNameIndex() { return superclassnameIdx; } /** * @param attributes . */ public void setAttributes(Attribute[] attributes) { this.attributes = attributes; annotationsOutOfDate = true; } /** * @param class_name . */ public void setClassName(String class_name) { this.classname = class_name; } /** * @param class_name_index . */ public void setClassNameIndex(int class_name_index) { this.classnameIdx = class_name_index; } /** * @param constant_pool . */ public void setConstantPool(ConstantPool constant_pool) { this.cpool = constant_pool; } /** * @param fields . */ public void setFields(Field[] fields) { this.fields = fields; } /** * Set File name of class, aka SourceFile attribute value */ public void setFileName(String file_name) { this.fileName = file_name; } /** * @param interface_names . */ public void setInterfaceNames(String[] interface_names) { this.interfacenames = interface_names; } /** * @param interfaces . */ public void setInterfaces(int[] interfaces) { this.interfaces = interfaces; } public void setMajor(int major) { this.major = major; } public void setMethods(Method[] methods) { this.methods = methods; } public void setMinor(int minor) { this.minor = minor; } /** * Set absolute path to file this class was read from. */ public void setSourceFileName(String source_file_name) { this.sourcefileName = source_file_name; } /** * @param superclass_name . */ public void setSuperclassName(String superclass_name) { this.superclassname = superclass_name; } /** * @param superclass_name_index . */ public void setSuperclassNameIndex(int superclass_name_index) { this.superclassnameIdx = superclass_name_index; } /** * @return String representing class contents. */ @Override public String toString() { String access = Utility.accessToString(modifiers, true); access = access.equals("") ? "" : access + " "; StringBuilder buf = new StringBuilder(access + Utility.classOrInterface(modifiers) + " " + classname + " extends " + Utility.compactClassName(superclassname, false) + '\n'); int size = interfaces.length; if (size > 0) { buf.append("implements\t\t"); for (int i = 0; i < size; i++) { buf.append(interfacenames[i]); if (i < size - 1) { buf.append(", "); } } buf.append('\n'); } buf.append("filename\t\t" + fileName + '\n'); buf.append("compiled from\t\t" + sourcefileName + '\n'); buf.append("compiler version\t" + major + "." + minor + '\n'); buf.append("access flags\t\t" + modifiers + '\n'); buf.append("constant pool\t\t" + cpool.getLength() + " entries\n"); buf.append("ACC_SUPER flag\t\t" + isSuper() + "\n"); if (attributes.length > 0) { buf.append("\nAttribute(s):\n"); for (Attribute attribute : attributes) { buf.append(indent(attribute)); } } if (annotations != null && annotations.length > 0) { buf.append("\nAnnotation(s):\n"); for (AnnotationGen annotation : annotations) { buf.append(indent(annotation)); } } if (fields.length > 0) { buf.append("\n" + fields.length + " fields:\n"); for (Field field : fields) { buf.append("\t" + field + '\n'); } } if (methods.length > 0) { buf.append("\n" + methods.length + " methods:\n"); for (Method method : methods) { buf.append("\t" + method + '\n'); } } return buf.toString(); } private static final String indent(Object obj) { StringTokenizer tok = new StringTokenizer(obj.toString(), "\n"); StringBuilder buf = new StringBuilder(); while (tok.hasMoreTokens()) { buf.append("\t" + tok.nextToken() + "\n"); } return buf.toString(); } public final boolean isSuper() { return (modifiers & Constants.ACC_SUPER) != 0; } public final boolean isClass() { return (modifiers & Constants.ACC_INTERFACE) == 0; } public final boolean isAnonymous() { computeNestedTypeStatus(); return this.isAnonymous; } public final boolean isNested() { computeNestedTypeStatus(); return this.isNested; } private final void computeNestedTypeStatus() { if (computedNestedTypeStatus) { return; } // Attribute[] attrs = attributes.getAttributes(); for (Attribute attribute : attributes) { if (attribute instanceof InnerClasses) { InnerClass[] innerClasses = ((InnerClasses) attribute).getInnerClasses(); for (InnerClass innerClass : innerClasses) { boolean innerClassAttributeRefersToMe = false; String inner_class_name = cpool.getConstantString(innerClass.getInnerClassIndex(), Constants.CONSTANT_Class); inner_class_name = Utility.compactClassName(inner_class_name); if (inner_class_name.equals(getClassName())) { innerClassAttributeRefersToMe = true; } if (innerClassAttributeRefersToMe) { this.isNested = true; if (innerClass.getInnerNameIndex() == 0) { this.isAnonymous = true; } } } } } this.computedNestedTypeStatus = true; } // J5SUPPORT: /** * Returns true if this class represents an annotation, i.e. it was a 'public @interface blahblah' declaration */ public final boolean isAnnotation() { return (modifiers & Constants.ACC_ANNOTATION) != 0; } /** * Returns true if this class represents an enum type */ public final boolean isEnum() { return (modifiers & Constants.ACC_ENUM) != 0; } /********************* New repository functionality *********************/ /** * Gets the ClassRepository which holds its definition. By default this is the same as SyntheticRepository.getInstance(); */ public org.aspectj.apache.bcel.util.Repository getRepository() { if (repository == null) { repository = SyntheticRepository.getInstance(); } return repository; } /** * Sets the ClassRepository which loaded the JavaClass. Should be called immediately after parsing is done. */ public void setRepository(org.aspectj.apache.bcel.util.Repository repository) { this.repository = repository; } /** * Equivalent to runtime "instanceof" operator. * * @return true if this JavaClass is derived from teh super class */ public final boolean instanceOf(JavaClass super_class) { if (this.equals(super_class)) { return true; } JavaClass[] super_classes = getSuperClasses(); for (JavaClass superClass : super_classes) { if (superClass.equals(super_class)) { return true; } } if (super_class.isInterface()) { return implementationOf(super_class); } return false; } /** * @return true, if clazz is an implementation of interface inter */ public boolean implementationOf(JavaClass inter) { if (!inter.isInterface()) { throw new IllegalArgumentException(inter.getClassName() + " is no interface"); } if (this.equals(inter)) { return true; } Collection superInterfaces = getAllInterfaces(); for (JavaClass superInterface : superInterfaces) { if (superInterface.equals(inter)) { return true; } } // for (int i = 0; i < super_interfaces.length; i++) { // if (super_interfaces[i].equals(inter)) { // return true; // } // } return false; } /** * @return the superclass for this JavaClass object, or null if this is java.lang.Object */ public JavaClass getSuperClass() { if ("java.lang.Object".equals(getClassName())) { return null; } try { return getRepository().loadClass(getSuperclassName()); } catch (ClassNotFoundException e) { System.err.println(e); return null; } } /** * @return list of super classes of this class in ascending order, i.e., java.lang.Object is always the last element */ public JavaClass[] getSuperClasses() { JavaClass clazz = this; List vec = new ArrayList<>(); for (clazz = clazz.getSuperClass(); clazz != null; clazz = clazz.getSuperClass()) { vec.add(clazz); } return vec.toArray(new JavaClass[0]); } /** * Get interfaces directly implemented by this JavaClass. */ public JavaClass[] getInterfaces() { String[] interfaces = getInterfaceNames(); JavaClass[] classes = new JavaClass[interfaces.length]; try { for (int i = 0; i < interfaces.length; i++) { classes[i] = getRepository().loadClass(interfaces[i]); } } catch (ClassNotFoundException e) { System.err.println(e); return null; } return classes; } /** * Get all interfaces implemented by this JavaClass (transitively). */ public Collection getAllInterfaces() { Queue queue = new LinkedList<>(); List interfaceList = new ArrayList<>(); queue.add(this); while (!queue.isEmpty()) { JavaClass clazz = queue.remove(); JavaClass souper = clazz.getSuperClass(); JavaClass[] interfaces = clazz.getInterfaces(); if (clazz.isInterface()) { interfaceList.add(clazz); } else { if (souper != null) { queue.add(souper); } } Collections.addAll(queue, interfaces); } return interfaceList; // return interfaceList.toArray(new JavaClass[interfaceList.size()]); } /** * Hunts for a signature attribute on the member and returns its contents. So where the 'regular' signature may be * Ljava/util/Vector; the signature attribute will tell us e.g. "Ljava/lang/Object". We can learn the type variable names, * their bounds, and the true superclass and superinterface types (including any parameterizations) Coded for performance - * searches for the attribute only when requested - only searches for it once. */ public final String getGenericSignature() { loadGenericSignatureInfoIfNecessary(); return signatureAttributeString; } public boolean isGeneric() { loadGenericSignatureInfoIfNecessary(); return isGeneric; } private void loadGenericSignatureInfoIfNecessary() { if (!searchedForSignatureAttribute) { signatureAttribute = AttributeUtils.getSignatureAttribute(attributes); signatureAttributeString = signatureAttribute == null ? null : signatureAttribute.getSignature(); isGeneric = signatureAttribute != null && signatureAttributeString.charAt(0) == '<'; searchedForSignatureAttribute = true; } } public final Signature getSignatureAttribute() { loadGenericSignatureInfoIfNecessary(); return signatureAttribute; } }