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

com.feilong.lib.javassist.ClassPool 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.0.8
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;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;

import com.feilong.lib.javassist.bytecode.ClassFile;
import com.feilong.lib.javassist.bytecode.Descriptor;

/**
 * A container of CtClass objects.
 * A CtClass object must be obtained from this object.
 * If get() is called on this object,
 * it searches various sources represented by ClassPath
 * to find a class file and then it creates a CtClass object
 * representing that class file. The created object is returned to the
 * caller.
 *
 * 

* Memory consumption memo: * *

* ClassPool objects hold all the CtClasses * that have been created so that the consistency among modified classes * can be guaranteed. Thus if a large number of CtClasses * are processed, the ClassPool will consume a huge amount * of memory. To avoid this, a ClassPool object * should be recreated, for example, every hundred classes processed. * Note that getDefault() is a singleton factory. * Otherwise, detach() in CtClass should be used * to avoid huge memory consumption. * *

* ClassPool hierarchy: * *

* ClassPools can make a parent-child hierarchy as * java.lang.ClassLoaders. If a ClassPool has * a parent pool, get() first asks the parent pool to find * a class file. Only if the parent could not find the class file, * get() searches the ClassPaths of * the child ClassPool. This search order is reversed if * ClassPath.childFirstLookup is true. * * @see com.feilong.lib.javassist.CtClass * @see com.feilong.lib.javassist.ClassPath */ @SuppressWarnings({ "unchecked", "rawtypes" }) public class ClassPool{ /** * Determines the search order. * *

* If this field is true, get() first searches the * class path associated to this ClassPool and then * the class path associated with the parent ClassPool. * Otherwise, the class path associated with the parent is searched * first. * *

* The default value is false. */ public boolean childFirstLookup = false; /** * Turning the automatic pruning on/off. * *

* If this field is true, CtClass objects are * automatically pruned by default when toBytecode() etc. * are called. The automatic pruning can be turned on/off individually * for each CtClass object. * *

* The initial value is false. * * @see CtClass#prune() * @see CtClass#stopPruning(boolean) * @see CtClass#detach() */ public static boolean doPruning = false; private int compressCount; private static final int COMPRESS_THRESHOLD = 100; /* * releaseUnmodifiedClassFile was introduced for avoiding a bug * of JBoss AOP. So the value should be true except for JBoss AOP. */ /** * If true, unmodified and not-recently-used class files are * periodically released for saving memory. * *

* The initial value is true. */ public static boolean releaseUnmodifiedClassFile = true; protected ClassPoolTail source; protected ClassPool parent; protected Hashtable classes; // should be synchronous /** * Table of registered cflow variables. */ private Hashtable cflow = null; // should be synchronous. private static final int INIT_HASH_SIZE = 191; private ArrayList importedPackages; /** * Creates a class pool. * * @param parent * the parent of this class pool. If this is a root * class pool, this parameter must be null. * @see com.feilong.lib.javassist.ClassPool#getDefault() */ public ClassPool(ClassPool parent){ this.classes = new Hashtable(INIT_HASH_SIZE); this.source = new ClassPoolTail(); this.parent = parent; if (parent == null){ CtClass[] pt = CtClass.primitiveTypes; for (CtClass element : pt){ classes.put(element.getName(), element); } } this.cflow = null; this.compressCount = 0; clearImportedPackages(); } /** * Returns the default class pool. * The returned object is always identical since this method is * a singleton factory. * *

* The default class pool searches the system search path, * which usually includes the platform library, extension * libraries, and the search path specified by the * -classpath option or the CLASSPATH * environment variable. * *

* When this method is called for the first time, the default * class pool is created with the following code snippet: * *

     * ClassPool cp = new ClassPool();
     * cp.appendSystemPath();
     * 
* *

* If the default class pool cannot find any class files, * try ClassClassPath, ModuleClassPath, * or LoaderClassPath. * * @see ClassClassPath * @see LoaderClassPath */ public static synchronized ClassPool getDefault(){ if (defaultPool == null){ defaultPool = new ClassPool(null); defaultPool.appendSystemPath(); } return defaultPool; } private static ClassPool defaultPool = null; /** * Provide a hook so that subclasses can do their own * caching of classes. * * @see #cacheCtClass(String,CtClass,boolean) * @see #removeCached(String) */ protected CtClass getCached(String classname){ return (CtClass) classes.get(classname); } /** * Provides a hook so that subclasses can do their own * caching of classes. * * @see #getCached(String) * @see #removeCached(String) */ protected void cacheCtClass(String classname,CtClass c,boolean dynamic){ classes.put(classname, c); } /** * Provide a hook so that subclasses can do their own * caching of classes. * * @see #getCached(String) * @see #cacheCtClass(String,CtClass,boolean) */ protected CtClass removeCached(String classname){ return (CtClass) classes.remove(classname); } /** * Returns the class search path. */ @Override public String toString(){ return source.toString(); } /** * This method is periodically invoked so that memory * footprint will be minimized. */ void compress(){ if (compressCount++ > COMPRESS_THRESHOLD){ compressCount = 0; Enumeration e = classes.elements(); while (e.hasMoreElements()){ ((CtClass) e.nextElement()).compress(); } } } /** * Record a package name so that the Javassist compiler searches * the package to resolve a class name. * Don't record the java.lang package, which has * been implicitly recorded by default. * *

* Since version 3.14, packageName can be a * fully-qualified class name. * *

* Note that get() in ClassPool does * not search the recorded package. Only the compiler searches it. * * @param packageName * the package name. * It must not include the last '.' (dot). * For example, "java.util" is valid but "java.util." is wrong. * @since 3.1 */ private void importPackage(String packageName){ importedPackages.add(packageName); } /** * Clear all the package names recorded by importPackage(). * The java.lang package is not removed. * * @see #importPackage(String) * @since 3.1 */ private void clearImportedPackages(){ importedPackages = new ArrayList(); importedPackages.add("java.lang"); } /** * Returns all the package names recorded by importPackage(). * * @see #importPackage(String) * @since 3.1 */ public Iterator getImportedPackages(){ return importedPackages.iterator(); } /** * Records the $cflow variable for the field specified * by cname and fname. * * @param name * variable name * @param cname * class name * @param fname * field name */ void recordCflow(String name,String cname,String fname){ if (cflow == null){ cflow = new Hashtable(); } cflow.put(name, new Object[] { cname, fname }); } /** * Undocumented method. Do not use; internal-use only. * * @param name * the name of $cflow variable */ public Object[] lookupCflow(String name){ if (cflow == null){ cflow = new Hashtable(); } return (Object[]) cflow.get(name); } /* * This method is invoked by CtClassType.setName(). It removes a * CtClass object from the hash table and inserts it with the new * name. Don't delegate to the parent. */ synchronized void classNameChanged(String oldname,CtClass clazz){ CtClass c = getCached(oldname); if (c == clazz){ removeCached(oldname); // see getAndRename(). } String newName = clazz.getName(); checkNotFrozen(newName); cacheCtClass(newName, clazz, false); } /** * Reads a class file from the source and returns a reference * to the CtClass * object representing that class file. If that class file has been * already read, this method returns a reference to the * CtClass created when that class file was read at the * first time. * *

* If classname ends with "[]", then this method * returns a CtClass object for that array type. * *

* To obtain an inner class, use "$" instead of "." for separating * the enclosing class name and the inner class name. * * @param classname * a fully-qualified class name. */ public CtClass get(String classname) throws NotFoundException{ CtClass clazz; if (classname == null){ clazz = null; }else{ clazz = get0(classname, true); } if (clazz == null){ throw new NotFoundException(classname); }else{ clazz.incGetCounter(); return clazz; } } /** * Returns a CtClass object with the given name. * This is almost equivalent to get(String) except * that classname can be an array-type "descriptor" (an encoded * type name) such as [Ljava/lang/Object;. * *

* Using this method is not recommended; this method should be * used only to obtain the CtClass object * with a name returned from getClassInfo in * javassist.bytecode.ClassPool. getClassInfo * returns a fully-qualified class name but, if the class is an array * type, it returns a descriptor. * * @param classname * a fully-qualified class name or a descriptor * representing an array type. * @see #get(String) * @see com.feilong.lib.javassist.bytecode.ConstPool#getClassInfo(int) * @see com.feilong.lib.javassist.bytecode.Descriptor#toCtClass(String, ClassPool) * @since 3.8.1 */ public CtClass getCtClass(String classname) throws NotFoundException{ if (classname.charAt(0) == '['){ return Descriptor.toCtClass(classname, this); } return get(classname); } /** * @param useCache * false if the cached CtClass must be ignored. * @return null if the class could not be found. */ protected synchronized CtClass get0(String classname,boolean useCache) throws NotFoundException{ CtClass clazz = null; if (useCache){ clazz = getCached(classname); if (clazz != null){ return clazz; } } if (!childFirstLookup && parent != null){ clazz = parent.get0(classname, useCache); if (clazz != null){ return clazz; } } clazz = createCtClass(classname, useCache); if (clazz != null){ // clazz.getName() != classname if classname is "[L;". if (useCache){ cacheCtClass(clazz.getName(), clazz, false); } return clazz; } if (childFirstLookup && parent != null){ clazz = parent.get0(classname, useCache); } return clazz; } /** * Creates a CtClass object representing the specified class. * It first examines whether or not the corresponding class * file exists. If yes, it creates a CtClass object. * * @return null if the class file could not be found. */ protected CtClass createCtClass(String classname,boolean useCache){ // accept "[L;" as a class name. if (classname.charAt(0) == '['){ classname = Descriptor.toClassName(classname); } if (classname.endsWith("[]")){ String base = classname.substring(0, classname.indexOf('[')); if ((!useCache || getCached(base) == null) && find(base) == null){ return null; } return new CtArray(classname, this); }else if (find(classname) == null){ return null; }else{ return new CtClassType(classname, this); } } /** * Searches the class path to obtain the URL of the class file * specified by classname. It is also used to determine whether * the class file exists. * * @param classname * a fully-qualified class name. * @return null if the class file could not be found. * @see CtClass#getURL() */ public URL find(String classname){ return source.find(classname); } /* * Is invoked by CtClassType.setName() and methods in this class. * This method throws an exception if the class is already frozen or * if this class pool cannot edit the class since it is in a parent * class pool. * * @see checkNotExists(String) */ void checkNotFrozen(String classname) throws RuntimeException{ CtClass clazz = getCached(classname); if (clazz == null){ if (!childFirstLookup && parent != null){ try{ clazz = parent.get0(classname, true); }catch (NotFoundException e){} if (clazz != null){ throw new RuntimeException(classname + " is in a parent ClassPool. Use the parent."); } } }else if (clazz.isFrozen()){ throw new RuntimeException(classname + ": frozen class (cannot edit)"); } } /* * This method returns null if this or its parent class pool does * not contain a CtClass object with the class name. * * @see checkNotFrozen(String) */ CtClass checkNotExists(String classname){ CtClass clazz = getCached(classname); if (clazz == null){ if (!childFirstLookup && parent != null){ try{ clazz = parent.get0(classname, true); }catch (NotFoundException e){} } } return clazz; } /* * for CtClassType.getClassFile2(). Don't delegate to the parent. */ InputStream openClassfile(String classname) throws NotFoundException{ return source.openClassfile(classname); } void writeClassfile(String classname,OutputStream out) throws NotFoundException,IOException,CannotCompileException{ source.writeClassfile(classname, out); } /** * Reads class files from the source and returns an array of * CtClass * objects representing those class files. * *

* If an element of classnames ends with "[]", * then this method * returns a CtClass object for that array type. * * @param classnames * an array of fully-qualified class name. */ public CtClass[] get(String[] classnames) throws NotFoundException{ if (classnames == null){ return new CtClass[0]; } int num = classnames.length; CtClass[] result = new CtClass[num]; for (int i = 0; i < num; ++i){ result[i] = get(classnames[i]); } return result; } /** * Creates a new class (or interface) from the given class file. * If there already exists a class with the same name, the new class * overwrites that previous class. * *

* This method is used for creating a CtClass object * directly from a class file. The qualified class name is obtained * from the class file; you do not have to explicitly give the name. * * @param classfile * class file. * @throws RuntimeException * if there is a frozen class with the * the same name. * @see #makeClassIfNew(InputStream) * @see com.feilong.lib.javassist.ByteArrayClassPath */ public CtClass makeClass(InputStream classfile) throws IOException,RuntimeException{ return makeClass(classfile, true); } /** * Creates a new class (or interface) from the given class file. * If there already exists a class with the same name, the new class * overwrites that previous class. * *

* This method is used for creating a CtClass object * directly from a class file. The qualified class name is obtained * from the class file; you do not have to explicitly give the name. * * @param classfile * class file. * @param ifNotFrozen * throws a RuntimeException if this parameter is true * and there is a frozen class with the same name. * @see com.feilong.lib.javassist.ByteArrayClassPath */ public CtClass makeClass(InputStream classfile,boolean ifNotFrozen) throws IOException,RuntimeException{ compress(); classfile = new BufferedInputStream(classfile); CtClass clazz = new CtClassType(classfile, this); clazz.checkModify(); String classname = clazz.getName(); if (ifNotFrozen){ checkNotFrozen(classname); } cacheCtClass(classname, clazz, true); return clazz; } /** * Creates a new class (or interface) from the given class file. * If there already exists a class with the same name, the new class * overwrites that previous class. * *

* This method is used for creating a CtClass object * directly from a class file. The qualified class name is obtained * from the class file; you do not have to explicitly give the name. * * @param classfile * class file. * @throws RuntimeException * if there is a frozen class with the * the same name. * @since 3.20 */ public CtClass makeClass(ClassFile classfile) throws RuntimeException{ return makeClass(classfile, true); } /** * Creates a new class (or interface) from the given class file. * If there already exists a class with the same name, the new class * overwrites that previous class. * *

* This method is used for creating a CtClass object * directly from a class file. The qualified class name is obtained * from the class file; you do not have to explicitly give the name. * * @param classfile * class file. * @param ifNotFrozen * throws a RuntimeException if this parameter is true * and there is a frozen class with the same name. * @since 3.20 */ private CtClass makeClass(ClassFile classfile,boolean ifNotFrozen) throws RuntimeException{ compress(); CtClass clazz = new CtClassType(classfile, this); clazz.checkModify(); String classname = clazz.getName(); if (ifNotFrozen){ checkNotFrozen(classname); } cacheCtClass(classname, clazz, true); return clazz; } /** * Creates a new class (or interface) from the given class file. * If there already exists a class with the same name, this method * returns the existing class; a new class is never created from * the given class file. * *

* This method is used for creating a CtClass object * directly from a class file. The qualified class name is obtained * from the class file; you do not have to explicitly give the name. * * @param classfile * the class file. * @see #makeClass(InputStream) * @see com.feilong.lib.javassist.ByteArrayClassPath * @since 3.9 */ public CtClass makeClassIfNew(InputStream classfile) throws IOException,RuntimeException{ compress(); classfile = new BufferedInputStream(classfile); CtClass clazz = new CtClassType(classfile, this); clazz.checkModify(); String classname = clazz.getName(); CtClass found = checkNotExists(classname); if (found != null){ return found; } cacheCtClass(classname, clazz, true); return clazz; } /** * Creates a new public class. * If there already exists a class with the same name, the new class * overwrites that previous class. * *

* If no constructor is explicitly added to the created new * class, Javassist generates constructors and adds it when * the class file is generated. It generates a new constructor * for each constructor of the super class. The new constructor * takes the same set of parameters and invokes the * corresponding constructor of the super class. All the received * parameters are passed to it. * * @param classname * a fully-qualified class name. * @throws RuntimeException * if the existing class is frozen. */ private CtClass makeClass(String classname) throws RuntimeException{ return makeClass(classname, null); } /** * Creates a new public class. * If there already exists a class/interface with the same name, * the new class overwrites that previous class. * *

* If no constructor is explicitly added to the created new * class, Javassist generates constructors and adds it when * the class file is generated. It generates a new constructor * for each constructor of the super class. The new constructor * takes the same set of parameters and invokes the * corresponding constructor of the super class. All the received * parameters are passed to it. * * @param classname * a fully-qualified class name. * @param superclass * the super class. * @throws RuntimeException * if the existing class is frozen. */ public synchronized CtClass makeClass(String classname,CtClass superclass) throws RuntimeException{ checkNotFrozen(classname); CtClass clazz = new CtNewClass(classname, this, false, superclass); cacheCtClass(classname, clazz, true); return clazz; } /** * Creates a new public nested class. * This method is called by {@link CtClassType#makeNestedClass()}. * * @param classname * a fully-qualified class name. * @return the nested class. */ synchronized CtClass makeNestedClass(String classname){ checkNotFrozen(classname); CtClass clazz = new CtNewClass(classname, this, false, null); cacheCtClass(classname, clazz, true); return clazz; } /** * Appends the system search path to the end of the * search path. The system search path * usually includes the platform library, extension * libraries, and the search path specified by the * -classpath option or the CLASSPATH * environment variable. * * @return the appended class path. */ public ClassPath appendSystemPath(){ return source.appendSystemPath(); } /** * Insert a ClassPath object at the head of the * search path. * * @return the inserted class path. * @see com.feilong.lib.javassist.ClassPath * @see com.feilong.lib.javassist.URLClassPath * @see com.feilong.lib.javassist.ByteArrayClassPath */ public ClassPath insertClassPath(ClassPath cp){ return source.insertClassPath(cp); } /** * Appends a ClassPath object to the end of the * search path. * * @return the appended class path. * @see com.feilong.lib.javassist.ClassPath * @see com.feilong.lib.javassist.URLClassPath * @see com.feilong.lib.javassist.ByteArrayClassPath */ public ClassPath appendClassPath(ClassPath cp){ return source.appendClassPath(cp); } /** * Appends a directory or a jar (or zip) file to the end of the * search path. * * @param pathname * the path name of the directory or jar file. * It must not end with a path separator ("/"). * If the path name ends with "/*", then all the * jar files matching the path name are appended. * * @return the appended class path. * @throws NotFoundException * if the jar file is not found. */ public ClassPath appendClassPath(String pathname) throws NotFoundException{ return source.appendClassPath(pathname); } /** * Detatches the ClassPath object from the search path. * The detached ClassPath object cannot be added * to the path again. */ public void removeClassPath(ClassPath cp){ source.removeClassPath(cp); } /** * Converts the given class to a java.lang.Class object. * Once this method is called, further modifications are not * allowed any more. * To load the class, this method uses the context class loader * of the current thread. It is obtained by calling * getClassLoader(). * *

* This behavior can be changed by subclassing the pool and changing * the getClassLoader() method. * If the program is running on some application * server, the context class loader might be inappropriate to load the * class. *

* *

* This method is provided for convenience. If you need more * complex functionality, you should write your own class loader. * *

* Warining: * This method should not be used in Java 11 or later. * Use {@link #toClass(CtClass,Class)}. *

* *

* Warining: * A Class object returned by this method may not * work with a security manager or a signed jar file because a * protection domain is not specified. *

* * @see #toClass(CtClass,Class) * @see #toClass(CtClass,Class,java.lang.ClassLoader,ProtectionDomain) * @see #getClassLoader() */ public Class toClass(CtClass clazz) throws CannotCompileException{ // Some subclasses of ClassPool may override toClass(CtClass,ClassLoader). // So we should call that method instead of toClass(.., ProtectionDomain). return toClass(clazz, getClassLoader()); } /** * Get the classloader for toClass(), getAnnotations() in * CtClass, etc. * *

* The default is the context class loader. * * @return the classloader for the pool * @see #toClass(CtClass) * @see CtClass#getAnnotations() */ public ClassLoader getClassLoader(){ return getContextClassLoader(); } /** * Obtains a class loader that seems appropriate to look up a class * by name. */ static ClassLoader getContextClassLoader(){ return Thread.currentThread().getContextClassLoader(); } /** * Converts the class to a java.lang.Class object. * Do not override this method any more at a subclass because * {@link #toClass(CtClass)} will never calls this method. * *

* Warining: A Class object returned by this method may not * work with a security manager or a signed jar file because a * protection domain is not specified. * * @deprecated Replaced by {@link #toClass(CtClass,Class,ClassLoader,ProtectionDomain)}. * A subclass of ClassPool that has been * overriding this method should be modified. It should override * {@link #toClass(CtClass,Class,ClassLoader,ProtectionDomain)}. */ @Deprecated public Class toClass(CtClass ct,ClassLoader loader) throws CannotCompileException{ return toClass(ct, null, loader, null); } /** * Converts the class to a java.lang.Class object. * Once this method is called, further modifications are not allowed * any more. * *

* The class file represented by the given CtClass is * loaded by the given class loader to construct a * java.lang.Class object. Since a private method * on the class loader is invoked through the reflection API, * the caller must have permissions to do that. *

* *

* An easy way to obtain ProtectionDomain object is * to call getProtectionDomain() * in java.lang.Class. It returns the domain that the * class belongs to. * *

* This method is provided for convenience. If you need more * complex functionality, you should write your own class loader. *

* * @param ct * the class converted into {@code java.lang.Class}. * @param loader * the class loader used to load this class. * For example, the loader returned by * getClassLoader() can be used * for this parameter. * @param domain * the protection domain for the class. * If it is null, the default domain created * by java.lang.ClassLoader is used. * * @see #getClassLoader() * @since 3.3 * @deprecated Replaced by {@link #toClass(CtClass,Class,ClassLoader,ProtectionDomain)}. */ @Deprecated public Class toClass(CtClass ct,ClassLoader loader,ProtectionDomain domain) throws CannotCompileException{ return toClass(ct, null, loader, domain); } /** * Converts the class to a java.lang.Class object. * Once this method is called, further modifications are not allowed * any more. * *

* When the JVM is Java 11 or later, this method loads the class * by using {@code java.lang.invoke.MethodHandles} with {@code neighbor}. * The other arguments {@code loader} and {@code domain} are not used; * so they can be null. *

* *

* Otherwise, or when {@code neighbor} is null, * the class file represented by the given CtClass is * loaded by the given class loader to construct a * java.lang.Class object. Since a private method * on the class loader is invoked through the reflection API, * the caller must have permissions to do that. * *

* An easy way to obtain ProtectionDomain object is * to call getProtectionDomain() * in java.lang.Class. It returns the domain that the * class belongs to. * *

* If your program is for only Java 9 or later, don't use this method. * Use {@link #toClass(CtClass,Class)} or * {@link #toClass(CtClass,java.lang.invoke.MethodHandles.Lookup)}. *

* * @param ct * the class converted into {@code java.lang.Class}. * @param neighbor * a class belonging to the same package that * the converted class belongs to. * It can be null. * @param loader * the class loader used to load this class. * For example, the loader returned by * getClassLoader() can be used * for this parameter. * @param domain * the protection domain for the class. * If it is null, the default domain created * by java.lang.ClassLoader is used. * * @see #getClassLoader() * @since 3.24 */ public Class toClass(CtClass ct,Class neighbor,ClassLoader loader,ProtectionDomain domain) throws CannotCompileException{ try{ return com.feilong.lib.javassist.util.proxy.DefineClassHelper.toClass(ct.getName(), neighbor, loader, domain, ct.toBytecode()); }catch (IOException e){ throw new CannotCompileException(e); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy