All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.feilong.lib.javassist.bytecode.ClassFile Maven / Gradle / Ivy

Go to download

feilong is a suite of core and expanded libraries that include utility classes, http, excel,cvs, io classes, and much much more.

There is a newer version: 4.3.0
Show newest version
/*
 * Javassist, a Java-bytecode translator toolkit.
 * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License.  Alternatively, the contents of this file may be used under
 * the terms of the GNU Lesser General Public License Version 2.1 or later,
 * or the Apache License Version 2.0.
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 */

package com.feilong.lib.javassist.bytecode;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;

import com.feilong.lib.javassist.CannotCompileException;

/**
 * ClassFile represents a Java .class file, which
 * consists of a constant pool, methods, fields, and attributes.
 *
 * 

* For example, *

*
* *
 * ClassFile cf = new ClassFile(false, "test.Foo", null);
 * cf.setInterfaces(new String[] { "java.lang.Cloneable" });
 *
 * FieldInfo f = new FieldInfo(cf.getConstPool(), "width", "I");
 * f.setAccessFlags(AccessFlag.PUBLIC);
 * cf.addField(f);
 *
 * cf.write(new DataOutputStream(new FileOutputStream("Foo.class")));
 * 
* *
*

* This code generates a class file Foo.class for the following class: *

*
* *
 * package test;
 * 
 * class Foo implements Cloneable{
 * 
 *     public int width;
 * }
 * 
* *
* * @see FieldInfo * @see MethodInfo * @see ClassFileWriter * @see com.feilong.lib.javassist.CtClass#getClassFile() * @see com.feilong.lib.javassist.ClassPool#makeClass(ClassFile) */ public final class ClassFile{ int major, minor; // version number ConstPool constPool; int thisClass; int accessFlags; int superClass; int[] interfaces; List fields; List methods; List attributes; String thisclassname; // not JVM-internal name String[] cachedInterfaces; String cachedSuperclass; /** * The major version number of class files * for JDK 1.1. */ public static final int JAVA_1 = 45; /** * The major version number of class files * for JDK 1.2. */ public static final int JAVA_2 = 46; /** * The major version number of class files * for JDK 1.3. */ public static final int JAVA_3 = 47; /** * The major version number of class files * for JDK 1.4. */ public static final int JAVA_4 = 48; /** * The major version number of class files * for JDK 1.5. */ public static final int JAVA_5 = 49; /** * The major version number of class files * for JDK 1.6. */ public static final int JAVA_6 = 50; /** * The major version number of class files * for JDK 1.7. */ public static final int JAVA_7 = 51; /** * The major version number of class files * for JDK 1.8. */ public static final int JAVA_8 = 52; /** * The major version number of class files * for JDK 1.9. */ public static final int JAVA_9 = 53; /** * The major version number of class files * for JDK 10. */ public static final int JAVA_10 = 54; /** * The major version number of class files * for JDK 11. */ public static final int JAVA_11 = 55; /** * The major version number of class files created * from scratch. The default value is 47 (JDK 1.3). * It is 49 (JDK 1.5) * if the JVM supports java.lang.StringBuilder. * It is 50 (JDK 1.6) * if the JVM supports java.util.zip.DeflaterInputStream. * It is 51 (JDK 1.7) * if the JVM supports java.lang.invoke.CallSite. * It is 52 (JDK 1.8) * if the JVM supports java.util.function.Function. * It is 53 (JDK 1.9) * if the JVM supports java.lang.reflect.Module. * It is 54 (JDK 10) * if the JVM supports java.util.List.copyOf(Collection). * It is 55 (JDK 11) * if the JVM supports java.util.Optional.isEmpty(). */ public static final int MAJOR_VERSION; static{ int ver = JAVA_3; try{ Class.forName("java.lang.StringBuilder"); ver = JAVA_5; Class.forName("java.util.zip.DeflaterInputStream"); ver = JAVA_6; Class.forName("java.lang.invoke.CallSite", false, ClassLoader.getSystemClassLoader()); ver = JAVA_7; Class.forName("java.util.function.Function"); ver = JAVA_8; Class.forName("java.lang.Module"); ver = JAVA_9; List.class.getMethod("copyOf", Collection.class); ver = JAVA_10; Class.forName("java.util.Optional").getMethod("isEmpty"); ver = JAVA_11; }catch (Throwable t){} MAJOR_VERSION = ver; } /** * Constructs a class file from a byte stream. */ public ClassFile(DataInputStream in) throws IOException{ read(in); } /** * Constructs a class file including no members. * * @param isInterface * true if this is an interface. false if this is a class. * @param classname * a fully-qualified class name * @param superclass * a fully-qualified super class name or null. */ public ClassFile(boolean isInterface, String classname, String superclass){ major = MAJOR_VERSION; minor = 0; // JDK 1.3 or later constPool = new ConstPool(classname); thisClass = constPool.getThisClassInfo(); if (isInterface){ accessFlags = AccessFlag.INTERFACE | AccessFlag.ABSTRACT; }else{ accessFlags = AccessFlag.SUPER; } initSuperclass(superclass); interfaces = null; fields = new ArrayList<>(); methods = new ArrayList<>(); thisclassname = classname; attributes = new ArrayList<>(); attributes.add(new SourceFileAttribute(constPool, getSourcefileName(thisclassname))); } private void initSuperclass(String superclass){ if (superclass != null){ this.superClass = constPool.addClassInfo(superclass); cachedSuperclass = superclass; }else{ this.superClass = constPool.addClassInfo("java.lang.Object"); cachedSuperclass = "java.lang.Object"; } } private static String getSourcefileName(String qname){ return qname.replaceAll("^.*\\.", "") + ".java"; } /** * Eliminates dead constant pool items. If a method or a field is removed, * the constant pool items used by that method/field become dead items. This * method recreates a constant pool. */ public void compact(){ ConstPool cp = compact0(); for (MethodInfo minfo : methods){ minfo.compact(cp); } for (FieldInfo finfo : fields){ finfo.compact(cp); } attributes = AttributeInfo.copyAll(attributes, cp); constPool = cp; } private ConstPool compact0(){ ConstPool cp = new ConstPool(thisclassname); thisClass = cp.getThisClassInfo(); String sc = getSuperclass(); if (sc != null){ superClass = cp.addClassInfo(getSuperclass()); } if (interfaces != null){ for (int i = 0; i < interfaces.length; ++i){ interfaces[i] = cp.addClassInfo(constPool.getClassInfo(interfaces[i])); } } return cp; } /** * Discards all attributes, associated with both the class file and the * members such as a code attribute and exceptions attribute. The unused * constant pool entries are also discarded (a new packed constant pool is * constructed). */ public void prune(){ ConstPool cp = compact0(); List newAttributes = new ArrayList<>(); AttributeInfo invisibleAnnotations = getAttribute(AnnotationsAttribute.invisibleTag); if (invisibleAnnotations != null){ invisibleAnnotations = invisibleAnnotations.copy(cp, null); newAttributes.add(invisibleAnnotations); } AttributeInfo visibleAnnotations = getAttribute(AnnotationsAttribute.visibleTag); if (visibleAnnotations != null){ visibleAnnotations = visibleAnnotations.copy(cp, null); newAttributes.add(visibleAnnotations); } AttributeInfo signature = getAttribute(SignatureAttribute.tag); if (signature != null){ signature = signature.copy(cp, null); newAttributes.add(signature); } for (MethodInfo minfo : methods){ minfo.prune(cp); } for (FieldInfo finfo : fields){ finfo.prune(cp); } attributes = newAttributes; constPool = cp; } /** * Returns a constant pool table. */ public ConstPool getConstPool(){ return constPool; } /** * Returns true if this is an interface. */ public boolean isInterface(){ return (accessFlags & AccessFlag.INTERFACE) != 0; } /** * Returns true if this is a final class or interface. */ public boolean isFinal(){ return (accessFlags & AccessFlag.FINAL) != 0; } /** * Returns true if this is an abstract class or an interface. */ public boolean isAbstract(){ return (accessFlags & AccessFlag.ABSTRACT) != 0; } /** * Returns access flags. * * @see com.feilong.lib.javassist.bytecode.AccessFlag */ public int getAccessFlags(){ return accessFlags; } /** * Changes access flags. * * @see com.feilong.lib.javassist.bytecode.AccessFlag */ public void setAccessFlags(int acc){ if ((acc & AccessFlag.INTERFACE) == 0){ acc |= AccessFlag.SUPER; } accessFlags = acc; } /** * Returns access and property flags of this nested class. * This method returns -1 if the class is not a nested class. * *

* The returned value is obtained from inner_class_access_flags * of the entry representing this nested class itself * in InnerClasses_attribute. */ public int getInnerAccessFlags(){ InnerClassesAttribute ica = (InnerClassesAttribute) getAttribute(InnerClassesAttribute.tag); if (ica == null){ return -1; } String name = getName(); int n = ica.tableLength(); for (int i = 0; i < n; ++i){ if (name.equals(ica.innerClass(i))){ return ica.accessFlags(i); } } return -1; } /** * Returns the class name. */ public String getName(){ return thisclassname; } /** * Sets the class name. This method substitutes the new name for all * occurrences of the old class name in the class file. */ public void setName(String name){ renameClass(thisclassname, name); } /** * Returns the super class name. */ public String getSuperclass(){ if (cachedSuperclass == null){ cachedSuperclass = constPool.getClassInfo(superClass); } return cachedSuperclass; } /** * Returns the index of the constant pool entry representing the super * class. */ public int getSuperclassId(){ return superClass; } /** * Sets the super class. * *

* The new super class should inherit from the old super class. * This method modifies constructors so that they call constructors declared * in the new super class. */ public void setSuperclass(String superclass) throws CannotCompileException{ if (superclass == null){ superclass = "java.lang.Object"; } try{ this.superClass = constPool.addClassInfo(superclass); for (MethodInfo minfo : methods){ minfo.setSuperclass(superclass); } }catch (BadBytecode e){ throw new CannotCompileException(e); } cachedSuperclass = superclass; } /** * Replaces all occurrences of a class name in the class file. * *

* If class X is substituted for class Y in the class file, X and Y must * have the same signature. If Y provides a method m(), X must provide it * even if X inherits m() from the super class. If this fact is not * guaranteed, the bytecode verifier may cause an error. * * @param oldname * the replaced class name * @param newname * the substituted class name */ public void renameClass(String oldname,String newname){ if (oldname.equals(newname)){ return; } if (oldname.equals(thisclassname)){ thisclassname = newname; } oldname = Descriptor.toJvmName(oldname); newname = Descriptor.toJvmName(newname); constPool.renameClass(oldname, newname); AttributeInfo.renameClass(attributes, oldname, newname); for (MethodInfo minfo : methods){ String desc = minfo.getDescriptor(); minfo.setDescriptor(Descriptor.rename(desc, oldname, newname)); AttributeInfo.renameClass(minfo.getAttributes(), oldname, newname); } for (FieldInfo finfo : fields){ String desc = finfo.getDescriptor(); finfo.setDescriptor(Descriptor.rename(desc, oldname, newname)); AttributeInfo.renameClass(finfo.getAttributes(), oldname, newname); } } /** * Replaces all occurrences of several class names in the class file. * * @param classnames * specifies which class name is replaced with which new name. * Class names must be described with the JVM-internal * representation like java/lang/Object. * @see #renameClass(String,String) */ public void renameClass(Map classnames){ String jvmNewThisName = classnames.get(Descriptor.toJvmName(thisclassname)); if (jvmNewThisName != null){ thisclassname = Descriptor.toJavaName(jvmNewThisName); } constPool.renameClass(classnames); AttributeInfo.renameClass(attributes, classnames); for (MethodInfo minfo : methods){ String desc = minfo.getDescriptor(); minfo.setDescriptor(Descriptor.rename(desc, classnames)); AttributeInfo.renameClass(minfo.getAttributes(), classnames); } for (FieldInfo finfo : fields){ String desc = finfo.getDescriptor(); finfo.setDescriptor(Descriptor.rename(desc, classnames)); AttributeInfo.renameClass(finfo.getAttributes(), classnames); } } /** * Internal-use only. * CtClass.getRefClasses() calls this method. */ public void getRefClasses(Map classnames){ constPool.renameClass(classnames); AttributeInfo.getRefClasses(attributes, classnames); for (MethodInfo minfo : methods){ String desc = minfo.getDescriptor(); Descriptor.rename(desc, classnames); AttributeInfo.getRefClasses(minfo.getAttributes(), classnames); } for (FieldInfo finfo : fields){ String desc = finfo.getDescriptor(); Descriptor.rename(desc, classnames); AttributeInfo.getRefClasses(finfo.getAttributes(), classnames); } } /** * Returns the names of the interfaces implemented by the class. * The returned array is read only. */ public String[] getInterfaces(){ if (cachedInterfaces != null){ return cachedInterfaces; } String[] rtn = null; if (interfaces == null){ rtn = new String[0]; }else{ String[] list = new String[interfaces.length]; for (int i = 0; i < interfaces.length; ++i){ list[i] = constPool.getClassInfo(interfaces[i]); } rtn = list; } cachedInterfaces = rtn; return rtn; } /** * Sets the interfaces. * * @param nameList * the names of the interfaces. */ public void setInterfaces(String[] nameList){ cachedInterfaces = null; if (nameList != null){ interfaces = new int[nameList.length]; for (int i = 0; i < nameList.length; ++i){ interfaces[i] = constPool.addClassInfo(nameList[i]); } } } /** * Appends an interface to the interfaces implemented by the class. */ public void addInterface(String name){ cachedInterfaces = null; int info = constPool.addClassInfo(name); if (interfaces == null){ interfaces = new int[1]; interfaces[0] = info; }else{ int n = interfaces.length; int[] newarray = new int[n + 1]; System.arraycopy(interfaces, 0, newarray, 0, n); newarray[n] = info; interfaces = newarray; } } /** * Returns all the fields declared in the class. * * @return a list of FieldInfo. * @see FieldInfo */ public List getFields(){ return fields; } /** * Appends a field to the class. * * @throws DuplicateMemberException * when the field is already included. */ public void addField(FieldInfo finfo) throws DuplicateMemberException{ testExistingField(finfo.getName(), finfo.getDescriptor()); fields.add(finfo); } /** * Just appends a field to the class. * It does not check field duplication. * Use this method only when minimizing performance overheads * is seriously required. * * @since 3.13 */ public void addField2(FieldInfo finfo){ fields.add(finfo); } private void testExistingField(String name,String descriptor) throws DuplicateMemberException{ for (FieldInfo minfo : fields){ if (minfo.getName().equals(name)){ throw new DuplicateMemberException("duplicate field: " + name); } } } /** * Returns all the methods declared in the class. * * @return a list of MethodInfo. * @see MethodInfo */ public List getMethods(){ return methods; } /** * Returns the method with the specified name. If there are multiple methods * with that name, this method returns one of them. * * @return null if no such method is found. */ public MethodInfo getMethod(String name){ for (MethodInfo minfo : methods){ if (minfo.getName().equals(name)){ return minfo; } } return null; } /** * Returns a static initializer (class initializer), or null if it does not * exist. */ public MethodInfo getStaticInitializer(){ return getMethod(MethodInfo.nameClinit); } /** * Appends a method to the class. * If there is a bridge method with the same name and signature, * then the bridge method is removed before a new method is added. * * @throws DuplicateMemberException * when the method is already included. */ public void addMethod(MethodInfo minfo) throws DuplicateMemberException{ testExistingMethod(minfo); methods.add(minfo); } /** * Just appends a method to the class. * It does not check method duplication or remove a bridge method. * Use this method only when minimizing performance overheads * is seriously required. * * @since 3.13 */ public void addMethod2(MethodInfo minfo){ methods.add(minfo); } private void testExistingMethod(MethodInfo newMinfo) throws DuplicateMemberException{ String name = newMinfo.getName(); String descriptor = newMinfo.getDescriptor(); ListIterator it = methods.listIterator(0); while (it.hasNext()){ if (isDuplicated(newMinfo, name, descriptor, it.next(), it)){ throw new DuplicateMemberException("duplicate method: " + name + " in " + this.getName()); } } } private static boolean isDuplicated(MethodInfo newMethod,String newName,String newDesc,MethodInfo minfo,ListIterator it){ if (!minfo.getName().equals(newName)){ return false; } String desc = minfo.getDescriptor(); if (!Descriptor.eqParamTypes(desc, newDesc)){ return false; } if (desc.equals(newDesc)){ if (notBridgeMethod(minfo)){ return true; } // if the bridge method with the same signature // already exists, replace it. it.remove(); return false; } return false; // return notBridgeMethod(minfo) && notBridgeMethod(newMethod); } /* * For a bridge method, see Sec. 15.12.4.5 of JLS 3rd Ed. */ private static boolean notBridgeMethod(MethodInfo minfo){ return (minfo.getAccessFlags() & AccessFlag.BRIDGE) == 0; } /** * Returns all the attributes. The returned List object * is shared with this object. If you add a new attribute to the list, * the attribute is also added to the classs file represented by this * object. If you remove an attribute from the list, it is also removed * from the class file. * * @return a list of AttributeInfo objects. * @see AttributeInfo */ public List getAttributes(){ return attributes; } /** * Returns the attribute with the specified name. If there are multiple * attributes with that name, this method returns either of them. It * returns null if the specified attributed is not found. * *

* An attribute name can be obtained by, for example, * {@link AnnotationsAttribute#visibleTag} or * {@link AnnotationsAttribute#invisibleTag}. *

* * @param name * attribute name * @see #getAttributes() */ public AttributeInfo getAttribute(String name){ for (AttributeInfo ai : attributes){ if (ai.getName().equals(name)){ return ai; } } return null; } /** * Removes an attribute with the specified name. * * @param name * attribute name. * @return the removed attribute or null. * @since 3.21 */ public AttributeInfo removeAttribute(String name){ return AttributeInfo.remove(attributes, name); } /** * Appends an attribute. If there is already an attribute with the same * name, the new one substitutes for it. * * @see #getAttributes() */ public void addAttribute(AttributeInfo info){ AttributeInfo.remove(attributes, info.getName()); attributes.add(info); } /** * Returns the source file containing this class. * * @return null if this information is not available. */ public String getSourceFile(){ SourceFileAttribute sf = (SourceFileAttribute) getAttribute(SourceFileAttribute.tag); if (sf == null){ return null; } return sf.getFileName(); } private void read(DataInputStream in) throws IOException{ int i, n; int magic = in.readInt(); if (magic != 0xCAFEBABE){ throw new IOException("bad magic number: " + Integer.toHexString(magic)); } minor = in.readUnsignedShort(); major = in.readUnsignedShort(); constPool = new ConstPool(in); accessFlags = in.readUnsignedShort(); thisClass = in.readUnsignedShort(); constPool.setThisClassInfo(thisClass); superClass = in.readUnsignedShort(); n = in.readUnsignedShort(); if (n == 0){ interfaces = null; }else{ interfaces = new int[n]; for (i = 0; i < n; ++i){ interfaces[i] = in.readUnsignedShort(); } } ConstPool cp = constPool; n = in.readUnsignedShort(); fields = new ArrayList<>(); for (i = 0; i < n; ++i){ addField2(new FieldInfo(cp, in)); } n = in.readUnsignedShort(); methods = new ArrayList<>(); for (i = 0; i < n; ++i){ addMethod2(new MethodInfo(cp, in)); } attributes = new ArrayList<>(); n = in.readUnsignedShort(); for (i = 0; i < n; ++i){ addAttribute(AttributeInfo.read(cp, in)); } thisclassname = constPool.getClassInfo(thisClass); } /** * Writes a class file represented by this object into an output stream. */ public void write(DataOutputStream out) throws IOException{ int i, n; out.writeInt(0xCAFEBABE); // magic out.writeShort(minor); // minor version out.writeShort(major); // major version constPool.write(out); // constant pool out.writeShort(accessFlags); out.writeShort(thisClass); out.writeShort(superClass); if (interfaces == null){ n = 0; }else{ n = interfaces.length; } out.writeShort(n); for (i = 0; i < n; ++i){ out.writeShort(interfaces[i]); } n = fields.size(); out.writeShort(n); for (i = 0; i < n; ++i){ FieldInfo finfo = fields.get(i); finfo.write(out); } out.writeShort(methods.size()); for (MethodInfo minfo : methods){ minfo.write(out); } out.writeShort(attributes.size()); AttributeInfo.writeAll(attributes, out); } /** * Get the Major version. * * @return the major version */ public int getMajorVersion(){ return major; } /** * Set the major version. * * @param major * the major version */ public void setMajorVersion(int major){ this.major = major; } /** * Get the minor version. * * @return the minor version */ public int getMinorVersion(){ return minor; } /** * Set the minor version. * * @param minor * the minor version */ public void setMinorVersion(int minor){ this.minor = minor; } /** * Sets the major and minor version to Java 5. * * If the major version is older than 49, Java 5 * extensions such as annotations are ignored * by the JVM. */ public void setVersionToJava5(){ this.major = 49; this.minor = 0; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy