* <p>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 <code>invoke()</code> is called on the method handler
- * associated with the generated class. The calls of the methods from the interfaces
- * are also forwarded to 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.
*
* <p>For example, if the following code is executed,
*
* <ul><pre>
* ProxyFactory f = new ProxyFactory();
* f.setSuperclass(Foo.class);
- * 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.
- * }
- * };
* f.setFilter(new MethodFilter() {
* public boolean isHandled(Method m) {
* // ignore 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();
* ((ProxyObject)foo).setHandler(mi);
* </pre></ul>
* execute the following code:
*
* <ul><pre>
- * MethodHandler mi2 = ... ; // another handler
- * ((ProxyObject)foo).setHandler(mi2);
- * </pre></ul>
- *
- * <p>You can also specify the default method handler:
- *
- * <ul><pre>
- * ProxyFactory f2 = new ProxyFactory();
- * f2.setSuperclass(Foo.class);
- * f2.setHandler(mi); // set the default handler
- * Class c2 = f2.createClass();
+ * MethodHandler mi = ... ; // alternative handler
+ * ((ProxyObject)foo).setHandler(mi);
* </pre></ul>
*
- * <p>The default handler is implicitly attached to an instance of the generated class
- * <code>c2</code>. Calling <code>setHandler</code> on the instance is not necessary
- * unless another method handler must be attached to the instance.
- *
- * <p>The following code is an example of method handler. It does not execute
- * anything except invoking the original method:
+ * <p> 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:
*
* <ul><pre>
- * class SimpleHandler implements MethodHandler {
+ * class EmptyHandler implements MethodHandler {
* public Object invoke(Object self, Method m,
* Method proceed, Object[] args) throws Exception {
* return proceed.invoke(self, args);
* }
* </pre></ul>
*
+ * <p>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.
+ *
+ * <p>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 ProxyObject#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)}.
+ *
* <p>A proxy object generated by <code>ProxyFactory</code> is serializable
- * if its super class or interfaces implement a <code>java.io.Serializable</code>.
- * However, a serialized proxy object will not be compatible with future releases.
+ * if its super class or any of its interfaces implement <code>java.io.Serializable</code>.
+ * However, a serialized proxy object may not be compatible with future releases.
* The serialization support should be used for short-term storage or RMI.
*
+ * <p>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.
+ * <p>
+ * 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;
+ private MethodHandler handler; // retained for legacy usage
+ private List signatureMethods;
+ private byte[] signature;
+ private String classname;
+ private String superName;
+ private String packageName;
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
private static final String HOLDER = "_methods_";
private static final String HOLDER_TYPE = "[Ljava/lang/reflect/Method;";
- private static final String METHOD_FILTER_FIELD = "_method_filter";
+ 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_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 int SERIAL_VERSION_UID_VALUE = -1;
+
/**
* 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 boolean useCache = true;
+ public static volatile boolean useCache = true;
- private static WeakHashMap proxyCache = new WeakHashMap();
+ /**
+ * 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
+ */
/**
- * store details of a specific proxy created using a given method filter and handler
+ * test whether this factory uses the proxy cache
+ * @return true if this factory uses the proxy cache otherwise false
*/
- static class ProxyDetails {
- MethodFilter filter;
- MethodHandler handler;
- Class proxyClass;
+ public boolean isUseCache()
+ {
+ return factoryUseCache;
+ }
- ProxyDetails(MethodFilter filter, MethodHandler handler, Class proxyClass)
- {
- this.filter = filter;
- this.handler = handler;
- this.proxyClass = proxyClass;
+ /**
+ * 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;
}
/**
- * collect together details of all proxies associated with a given classloader which are constructed
- * from a given superclass and set of interfaces
+ * 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
*/
- static class ProxySet {
- String classes; // key constructed from super/interfaces names
- List proxyDetails; // hold details of all proxies with given super/interfaces
+ public boolean isUseWriteReplace()
+ {
+ return factoryWriteReplace;
+ }
- public ProxySet(String key)
- {
- classes = key;
- proxyDetails = new ArrayList();
- }
+ /**
+ * 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 ProxyObject. nothing else should.
+ return (ProxyObject.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 {
/**
- * retrieve an entry from the set with the given filter and handler
- * @param filter
- * @param handler
- * @return the proxy details or null if it is not found
+ * 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.
*/
- public ProxyDetails lookup(MethodFilter filter, MethodHandler handler)
- {
- Iterator iterator = proxyDetails.iterator();
- while (iterator.hasNext()) {
- ProxyDetails details = (ProxyDetails)iterator.next();
- if (details.filter == filter && details.handler == handler) {
- return details;
- }
- }
- return null;
- }
-
+ byte[] signature;
/**
- * add details of a new proxy to the set
- * @param details must not contain the same filter and handler as any existing entry in the set
+ * a hexadecimal string representation of the signature bit sequence. this string also forms part
+ * of the proxy class name.
*/
- public void add(ProxyDetails details)
- {
- proxyDetails.add(details);
- }
-
+ WeakReference proxyClass;
/**
- * remove details of an existing proxy from the set
- * @param details must be in the set
+ * 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
*/
- public void remove(ProxyDetails details)
- {
- proxyDetails.remove(details);
- }
+ boolean isUseWriteReplace;
- static String getKey(Class superClass, Class[] interfaces) {
- StringBuffer sbuf = new StringBuffer();
- if (superClass != null)
- sbuf.append(superClass.getName());
- sbuf.append(':');
- if (interfaces != null) {
- int len = interfaces.length;
- for (int i = 0; i < len; i++)
- sbuf.append(interfaces[i].getName()).append(',');
- }
-
- return sbuf.toString();
+ ProxyDetails(byte[] signature, Class proxyClass, boolean isUseWriteReplace)
+ {
+ this.signature = signature;
+ this.proxyClass = new WeakReference(proxyClass);
+ this.isUseWriteReplace = isUseWriteReplace;
}
}
interfaces = null;
methodFilter = null;
handler = null;
+ signature = null;
+ signatureMethods = null;
thisClass = null;
writeDirectory = null;
+ factoryUseCache = useCache;
+ factoryWriteReplace = useWriteReplace;
}
/**
*/
public void setSuperclass(Class clazz) {
superClass = clazz;
+ // force recompute of signature
+ signature = null;
}
/**
*/
public void setInterfaces(Class[] ifs) {
interfaces = ifs;
+ // force recompute of signature
+ signature = null;
}
/**
*/
public void setFilter(MethodFilter mf) {
methodFilter = mf;
+ // force recompute of signature
+ signature = null;
}
/**
- * Generates a proxy class.
+ * 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)
+ {
+ this.signature = signature;
+ return createClass1();
+ }
+
+ private Class createClass1() {
if (thisClass == null) {
ClassLoader cl = getClassLoader();
synchronized (proxyCache) {
- if (useCache)
+ if (factoryUseCache)
createClass2(cl);
else
createClass3(cl);
}
}
- return thisClass;
+ // 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 = ProxySet.getKey(superClass, interfaces);
- WeakReference reference;
- ProxySet set;
+ 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
*/
// synchronized (proxyCache) {
HashMap cacheForTheLoader = (HashMap)proxyCache.get(cl);
+ ProxyDetails details;
if (cacheForTheLoader == null) {
cacheForTheLoader = new HashMap();
proxyCache.put(cl, cacheForTheLoader);
}
- reference = (WeakReference)cacheForTheLoader.get(key);
- if (reference != null) {
- set = (ProxySet)reference.get();
- } else {
- set = null;
- }
- if (set == null) {
- set = new ProxySet(key);
- reference = new WeakReference(set);
- cacheForTheLoader.put(key, reference);
- }
- ProxyDetails details = set.lookup(methodFilter, handler);
- if (details == null) {
- createClass3(cl);
- details = new ProxyDetails(methodFilter, handler, thisClass);
- set.add(details);
- } else {
- thisClass = details.proxyClass;
+ 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(DEFAULT_INTERCEPTOR, handler);
- setField(METHOD_FILTER_FIELD, methodFilter);
+ 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);
}
}
- static MethodFilter getFilter(Class clazz) {
- return (MethodFilter)getField(clazz, METHOD_FILTER_FIELD);
- }
-
- static MethodHandler getHandler(Class clazz) {
- return (MethodHandler)getField(clazz, DEFAULT_INTERCEPTOR);
+ static byte[] getFilterSignature(Class clazz) {
+ return (byte[])getField(clazz, FILTER_SIGNATURE_FIELD);
}
private static Object getField(Class clazz, String fieldName) {
* 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 ProxyObject#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);
}
}
private ClassFile make() throws CannotCompileException {
- String superName, classname;
- if (interfaces == null)
- interfaces = new Class[0];
-
- if (superClass == null) {
- superClass = OBJECT_TYPE;
- superName = superClass.getName();
- classname = interfaces.length == 0 ? superName
- : interfaces[0].getName();
- }
- else {
- superName = superClass.getName();
- classname = superName;
- }
-
- if (Modifier.isFinal(superClass.getModifiers()))
- throw new CannotCompileException(superName + " is final");
-
- classname = makeProxyName(classname);
- if (classname.startsWith("java."))
- classname = "org.javassist.tmp." + classname;
-
ClassFile cf = new ClassFile(false, classname, superName);
cf.setAccessFlags(AccessFlag.PUBLIC);
setInterfaces(cf, interfaces);
ConstPool pool = cf.getConstPool();
- FieldInfo finfo = new FieldInfo(pool, DEFAULT_INTERCEPTOR, HANDLER_TYPE);
- finfo.setAccessFlags(AccessFlag.PUBLIC | AccessFlag.STATIC);
- cf.addField(finfo);
+ // 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);
- FieldInfo finfo3 = new FieldInfo(pool, METHOD_FILTER_FIELD,
- "Ljavassist/util/proxy/MethodFilter;");
+ // 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);
- HashMap allMethods = getMethods(superClass, interfaces);
- int size = allMethods.size();
+ // 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);
- int s = overrideMethods(cf, pool, classname, allMethods);
+ int s = overrideMethods(cf, pool, classname);
addMethodsHolder(cf, pool, classname, s);
addSetter(classname, cf, pool);
addGetter(classname, cf, pool);
- try {
- cf.addMethod(makeWriteReplace(pool));
- }
- catch (DuplicateMemberException e) {
- // writeReplace() is already declared in the super class/interfaces.
+ if (factoryWriteReplace) {
+ try {
+ cf.addMethod(makeWriteReplace(pool));
+ }
+ catch (DuplicateMemberException e) {
+ // writeReplace() is already declared in the super class/interfaces.
+ }
}
- thisClass = null;
+ thisClass = null;
return cf;
}
+ private void checkClassAndSuperName()
+ {
+ if (interfaces == null)
+ interfaces = new Class[0];
+
+ if (superClass == null) {
+ superClass = OBJECT_TYPE;
+ }
+ superName = superClass.getName();
+
+ if (Modifier.isFinal(superClass.getModifiers()))
+ throw new RuntimeException(superName + " is final");
+ packageName = getPackageName(superName);
+ if (packageName.startsWith("java."))
+ packageName = "org.javassist.tmp." + packageName;
+ }
+
+ private void allocateClassName()
+ {
+ classname = makeProxyName(superName);
+ if (classname.startsWith("java."))
+ classname = "org.javassist.tmp." + classname;
+ }
+
+ 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 computeSignature(MethodFilter filter) // throws CannotCompileException
+ {
+ checkClassAndSuperName();
+
+ HashMap allMethods = getMethods(superClass, interfaces);
+ signatureMethods = new ArrayList(allMethods.entrySet());
+ Collections.sort(signatureMethods, sorter);
+ 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, packageName, m) && filter.isHandled(m)) {
+ setBit(signature, idx);
+ }
+ }
+ }
+
+ 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) {
String setterIntf = ProxyObject.class.getName();
String[] list;
code.addIconst(size * 2);
code.addAnewarray("java.lang.reflect.Method");
code.addPutstatic(classname, HOLDER, HOLDER_TYPE);
+ // also need to set serial version uid
+ code.addLconst(-1L);
+ code.addPutstatic(classname, SERIAL_VERSION_UID_FIELD, SERIAL_VERSION_UID_TYPE);
code.addOpcode(Bytecode.RETURN);
minfo.setCodeAttribute(code.toCodeAttribute());
cf.addMethod(minfo);
cf.addMethod(minfo);
}
- private int overrideMethods(ClassFile cf, ConstPool cp, String className,
- HashMap allMethods)
+ private int overrideMethods(ClassFile cf, ConstPool cp, String className)
throws CannotCompileException
{
- String prefix = makeUniqueName("_d", allMethods);
- Set entries = allMethods.entrySet();
- Iterator it = entries.iterator();
+ String prefix = makeUniqueName("_d", signatureMethods);
+ Iterator it = signatureMethods.iterator();
int index = 0;
while (it.hasNext()) {
Map.Entry e = (Map.Entry)it.next();
Method meth = (Method)e.getValue();
int mod = meth.getModifiers();
if (!Modifier.isFinal(mod) && !Modifier.isStatic(mod)
- && isVisible(mod, className, meth))
+ && isVisible(mod, packageName, meth))
+ if (testBit(signature, index))
if (methodFilter == null || methodFilter.isHandled(meth))
- override(className, meth, prefix, index++,
+ override(className, meth, prefix, index,
keyToDesc(key), cf, cp);
+ index++;
}
return index;
ConstPool cp, String classname) throws CannotCompileException
{
Constructor[] cons = SecurityActions.getDeclaredConstructors(superClass);
+ // legacy: if we are not caching then we need to initialise the default handler
+ boolean doHandlerInit = !factoryUseCache;
for (int i = 0; i < cons.length; i++) {
Constructor c = cons[i];
int mod = c.getModifiers();
if (!Modifier.isFinal(mod) && !Modifier.isPrivate(mod)
- && isVisible(mod, classname, c)) {
- MethodInfo m = makeConstructor(thisClassName, c, cp, superClass);
+ && isVisible(mod, packageName, c)) {
+ MethodInfo m = makeConstructor(thisClassName, c, cp, superClass, doHandlerInit);
cf.addMethod(m);
}
}
}
- private static String makeUniqueName(String name, HashMap hash) {
- Set keys = hash.keySet();
- if (makeUniqueName0(name, keys.iterator()))
+ private static String makeUniqueName(String name, List sortedMethods) {
+ if (makeUniqueName0(name, sortedMethods.iterator()))
return name;
for (int i = 100; i < 999; i++) {
String s = name + i;
- if (makeUniqueName0(s, keys.iterator()))
+ if (makeUniqueName0(s, sortedMethods.iterator()))
return s;
}
private static boolean makeUniqueName0(String name, Iterator it) {
while (it.hasNext()) {
- String key = (String)it.next();
+ Map.Entry e = (Map.Entry)it.next();
+ String key = (String)e.getKey();
if (key.startsWith(name))
return false;
}
}
/**
- * Returns true if the method is visible from the class.
+ * Returns true if the method is visible from the package.
*
* @param mod the modifiers of the method.
*/
- private static boolean isVisible(int mod, String from, Member meth) {
+ private static boolean isVisible(int mod, String fromPackage, Member meth) {
if ((mod & Modifier.PRIVATE) != 0)
return false;
else if ((mod & (Modifier.PUBLIC | Modifier.PROTECTED)) != 0)
return true;
else {
- String p = getPackageName(from);
String q = getPackageName(meth.getDeclaringClass().getName());
- if (p == null)
+ if (fromPackage == null)
return q == null;
else
- return p.equals(q);
+ return fromPackage.equals(q);
}
}
}
private static MethodInfo makeConstructor(String thisClassName, Constructor cons,
- ConstPool cp, Class superClass) {
+ ConstPool cp, Class superClass, boolean doHandlerInit) {
String desc = RuntimeSupport.makeDescriptor(cons.getParameterTypes(),
Void.TYPE);
MethodInfo minfo = new MethodInfo(cp, "<init>", desc);
setThrows(minfo, cp, cons.getExceptionTypes());
Bytecode code = new Bytecode(cp, 0, 0);
- code.addAload(0);
- code.addGetstatic(thisClassName, DEFAULT_INTERCEPTOR, HANDLER_TYPE);
- code.addPutfield(thisClassName, HANDLER, HANDLER_TYPE);
- code.addGetstatic(thisClassName, DEFAULT_INTERCEPTOR, HANDLER_TYPE);
- code.addOpcode(Opcode.IFNONNULL);
- code.addIndex(10);
+ // legacy: if we are not using caching then we initialise the instance's handler
+ // from the class's static default interceptor and skip the next few instructions if
+ // it is non-null
+ if (doHandlerInit) {
+ code.addAload(0);
+ code.addGetstatic(thisClassName, DEFAULT_INTERCEPTOR, HANDLER_TYPE);
+ code.addPutfield(thisClassName, HANDLER, HANDLER_TYPE);
+ code.addGetstatic(thisClassName, DEFAULT_INTERCEPTOR, HANDLER_TYPE);
+ code.addOpcode(Opcode.IFNONNULL);
+ code.addIndex(10);
+ }
+ // if caching is enabled then we don't have a handler to initialise so this else branch will install
+ // the handler located in the static field of class RuntimeSupport.
code.addAload(0);
code.addGetstatic(NULL_INTERCEPTOR_HOLDER, DEFAULT_INTERCEPTOR, HANDLER_TYPE);
code.addPutfield(thisClassName, HANDLER, HANDLER_TYPE);
FactoryHelper.unwrapDesc[index]);
}
}
- else
+ else
code.addCheckcast(type.getName());
}