Permalink
Browse files

Change the way cached methods are implemented, so the same Method obj…

…ects can be re-used, and there are no reflection calls nessesarry in <clinit>
  • Loading branch information...
1 parent 9f1a2ea commit 0f366a454cf8fd092e6baa174a26b41b7ef3a039 @stuartwdouglas stuartwdouglas committed Aug 8, 2011
@@ -24,20 +24,20 @@
import org.jboss.classfilewriter.AccessFlag;
import org.jboss.classfilewriter.ClassMethod;
-import org.jboss.classfilewriter.code.BranchEnd;
import org.jboss.classfilewriter.code.CodeAttribute;
-import org.jboss.classfilewriter.code.CodeLocation;
-import org.jboss.classfilewriter.util.DescriptorUtils;
+import org.jboss.invocation.proxy.classloading.MethodStore;
+import org.jboss.invocation.proxy.reflection.ReflectionMetadataSource;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.ProtectionDomain;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
-import java.util.concurrent.atomic.AtomicInteger;
/**
* A subclass factory specializing in proxy generation.
@@ -50,15 +50,13 @@
private static final String METHOD_FIELD_DESCRIPTOR = "Ljava/lang/reflect/Method;";
- private final Map<Method, String> methodIdentifiers = new HashMap<Method, String>();
+ private final Map<Method, Integer> methodIdentifiers = new HashMap<Method, Integer>();
private int identifierCount = 0;
private ClassMethod staticConstructor;
- private volatile Method[] cachedMethods;
-
- private static final AtomicInteger count = new AtomicInteger();
+ private final List<Method> cachedMethods = new ArrayList<Method>(0);
/**
@@ -71,14 +69,15 @@
*/
protected AbstractProxyFactory(String className, Class<T> superClass, ClassLoader classLoader,
ProtectionDomain protectionDomain, final ReflectionMetadataSource reflectionMetadataSource) {
- super(className, superClass, classLoader, protectionDomain,reflectionMetadataSource);
+ super(className, superClass, classLoader, protectionDomain, reflectionMetadataSource);
staticConstructor = classFile.addMethod(AccessFlag.of(AccessFlag.PUBLIC, AccessFlag.STATIC), "<clinit>", "V");
}
/**
* This method must be called by subclasses after they have finished generating the class.
*/
protected void finalizeStaticConstructor() {
+ setupCachedProxyFields();
staticConstructor.getCodeAttribute().returnInstruction();
}
@@ -88,7 +87,54 @@ protected void finalizeStaticConstructor() {
@Override
public void afterClassLoad(Class<?> clazz) {
super.afterClassLoad(clazz);
- cachedMethods = AccessController.doPrivileged(new CachedMethodGetter());
+ //we create a new instance of the proxy class
+ //this forces <clinit> to be run, while the correct ThreadLocal is set
+ //if we do not run this then <clinit> may be run later, perhaps even in
+ //another thread
+ try {
+ clazz.newInstance();
+ } catch (InstantiationException e) {
+ throw new RuntimeException(e);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ MethodStore.METHODS.remove();
+ }
+
+ private void setupCachedProxyFields() {
+ cachedMethods.addAll(methodIdentifiers.keySet());
+
+ //set the methods to be accessible
+ AccessController.doPrivileged(new PrivilegedAction<Object>() {
+ @Override
+ public Object run() {
+ for(Method method : cachedMethods) {
+ method.setAccessible(true);
+ }
+ return null;
+ }
+ });
+
+ //store the Method objects in a thread local, so that
+ //the proxies <clinit> method can access them
+ //this removes the need for reflection in the proxy <clinit> method
+ final Method[] methods = new Method[identifierCount];
+ for (Map.Entry<Method, Integer> entry : methodIdentifiers.entrySet()) {
+ methods[entry.getValue()] = entry.getKey();
+ }
+ MethodStore.METHODS.set(methods);
+
+ //add the bytecode to load the cached fields in the static constructor
+ CodeAttribute ca = staticConstructor.getCodeAttribute();
+ ca.getstatic(MethodStore.class.getName(), "METHODS", "Ljava/lang/ThreadLocal;");
+ ca.invokevirtual(ThreadLocal.class.getName(), "get", "()Ljava/lang/Object;");
+ ca.checkcast("[Ljava/lang/reflect/Method;");
+ for(int i = 0; i < identifierCount; ++i) {
+ ca.dup();
+ ca.ldc(i);
+ ca.aaload();
+ ca.putstatic(getClassName(), METHOD_FIELD_PREFIX + i, METHOD_FIELD_DESCRIPTOR);
+ }
}
/**
@@ -97,7 +143,7 @@ public void afterClassLoad(Class<?> clazz) {
*
* @return The cached methods
*/
- public Method[] getCachedMethods() {
+ public List<Method> getCachedMethods() {
defineClass();
return cachedMethods;
}
@@ -126,84 +172,11 @@ protected void loadMethodIdentifier(Method methodToLoad, ClassMethod method) {
int identifierNo = identifierCount++;
String fieldName = METHOD_FIELD_PREFIX + identifierNo;
classFile.addField(AccessFlag.PRIVATE | AccessFlag.STATIC, fieldName, Method.class);
- methodIdentifiers.put(methodToLoad, fieldName);
- // we need to create the method in the static constructor
- CodeAttribute ca = staticConstructor.getCodeAttribute();
- // we need to call getDeclaredMethods and then iterate
- ca.loadClass(methodToLoad.getDeclaringClass().getName());
- ca.invokevirtual("java.lang.Class", "getDeclaredMethods", "()[Ljava/lang/reflect/Method;");
- ca.dup();
- ca.arraylength();
- ca.dup();
- ca.istore(0);
- ca.aconstNull();
- ca.astore(1);
- ca.aconstNull();
- ca.astore(2);
- ca.aconstNull();
- ca.astore(3);
- // so here we have the array index on top of the stack, followed by the array
- CodeLocation loopBegin = ca.mark();
- BranchEnd loopEnd = ca.ifeq();
- ca.dup();
- ca.iinc(0, -1);
- ca.iload(0); // load the array index into the stack
- ca.dupX1(); // index, array, index, array
- ca.aaload();
- ca.checkcast("java.lang.reflect.Method");
- ca.dup();
- ca.astore(2); // Method, index, array
- // compare method names
- ca.invokevirtual("java.lang.reflect.Method", "getName", "()Ljava/lang/String;");
- ca.ldc(methodToLoad.getName());
- ca.invokevirtual("java.lang.Object", "equals", "(Ljava/lang/Object;)Z"); // int,index,array
- ca.ifEq(loopBegin);
- // compare return types
- ca.aload(2);
- ca.invokevirtual("java.lang.reflect.Method", "getReturnType", "()Ljava/lang/Class;");
- ca.loadType(DescriptorUtils.makeDescriptor(methodToLoad.getReturnType()));
- ca.invokevirtual("java.lang.Object", "equals", "(Ljava/lang/Object;)Z"); // int,index,array
- ca.ifEq(loopBegin);
- // load the method parameters
- Class<?>[] parameters = methodToLoad.getParameterTypes();
- ca.aload(2);
- ca.invokevirtual("java.lang.reflect.Method", "getParameterTypes", "()[Ljava/lang/Class;");
- ca.dup();
- ca.astore(3);
- ca.arraylength();
- ca.iconst(parameters.length);
- ca.ifIcmpne(loopBegin); // compare parameter array length
-
- for (int i = 0; i < parameters.length; ++i) {
- ca.aload(3);
- ca.iconst(i);
- ca.aaload();
- ca.loadType(DescriptorUtils.makeDescriptor(parameters[i]));
- ca.invokevirtual("java.lang.Object", "equals", "(Ljava/lang/Object;)Z"); // int,index,array
- ca.ifEq(loopBegin);
- }
- ca.pop();
-
- BranchEnd gotoEnd = ca.gotoInstruction(); // we have found the method, goto the pointwhere we write it to a static
- // field
-
- // throw runtime exception as we could not find the method.
- // this will only happen if the proxy isloaded into the wrong classloader
- ca.branchEnd(loopEnd);
- ca.newInstruction("java.lang.RuntimeException");
- ca.dup();
- ca.ldc("Could not find method " + methodToLoad);
- ca.invokespecial("java.lang.RuntimeException", "<init>", "(Ljava/lang/String;)V");
- ca.athrow();
- ca.branchEnd(gotoEnd);
- ca.pop();
- ca.aload(2);
- ca.checkcast("java.lang.reflect.Method");
- ca.putstatic(getClassName(), fieldName, METHOD_FIELD_DESCRIPTOR);
+ methodIdentifiers.put(methodToLoad, identifierNo);
}
- String fieldName = methodIdentifiers.get(methodToLoad);
- method.getCodeAttribute().getstatic(getClassName(), fieldName, METHOD_FIELD_DESCRIPTOR);
+ final Integer fieldNo = methodIdentifiers.get(methodToLoad);
+ method.getCodeAttribute().getstatic(getClassName(), METHOD_FIELD_PREFIX + fieldNo, METHOD_FIELD_DESCRIPTOR);
}
/**
@@ -25,13 +25,16 @@
import org.jboss.classfilewriter.AccessFlag;
import org.jboss.classfilewriter.ClassMethod;
import org.jboss.classfilewriter.util.DescriptorUtils;
+import org.jboss.invocation.proxy.reflection.ClassMetadataSource;
+import org.jboss.invocation.proxy.reflection.ReflectionMetadataSource;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.security.ProtectionDomain;
import java.util.Collections;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.Set;
/**
@@ -155,15 +158,20 @@ protected void cleanup() {
* @param override the method body creator to use
*/
protected void overridePublicMethods(MethodBodyCreator override) {
- ClassMetadataSource data = reflectionMetadataSource.getClassMetadata(getSuperClass());
- for (Method method : data.getMethods()) {
- MethodIdentifier identifier = MethodIdentifier.getIdentifierForMethod(method);
- if (Modifier.isFinal(method.getModifiers())) {
- continue;
- }
- if (!SKIP_BY_DEFAULT.contains(identifier)) {
- overrideMethod(method, identifier, override);
+ Class<?> c = getSuperClass();
+ while (c != null) {
+ ClassMetadataSource data = reflectionMetadataSource.getClassMetadata(c);
+
+ for (Method method : data.getDeclaredMethods()) {
+ MethodIdentifier identifier = MethodIdentifier.getIdentifierForMethod(method);
+ if (!Modifier.isPublic(method.getModifiers()) || Modifier.isFinal(method.getModifiers())) {
+ continue;
+ }
+ if (!SKIP_BY_DEFAULT.contains(identifier)) {
+ overrideMethod(method, identifier, override);
+ }
}
+ c = c.getSuperclass();
}
}
@@ -224,7 +232,7 @@ protected boolean overrideEquals() {
*/
protected boolean overrideEquals(MethodBodyCreator creator) {
Method equals = null;
- ClassMetadataSource data = reflectionMetadataSource.getClassMetadata(getSuperClass());
+ ClassMetadataSource data = reflectionMetadataSource.getClassMetadata(Object.class);
try {
equals = data.getMethod("equals", Boolean.TYPE, Object.class);
} catch (Exception e) {
@@ -251,7 +259,7 @@ protected boolean overrideHashcode() {
protected boolean overrideHashcode(MethodBodyCreator creator) {
Method hashCode = null;
- ClassMetadataSource data = reflectionMetadataSource.getClassMetadata(getSuperClass());
+ ClassMetadataSource data = reflectionMetadataSource.getClassMetadata(Object.class);
try {
hashCode = data.getMethod("hashCode", Integer.TYPE);
} catch (Exception e) {
@@ -277,7 +285,7 @@ protected boolean overrideToString() {
*/
protected boolean overrideToString(MethodBodyCreator creator) {
Method toString = null;
- ClassMetadataSource data = reflectionMetadataSource.getClassMetadata(getSuperClass());
+ final ClassMetadataSource data = reflectionMetadataSource.getClassMetadata(Object.class);
try {
toString = data.getMethod("toString", String.class);
} catch (Exception e) {
@@ -302,8 +310,9 @@ protected boolean overrideFinalize() {
*/
protected boolean overrideFinalize(MethodBodyCreator creator) {
Method finalize = null;
+ final ClassMetadataSource data = reflectionMetadataSource.getClassMetadata(Object.class);
try {
- finalize = Object.class.getDeclaredMethod("finalize");
+ finalize = data.getMethod("finalize", void.class);
} catch (Exception e) {
throw new RuntimeException(e);
}
@@ -332,9 +341,27 @@ protected boolean addInterface(MethodBodyCreator override, Class<?> interfaceCla
}
interfaces.add(interfaceClass);
classFile.addInterface(interfaceClass.getName());
- ClassMetadataSource data = reflectionMetadataSource.getClassMetadata(interfaceClass);
- for (Method method : data.getMethods()) {
- overrideMethod(method, MethodIdentifier.getIdentifierForMethod(method), override);
+ final Set<Class<?>> interfaces = new HashSet<Class<?>>();
+ final Set<Class<?>> toProcess = new HashSet<Class<?>>();
+ toProcess.add(interfaceClass);
+
+ //walk the interface hierarchy and get all methods
+ while(!toProcess.isEmpty()) {
+ Iterator<Class<?>> it = toProcess.iterator();
+ final Class<?> c = it.next();
+ it.remove();
+ interfaces.add(c);
+ for(Class<?> i : c.getInterfaces()) {
+ if(!interfaces.contains(i)) {
+ toProcess.add(i);
+ }
+ }
+ }
+ for(final Class<?> c : interfaces) {
+ ClassMetadataSource data = reflectionMetadataSource.getClassMetadata(c);
+ for (Method method : data.getDeclaredMethods()) {
+ overrideMethod(method, MethodIdentifier.getIdentifierForMethod(method), override);
+ }
}
return true;
}
@@ -21,6 +21,9 @@
*/
package org.jboss.invocation.proxy;
+import org.jboss.invocation.proxy.reflection.DefaultReflectionMetadataSource;
+import org.jboss.invocation.proxy.reflection.ReflectionMetadataSource;
+
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.Collections;
@@ -0,0 +1,33 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2010, Red Hat Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.invocation.proxy.classloading;
+
+import java.lang.reflect.Method;
+
+/**
+ * @author Stuart Douglas
+ */
+public class MethodStore {
+
+ public static final ThreadLocal<Method[]> METHODS = new ThreadLocal<Method[]>();
+
+}
@@ -0,0 +1,4 @@
+/**
+ * This package contains classes that must be visible to the proxies class loader
+ */
+package org.jboss.invocation.proxy.classloading;
@@ -19,7 +19,7 @@
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
-package org.jboss.invocation.proxy;
+package org.jboss.invocation.proxy.reflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
@@ -34,8 +34,6 @@
Collection<Method> getDeclaredMethods();
- Collection<Method> getMethods();
-
Method getMethod(String methodName, Class<?> returnType, Class<?> ... parameters) throws NoSuchMethodException;
Collection<Constructor<?>> getConstructors();
Oops, something went wrong.

0 comments on commit 0f366a4

Please sign in to comment.