/* * 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 javassist.util.proxy; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Constructor; import java.lang.reflect.Member; import java.lang.reflect.Modifier; import java.security.ProtectionDomain; import java.util.*; import java.lang.ref.WeakReference; import javassist.CannotCompileException; import javassist.NotFoundException; import javassist.bytecode.*; /* * This class is implemented only with the lower-level API of Javassist. * This design decision is for maximizing performance. */ /** * Factory of dynamic proxy classes. * *
This factory generates a class that extends the given super class and implements
* the given interfaces. The calls of the methods inherited from the super class are
* forwarded and then invoke()
is called on the method handler
* associated with instances of the generated class. The calls of the methods from
* the interfaces are also forwarded to the method handler.
*
*
For example, if the following code is executed, * *
* ProxyFactory f = new ProxyFactory(); * f.setSuperclass(Foo.class); * f.setFilter(new MethodFilter() { * public boolean isHandled(Method m) { * // ignore finalize() * return !m.getName().equals("finalize"); * } * }); * Class c = f.createClass(); * MethodHandler mi = new MethodHandler() { * public Object invoke(Object self, Method m, Method proceed, * Object[] args) throws Throwable { * System.out.println("Name: " + m.getName()); * return proceed.invoke(self, args); // execute the original method. * } * }; * Foo foo = (Foo)c.newInstance(); * ((Proxy)foo).setHandler(mi); ** *
Here, Method
is java.lang.reflect.Method
.
Then, the following method call will be forwarded to MethodHandler
* mi
and prints a message before executing the originally called method
* bar()
in Foo
.
*
*
* foo.bar(); ** *
The last three lines of the code shown above can be replaced with a call to
* the helper method create
, which generates a proxy class, instantiates
* it, and sets the method handler of the instance:
*
*
* : * Foo foo = (Foo)f.create(new Class[0], new Object[0], mi); ** *
To change the method handler during runtime, * execute the following code: * *
* MethodHandler mi = ... ; // alternative handler * ((Proxy)foo).setHandler(mi); ** *
If setHandler is never called for a proxy instance then it will * employ the default handler which proceeds by invoking the original method. * The behaviour of the default handler is identical to the following * handler: * *
* class EmptyHandler implements MethodHandler { * public Object invoke(Object self, Method m, * Method proceed, Object[] args) throws Exception { * return proceed.invoke(self, args); * } * } ** *
A proxy factory caches and reuses proxy classes by default. It is possible to reset * this default globally by setting static field {@link ProxyFactory#useCache} to false. * Caching may also be configured for a specific factory by calling instance method * {@link ProxyFactory#setUseCache(boolean)}. It is strongly recommended that new clients * of class ProxyFactory enable caching. Failure to do so may lead to exhaustion of * the heap memory area used to store classes. * *
Caching is automatically disabled for any given proxy factory if deprecated instance * method {@link ProxyFactory#setHandler(MethodHandler)} is called. This method was * used to specify a default handler which newly created proxy classes should install * when they create their instances. It is only retained to provide backward compatibility * with previous releases of javassist. Unfortunately,this legacy behaviour makes caching * and reuse of proxy classes impossible. The current programming model expects javassist * clients to set the handler of a proxy instance explicitly by calling method * {@link Proxy#setHandler(MethodHandler)} as shown in the sample code above. New * clients are strongly recommended to use this model rather than calling * {@link ProxyFactory#setHandler(MethodHandler)}. * *
A proxy object generated by ProxyFactory
is serializable
* if its super class or any of its interfaces implement java.io.Serializable
.
* However, a serialized proxy object may not be compatible with future releases.
* The serialization support should be used for short-term storage or RMI.
*
*
For compatibility with older releases serialization of proxy objects is implemented by * adding a writeReplace method to the proxy class. This allows a proxy to be serialized * to a conventional {@link java.io.ObjectOutputStream} and deserialized from a corresponding * {@link java.io.ObjectInputStream}. However this method suffers from several problems, the most * notable one being that it fails to serialize state inherited from the proxy's superclass. *
* An alternative method of serializing proxy objects is available which fixes these problems. It
* requires inhibiting generation of the writeReplace method and instead using instances of
* {@link javassist.util.proxy.ProxyObjectOutputStream} and {@link javassist.util.proxy.ProxyObjectInputStream}
* (which are subclasses of {@link java.io.ObjectOutputStream} and {@link java.io.ObjectInputStream})
* to serialize and deserialize, respectively, the proxy. These streams recognise javassist proxies and ensure
* that they are serialized and deserialized without the need for the proxy class to implement special methods
* such as writeReplace. Generation of the writeReplace method can be disabled globally by setting static field
* {@link ProxyFactory#useWriteReplace} to false. Alternatively, it may be
* configured per factory by calling instance method {@link ProxyFactory#setUseWriteReplace(boolean)}.
*
* @see MethodHandler
* @since 3.1
* @author Muga Nishizawa
* @author Shigeru Chiba
* @author Andrew Dinn
*/
public class ProxyFactory {
private Class superClass;
private Class[] interfaces;
private MethodFilter methodFilter;
private MethodHandler handler; // retained for legacy usage
private List signatureMethods;
private boolean hasGetHandler;
private byte[] signature;
private String classname;
private String basename;
private String superName;
private Class thisClass;
/**
* per factory setting initialised from current setting for useCache but able to be reset before each create call
*/
private boolean factoryUseCache;
/**
* per factory setting initialised from current setting for useWriteReplace but able to be reset before each create call
*/
private boolean factoryWriteReplace;
/**
* If the value of this variable is not null, the class file of
* the generated proxy class is written under the directory specified
* by this variable. For example, if the value is
* "."
, then the class file is written under the current
* directory. This method is for debugging.
*
*
The default value is null.
*/
public String writeDirectory;
private static final Class OBJECT_TYPE = Object.class;
private static final String HOLDER = "_methods_";
private static final String HOLDER_TYPE = "[Ljava/lang/reflect/Method;";
private static final String FILTER_SIGNATURE_FIELD = "_filter_signature";
private static final String FILTER_SIGNATURE_TYPE = "[B";
private static final String HANDLER = "handler";
private static final String NULL_INTERCEPTOR_HOLDER = "javassist.util.proxy.RuntimeSupport";
private static final String DEFAULT_INTERCEPTOR = "default_interceptor";
private static final String HANDLER_TYPE
= 'L' + MethodHandler.class.getName().replace('.', '/') + ';';
private static final String HANDLER_SETTER = "setHandler";
private static final String HANDLER_SETTER_TYPE = "(" + HANDLER_TYPE + ")V";
private static final String HANDLER_GETTER = "getHandler";
private static final String HANDLER_GETTER_TYPE = "()" + HANDLER_TYPE;
private static final String SERIAL_VERSION_UID_FIELD = "serialVersionUID";
private static final String SERIAL_VERSION_UID_TYPE = "J";
private static final long SERIAL_VERSION_UID_VALUE = -1L;
/**
* If true, a generated proxy class is cached and it will be reused
* when generating the proxy class with the same properties is requested.
* The default value is true.
*
* Note that this value merely specifies the initial setting employed by any newly created
* proxy factory. The factory setting may be overwritten by calling factory instance method
* {@link #setUseCache(boolean)}
*
* @since 3.4
*/
public static volatile boolean useCache = true;
/**
* If true, a generated proxy class will implement method writeReplace enabling
* serialization of its proxies to a conventional ObjectOutputStream. this (default)
* setting retains the old javassist behaviour which has the advantage that it
* retains compatibility with older releases and requires no extra work on the part
* of the client performing the serialization. However, it has the disadvantage that
* state inherited from the superclasses of the proxy is lost during serialization.
* if false then serialization/deserialization of the proxy instances will preserve
* all fields. However, serialization must be performed via a {@link ProxyObjectOutputStream}
* and deserialization must be via {@link ProxyObjectInputStream}. Any attempt to serialize
* proxies whose class was created with useWriteReplace set to false via a normal
* {@link java.io.ObjectOutputStream} will fail.
*
* Note that this value merely specifies the initial setting employed by any newly created
* proxy factory. The factory setting may be overwritten by calling factory instance method
* {@link #setUseWriteReplace(boolean)}
*
* @since 3.4
*/
public static volatile boolean useWriteReplace = true;
/*
* methods allowing individual factory settings for factoryUseCache and factoryWriteReplace to be reset
*/
/**
* test whether this factory uses the proxy cache
* @return true if this factory uses the proxy cache otherwise false
*/
public boolean isUseCache()
{
return factoryUseCache;
}
/**
* configure whether this factory should use the proxy cache
* @param useCache true if this factory should use the proxy cache and false if it should not use the cache
* @throws RuntimeException if a default interceptor has been set for the factory
*/
public void setUseCache(boolean useCache)
{
// we cannot allow caching to be used if the factory is configured to install a default interceptor
// field into generated classes
if (handler != null && useCache) {
throw new RuntimeException("caching cannot be enabled if the factory default interceptor has been set");
}
factoryUseCache = useCache;
}
/**
* test whether this factory installs a writeReplace method in created classes
* @return true if this factory installs a writeReplace method in created classes otherwise false
*/
public boolean isUseWriteReplace()
{
return factoryWriteReplace;
}
/**
* configure whether this factory should add a writeReplace method to created classes
* @param useWriteReplace true if this factory should add a writeReplace method to created classes and false if it
* should not add a writeReplace method
*/
public void setUseWriteReplace(boolean useWriteReplace)
{
factoryWriteReplace = useWriteReplace;
}
private static WeakHashMap proxyCache = new WeakHashMap();
/**
* determine if a class is a javassist proxy class
* @param cl
* @return true if the class is a javassist proxy class otherwise false
*/
public static boolean isProxyClass(Class cl)
{
// all proxies implement Proxy or ProxyObject. nothing else should.
return (Proxy.class.isAssignableFrom(cl));
}
/**
* used to store details of a specific proxy class in the second tier of the proxy cache. this entry
* will be located in a hashmap keyed by the unique identifying name of the proxy class. the hashmap is
* located in a weak hashmap keyed by the classloader common to all proxy classes in the second tier map.
*/
static class ProxyDetails {
/**
* the unique signature of any method filter whose behaviour will be met by this class. each bit in
* the byte array is set if the filter redirects the corresponding super or interface method and clear
* if it does not redirect it.
*/
byte[] signature;
/**
* a hexadecimal string representation of the signature bit sequence. this string also forms part
* of the proxy class name.
*/
WeakReference proxyClass;
/**
* a flag which is true this class employs writeReplace to perform serialization of its instances
* and false if serialization must employ of a ProxyObjectOutputStream and ProxyObjectInputStream
*/
boolean isUseWriteReplace;
ProxyDetails(byte[] signature, Class proxyClass, boolean isUseWriteReplace)
{
this.signature = signature;
this.proxyClass = new WeakReference(proxyClass);
this.isUseWriteReplace = isUseWriteReplace;
}
}
/**
* Constructs a factory of proxy class.
*/
public ProxyFactory() {
superClass = null;
interfaces = null;
methodFilter = null;
handler = null;
signature = null;
signatureMethods = null;
hasGetHandler = false;
thisClass = null;
writeDirectory = null;
factoryUseCache = useCache;
factoryWriteReplace = useWriteReplace;
}
/**
* Sets the super class of a proxy class.
*/
public void setSuperclass(Class clazz) {
superClass = clazz;
// force recompute of signature
signature = null;
}
/**
* Obtains the super class set by setSuperclass()
.
*
* @since 3.4
*/
public Class getSuperclass() { return superClass; }
/**
* Sets the interfaces of a proxy class.
*/
public void setInterfaces(Class[] ifs) {
interfaces = ifs;
// force recompute of signature
signature = null;
}
/**
* Obtains the interfaces set by setInterfaces
.
*
* @since 3.4
*/
public Class[] getInterfaces() { return interfaces; }
/**
* Sets a filter that selects the methods that will be controlled by a handler.
*/
public void setFilter(MethodFilter mf) {
methodFilter = mf;
// force recompute of signature
signature = null;
}
/**
* Generates a proxy class using the current filter.
*/
public Class createClass() {
if (signature == null) {
computeSignature(methodFilter);
}
return createClass1();
}
/**
* Generates a proxy class using the supplied filter.
*/
public Class createClass(MethodFilter filter) {
computeSignature(filter);
return createClass1();
}
/**
* Generates a proxy class with a specific signature.
* access is package local so ProxyObjectInputStream can use this
* @param signature
* @return
*/
Class createClass(byte[] signature)
{
installSignature(signature);
return createClass1();
}
private Class createClass1() {
if (thisClass == null) {
ClassLoader cl = getClassLoader();
synchronized (proxyCache) {
if (factoryUseCache)
createClass2(cl);
else
createClass3(cl);
}
}
// don't retain any unwanted references
Class result = thisClass;
thisClass = null;
return result;
}
private static char[] hexDigits =
{ '0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
public String getKey(Class superClass, Class[] interfaces, byte[] signature, boolean useWriteReplace)
{
StringBuffer sbuf = new StringBuffer();
if (superClass != null){
sbuf.append(superClass.getName());
}
sbuf.append(":");
for (int i = 0; i < interfaces.length; i++) {
sbuf.append(interfaces[i].getName());
sbuf.append(":");
}
for (int i = 0; i < signature.length; i++) {
byte b = signature[i];
int lo = b & 0xf;
int hi = (b >> 4) & 0xf;
sbuf.append(hexDigits[lo]);
sbuf.append(hexDigits[hi]);
}
if (useWriteReplace) {
sbuf.append(":w");
}
return sbuf.toString();
}
private void createClass2(ClassLoader cl) {
String key = getKey(superClass, interfaces, signature, factoryWriteReplace);
/*
* Excessive concurrency causes a large memory footprint and slows the
* execution speed down (with JDK 1.5). Thus, we use a jumbo lock for
* reducing concrrency.
*/
// synchronized (proxyCache) {
HashMap cacheForTheLoader = (HashMap)proxyCache.get(cl);
ProxyDetails details;
if (cacheForTheLoader == null) {
cacheForTheLoader = new HashMap();
proxyCache.put(cl, cacheForTheLoader);
}
details = (ProxyDetails)cacheForTheLoader.get(key);
if (details != null) {
WeakReference reference = details.proxyClass;
thisClass = (Class)reference.get();
if (thisClass != null) {
return;
}
}
createClass3(cl);
details = new ProxyDetails(signature, thisClass, factoryWriteReplace);
cacheForTheLoader.put(key, details);
// }
}
private void createClass3(ClassLoader cl) {
// we need a new class so we need a new class name
allocateClassName();
try {
ClassFile cf = make();
if (writeDirectory != null)
FactoryHelper.writeFile(cf, writeDirectory);
thisClass = FactoryHelper.toClass(cf, cl, getDomain());
setField(FILTER_SIGNATURE_FIELD, signature);
// legacy behaviour : we only set the default interceptor static field if we are not using the cache
if (!factoryUseCache) {
setField(DEFAULT_INTERCEPTOR, handler);
}
}
catch (CannotCompileException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
private void setField(String fieldName, Object value) {
if (thisClass != null && value != null)
try {
Field f = thisClass.getField(fieldName);
SecurityActions.setAccessible(f, true);
f.set(null, value);
SecurityActions.setAccessible(f, false);
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
static byte[] getFilterSignature(Class clazz) {
return (byte[])getField(clazz, FILTER_SIGNATURE_FIELD);
}
private static Object getField(Class clazz, String fieldName) {
try {
Field f = clazz.getField(fieldName);
f.setAccessible(true);
Object value = f.get(null);
f.setAccessible(false);
return value;
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* Obtains the method handler of the given proxy object.
*
* @param p a proxy object.
* @return the method handler.
* @since 3.16
*/
public static MethodHandler getHandler(Proxy p) {
try {
Field f = p.getClass().getDeclaredField(HANDLER);
f.setAccessible(true);
Object value = f.get(p);
f.setAccessible(false);
return (MethodHandler)value;
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* A provider of class loaders.
*
* @see #classLoaderProvider
* @since 3.4
*/
public static interface ClassLoaderProvider {
/**
* Returns a class loader.
*
* @param pf a proxy factory that is going to obtain a class loader.
*/
public ClassLoader get(ProxyFactory pf);
}
/**
* A provider used by createClass()
for obtaining
* a class loader.
* get()
on this ClassLoaderProvider
object
* is called to obtain a class loader.
*
*
The value of this field can be updated for changing the default * implementation. * *
Example: *
* ProxyFactory.classLoaderProvider = new ProxyFactory.ClassLoaderProvider() { * public ClassLoader get(ProxyFactory pf) { * return Thread.currentThread().getContextClassLoader(); * } * }; ** * @since 3.4 */ public static ClassLoaderProvider classLoaderProvider = new ClassLoaderProvider() { public ClassLoader get(ProxyFactory pf) { return pf.getClassLoader0(); } }; protected ClassLoader getClassLoader() { return classLoaderProvider.get(this); } protected ClassLoader getClassLoader0() { ClassLoader loader = null; if (superClass != null && !superClass.getName().equals("java.lang.Object")) loader = superClass.getClassLoader(); else if (interfaces != null && interfaces.length > 0) loader = interfaces[0].getClassLoader(); if (loader == null) { loader = getClass().getClassLoader(); // In case javassist is in the endorsed dir if (loader == null) { loader = Thread.currentThread().getContextClassLoader(); if (loader == null) loader = ClassLoader.getSystemClassLoader(); } } return loader; } protected ProtectionDomain getDomain() { Class clazz; if (superClass != null && !superClass.getName().equals("java.lang.Object")) clazz = superClass; else if (interfaces != null && interfaces.length > 0) clazz = interfaces[0]; else clazz = this.getClass(); return clazz.getProtectionDomain(); } /** * Creates a proxy class and returns an instance of that class. * * @param paramTypes parameter types for a constructor. * @param args arguments passed to a constructor. * @param mh the method handler for the proxy class. * @since 3.4 */ public Object create(Class[] paramTypes, Object[] args, MethodHandler mh) throws NoSuchMethodException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException { Object obj = create(paramTypes, args); ((Proxy)obj).setHandler(mh); return obj; } /** * Creates a proxy class and returns an instance of that class. * * @param paramTypes parameter types for a constructor. * @param args arguments passed to a constructor. */ public Object create(Class[] paramTypes, Object[] args) throws NoSuchMethodException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException { Class c = createClass(); Constructor cons = c.getConstructor(paramTypes); return cons.newInstance(args); } /** * Sets the default invocation handler. This invocation handler is shared * among all the instances of a proxy class unless another is explicitly * specified. * @deprecated since 3.12 * use of this method is incompatible with proxy class caching. * instead clients should call method {@link Proxy#setHandler(MethodHandler)} to set the handler * for each newly created proxy instance. * calling this method will automatically disable caching of classes created by the proxy factory. */ public void setHandler(MethodHandler mi) { // if we were using the cache and the handler is non-null then we must stop caching if (factoryUseCache && mi != null) { factoryUseCache = false; // clear any currently held class so we don't try to reuse it or set its handler field thisClass = null; } handler = mi; // this retains the behaviour of the old code which resets any class we were holding on to // this is probably not what is wanted setField(DEFAULT_INTERCEPTOR, handler); } /** * A unique class name generator. */ public static interface UniqueName { /** * Returns a unique class name. * * @param classname the super class name of the proxy class. */ String get(String classname); } /** * A unique class name generator. * Replacing this generator changes the algorithm to generate a * unique name. The
get
method does not have to be
* a synchronized
method since the access to this field
* is mutually exclusive and thus thread safe.
*/
public static UniqueName nameGenerator = new UniqueName() {
private final String sep = "_$$_jvst" + Integer.toHexString(this.hashCode() & 0xfff) + "_";
private int counter = 0;
public String get(String classname) {
return classname + sep + Integer.toHexString(counter++);
}
};
private static String makeProxyName(String classname) {
synchronized (nameGenerator) {
return nameGenerator.get(classname);
}
}
private ClassFile make() throws CannotCompileException {
ClassFile cf = new ClassFile(false, classname, superName);
cf.setAccessFlags(AccessFlag.PUBLIC);
setInterfaces(cf, interfaces, hasGetHandler ? Proxy.class : ProxyObject.class);
ConstPool pool = cf.getConstPool();
// legacy: we only add the static field for the default interceptor if caching is disabled
if (!factoryUseCache) {
FieldInfo finfo = new FieldInfo(pool, DEFAULT_INTERCEPTOR, HANDLER_TYPE);
finfo.setAccessFlags(AccessFlag.PUBLIC | AccessFlag.STATIC);
cf.addField(finfo);
}
// handler is per instance
FieldInfo finfo2 = new FieldInfo(pool, HANDLER, HANDLER_TYPE);
finfo2.setAccessFlags(AccessFlag.PRIVATE);
cf.addField(finfo2);
// filter signature is per class
FieldInfo finfo3 = new FieldInfo(pool, FILTER_SIGNATURE_FIELD, FILTER_SIGNATURE_TYPE);
finfo3.setAccessFlags(AccessFlag.PUBLIC | AccessFlag.STATIC);
cf.addField(finfo3);
// the proxy class serial uid must always be a fixed value
FieldInfo finfo4 = new FieldInfo(pool, SERIAL_VERSION_UID_FIELD, SERIAL_VERSION_UID_TYPE);
finfo4.setAccessFlags(AccessFlag.PUBLIC | AccessFlag.STATIC| AccessFlag.FINAL);
cf.addField(finfo4);
// HashMap allMethods = getMethods(superClass, interfaces);
// int size = allMethods.size();
makeConstructors(classname, cf, pool, classname);
ArrayList forwarders = new ArrayList();
int s = overrideMethods(cf, pool, classname, forwarders);
addClassInitializer(cf, pool, classname, s, forwarders);
addSetter(classname, cf, pool);
if (!hasGetHandler)
addGetter(classname, cf, pool);
if (factoryWriteReplace) {
try {
cf.addMethod(makeWriteReplace(pool));
}
catch (DuplicateMemberException e) {
// writeReplace() is already declared in the super class/interfaces.
}
}
thisClass = null;
return cf;
}
private void checkClassAndSuperName() {
if (interfaces == null)
interfaces = new Class[0];
if (superClass == null) {
superClass = OBJECT_TYPE;
superName = superClass.getName();
basename = interfaces.length == 0 ? superName
: interfaces[0].getName();
} else {
superName = superClass.getName();
basename = superName;
}
if (Modifier.isFinal(superClass.getModifiers()))
throw new RuntimeException(superName + " is final");
if (basename.startsWith("java."))
basename = "org.javassist.tmp." + basename;
}
private void allocateClassName() {
classname = makeProxyName(basename);
}
private static Comparator sorter = new Comparator() {
public int compare(Object o1, Object o2) {
Map.Entry e1 = (Map.Entry)o1;
Map.Entry e2 = (Map.Entry)o2;
String key1 = (String)e1.getKey();
String key2 = (String)e2.getKey();
return key1.compareTo(key2);
}
};
private void makeSortedMethodList() {
checkClassAndSuperName();
hasGetHandler = false; // getMethods() may set this to true.
HashMap allMethods = getMethods(superClass, interfaces);
signatureMethods = new ArrayList(allMethods.entrySet());
Collections.sort(signatureMethods, sorter);
}
private void computeSignature(MethodFilter filter) // throws CannotCompileException
{
makeSortedMethodList();
int l = signatureMethods.size();
int maxBytes = ((l + 7) >> 3);
signature = new byte[maxBytes];
for (int idx = 0; idx < l; idx++)
{
Map.Entry e = (Map.Entry)signatureMethods.get(idx);
Method m = (Method)e.getValue();
int mod = m.getModifiers();
if (!Modifier.isFinal(mod) && !Modifier.isStatic(mod)
&& isVisible(mod, basename, m) && (filter == null || filter.isHandled(m))) {
setBit(signature, idx);
}
}
}
private void installSignature(byte[] signature) // throws CannotCompileException
{
makeSortedMethodList();
int l = signatureMethods.size();
int maxBytes = ((l + 7) >> 3);
if (signature.length != maxBytes) {
throw new RuntimeException("invalid filter signature length for deserialized proxy class");
}
this.signature = signature;
}
private boolean testBit(byte[] signature, int idx) {
int byteIdx = idx >> 3;
if (byteIdx > signature.length) {
return false;
} else {
int bitIdx = idx & 0x7;
int mask = 0x1 << bitIdx;
int sigByte = signature[byteIdx];
return ((sigByte & mask) != 0);
}
}
private void setBit(byte[] signature, int idx) {
int byteIdx = idx >> 3;
if (byteIdx < signature.length) {
int bitIdx = idx & 0x7;
int mask = 0x1 << bitIdx;
int sigByte = signature[byteIdx];
signature[byteIdx] = (byte)(sigByte | mask);
}
}
private static void setInterfaces(ClassFile cf, Class[] interfaces, Class proxyClass) {
String setterIntf = proxyClass.getName();
String[] list;
if (interfaces == null || interfaces.length == 0)
list = new String[] { setterIntf };
else {
list = new String[interfaces.length + 1];
for (int i = 0; i < interfaces.length; i++)
list[i] = interfaces[i].getName();
list[interfaces.length] = setterIntf;
}
cf.setInterfaces(list);
}
private static void addClassInitializer(ClassFile cf, ConstPool cp,
String classname, int size, ArrayList forwarders)
throws CannotCompileException
{
FieldInfo finfo = new FieldInfo(cp, HOLDER, HOLDER_TYPE);
finfo.setAccessFlags(AccessFlag.PRIVATE | AccessFlag.STATIC);
cf.addField(finfo);
MethodInfo minfo = new MethodInfo(cp, "