From 3c5bd2ad98707615ecaead0aacc4cfb4076a9cf1 Mon Sep 17 00:00:00 2001 From: greg Date: Tue, 20 Feb 2007 21:15:09 +0300 Subject: [PATCH] enhancer enhancements: covariant return types support ("add interface to implementation") --- .../com/intellij/util/xml/impl/AdvancedProxy.java | 18 ++++ .../src/net/sf/cglib/proxy/AdvancedEnhancer.java | 95 ++++++++++++++-------- .../net/sf/cglib/proxy/BridgeMethodGenerator.java | 40 +++++++++ 3 files changed, 119 insertions(+), 34 deletions(-) create mode 100644 dom/impl/src/net/sf/cglib/proxy/BridgeMethodGenerator.java diff --git a/dom/impl/src/com/intellij/util/xml/impl/AdvancedProxy.java b/dom/impl/src/com/intellij/util/xml/impl/AdvancedProxy.java index a84ee92f9e..2123e4beb7 100644 --- a/dom/impl/src/com/intellij/util/xml/impl/AdvancedProxy.java +++ b/dom/impl/src/com/intellij/util/xml/impl/AdvancedProxy.java @@ -48,9 +48,26 @@ public class AdvancedProxy { return createProxy(superClass, otherInterfaces, handler, Collections.emptySet(), ArrayUtil.EMPTY_OBJECT_ARRAY); } + public static T createProxy(final Class superClass, final Class... otherInterfaces) { + return createProxy(superClass, otherInterfaces, new InvocationHandler() { + public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable { + throw new AbstractMethodError(method.toString()); + } + }, false, Collections.emptySet(), ArrayUtil.EMPTY_OBJECT_ARRAY); + } + + public static T createProxy(final Class superClass, + final Class[] interfaces, + final InvocationHandler handler, + final Set additionalMethods, + final Object... constructorArgs) { + return createProxy(superClass, interfaces, handler, true, additionalMethods, constructorArgs); + } + public static T createProxy(final Class superClass, final Class[] interfaces, final InvocationHandler handler, + final boolean interceptObjectMethods, final Set additionalMethods, final Object... constructorArgs) { try { @@ -64,6 +81,7 @@ public class AdvancedProxy { AdvancedEnhancer e = new AdvancedEnhancer(); e.setAdditionalMethods(additionalMethods); + e.setInterceptObjectMethodsFlag(interceptObjectMethods); e.setInterfaces(interfaces); e.setCallbacks(callbacks); if (superClass != null) { diff --git a/dom/impl/src/net/sf/cglib/proxy/AdvancedEnhancer.java b/dom/impl/src/net/sf/cglib/proxy/AdvancedEnhancer.java index f96a8c4e9a..66f3f755f6 100644 --- a/dom/impl/src/net/sf/cglib/proxy/AdvancedEnhancer.java +++ b/dom/impl/src/net/sf/cglib/proxy/AdvancedEnhancer.java @@ -16,6 +16,7 @@ package net.sf.cglib.proxy; import com.intellij.util.xml.JavaMethodSignature; +import com.intellij.util.ReflectionCache; import com.intellij.ide.plugins.cl.PluginClassLoader; import net.sf.cglib.asm.ClassVisitor; import net.sf.cglib.asm.Label; @@ -147,7 +148,12 @@ public class AdvancedEnhancer extends AbstractClassGenerator //Changes by Peter Gromov private Set myAdditionalMethods = Collections.emptySet(); + //Changes by Gregory Shrago + private boolean myInterceptObjectMethodsFlag = true; + public void setInterceptObjectMethodsFlag(final boolean interceptObjectMethodsFlag) { + myInterceptObjectMethodsFlag = interceptObjectMethodsFlag; + } /** * Create a new Enhancer. A new Enhancer @@ -465,43 +471,28 @@ public class AdvancedEnhancer extends AbstractClassGenerator // its superinterfaces. final Set forcePublic = new HashSet(); List actualMethods = new ArrayList(); + final Map covariantMethods = new HashMap(); getMethods(sc, interfaces, actualMethods, new ArrayList(), forcePublic); //Changes by Peter Gromov final Set finalMethods = new HashSet(); - Class aClass = sc; - while (!Object.class.equals(aClass)) { + for(Class aClass = sc; aClass != null; aClass = aClass.getSuperclass()) { + if (myInterceptObjectMethodsFlag && Object.class.equals(aClass)) continue; for (final Method method : aClass.getDeclaredMethods()) { final int modifiers = method.getModifiers(); final JavaMethodSignature signature = JavaMethodSignature.getSignature(method); if ((modifiers & Constants.ACC_FINAL) != 0) { finalMethods.add(signature); - actualMethods.remove(method); + removeAllCovariantMethods(actualMethods, method, covariantMethods); } else if ((modifiers & Constants.ACC_ABSTRACT) == 0 && !(myAdditionalMethods.contains(signature)) || finalMethods.contains(signature)) { - actualMethods.remove(method); + removeAllCovariantMethods(actualMethods, method, covariantMethods); } } - aClass = aClass.getSuperclass(); } - List methods = CollectionUtils.transform(actualMethods, new Transformer() { - public Object transform(Object value) { - Method method = (Method)value; - int modifiers = Constants.ACC_FINAL - | (method.getModifiers() - & ~Constants.ACC_ABSTRACT - & ~Constants.ACC_NATIVE - & ~Constants.ACC_SYNCHRONIZED); - if (forcePublic.contains(MethodWrapper.create(method))) { - modifiers = (modifiers & ~Constants.ACC_PROTECTED) | Constants.ACC_PUBLIC; - } - return ReflectUtils.getMethodInfo(method, modifiers); - } - }); - ClassEmitter e = new ClassEmitter(v); e.begin_class(Constants.V1_2, Constants.ACC_PUBLIC, @@ -526,8 +517,20 @@ public class AdvancedEnhancer extends AbstractClassGenerator for (int i = 0; i < callbackTypes.length; i++) { e.declare_field(Constants.ACC_PRIVATE, getCallbackField(i), callbackTypes[i], null, null); } + final Map methodInfoMap = new HashMap(); + for (Method method : actualMethods) { + int modifiers = + Constants.ACC_FINAL | (method.getModifiers() & ~Constants.ACC_ABSTRACT & ~Constants.ACC_NATIVE & ~Constants.ACC_SYNCHRONIZED); + if (forcePublic.contains(MethodWrapper.create(method))) { + modifiers = (modifiers & ~Constants.ACC_PROTECTED) | Constants.ACC_PUBLIC; + } + if (covariantMethods.containsKey(method)) { + modifiers = modifiers | Constants.ACC_BRIDGE; + } + methodInfoMap.put(method, ReflectUtils.getMethodInfo(method, modifiers)); + } - emitMethods(e, methods, actualMethods); + emitMethods(e, methodInfoMap, covariantMethods); emitConstructors(e, constructorInfo); emitSetThreadCallbacks(e); emitSetStaticCallbacks(e); @@ -547,6 +550,25 @@ public class AdvancedEnhancer extends AbstractClassGenerator e.end_class(); } + private static void removeAllCovariantMethods(final List actualMethods, final Method method, final Map covariantMethods) { + for (Iterator it = actualMethods.iterator(); it.hasNext();) { + Method actualMethod = it.next(); + if (actualMethod.equals(method)) { + it.remove(); + } + else if (actualMethod.getName().equals(method.getName()) + && Arrays.equals(actualMethod.getParameterTypes(), method.getParameterTypes()) + && ReflectionCache.isAssignable(actualMethod.getReturnType(), method.getReturnType())) { + if ((actualMethod.getModifiers() & Constants.ACC_ABSTRACT) != 0) { + covariantMethods.put(actualMethod, method); + } + else { + it.remove(); + } + } + } + } + /** * Filter the list of constructors from the superclass. The * constructors which remain will be included in the generated @@ -901,34 +923,38 @@ public class AdvancedEnhancer extends AbstractClassGenerator e.end_method(); } - private void emitMethods(final ClassEmitter ce, List methods, List actualMethods) { + private void emitMethods(final ClassEmitter ce, Map methodMap, final Map covariantMethods) { CallbackGenerator[] generators = CallbackInfo.getGenerators(callbackTypes); + Map covariantInfoMap = new HashMap(); + for (Method method : methodMap.keySet()) { + final Method delegate = covariantMethods.get(method); + if (delegate != null) { + covariantInfoMap.put(methodMap.get(method), ReflectUtils.getMethodInfo(delegate, delegate.getModifiers())); + } + } + BridgeMethodGenerator bridgeMethodGenerator = new BridgeMethodGenerator(covariantInfoMap); Map> groups = new HashMap>(); final Map indexes = new HashMap(); final Map originalModifiers = new HashMap(); - final Map positions = CollectionUtils.getIndexMap(methods); - - Iterator it1 = methods.iterator(); - Iterator it2 = (actualMethods != null) ? actualMethods.iterator() : null; + final Map positions = CollectionUtils.getIndexMap(new ArrayList(methodMap.values())); - while (it1.hasNext()) { - MethodInfo method = (MethodInfo)it1.next(); - Method actualMethod = (it2 != null) ? it2.next() : null; + for (Method actualMethod : methodMap.keySet()) { + MethodInfo method = methodMap.get(actualMethod); int index = filter.accept(actualMethod); if (index >= callbackTypes.length) { throw new IllegalArgumentException("Callback filter returned an index that is too large: " + index); } originalModifiers.put(method, (actualMethod != null) ? actualMethod.getModifiers() : method.getModifiers()); indexes.put(method, index); - List group = groups.get(generators[index]); + final CallbackGenerator generator = covariantMethods.containsKey(actualMethod)? bridgeMethodGenerator : generators[index]; + List group = groups.get(generator); if (group == null) { - groups.put(generators[index], group = new ArrayList(methods.size())); + groups.put(generator, group = new ArrayList(methodMap.size())); } group.add(method); } - Set seenGen = new HashSet(); CodeEmitter se = ce.getStaticHook(); se.new_instance(THREAD_LOCAL); se.dup(); @@ -965,8 +991,9 @@ public class AdvancedEnhancer extends AbstractClassGenerator return e; } }; - for (int i = 0; i < callbackTypes.length; i++) { - CallbackGenerator gen = generators[i]; + Set seenGen = new HashSet(); + for (int i = 0; i < callbackTypes.length + 1; i++) { + CallbackGenerator gen = i == callbackTypes.length? bridgeMethodGenerator : generators[i]; if (!seenGen.contains(gen)) { seenGen.add(gen); final List fmethods = groups.get(gen); diff --git a/dom/impl/src/net/sf/cglib/proxy/BridgeMethodGenerator.java b/dom/impl/src/net/sf/cglib/proxy/BridgeMethodGenerator.java new file mode 100644 index 0000000000..dfa5827786 --- /dev/null +++ b/dom/impl/src/net/sf/cglib/proxy/BridgeMethodGenerator.java @@ -0,0 +1,40 @@ +package net.sf.cglib.proxy; + +import net.sf.cglib.core.*; +import net.sf.cglib.asm.Type; + +import java.util.List; +import java.util.Iterator; +import java.util.Map; + +/** + * @author Gregory.Shrago + */ +public class BridgeMethodGenerator implements CallbackGenerator { + public static final InvocationHandlerGenerator INSTANCE = new InvocationHandlerGenerator(); + + private final Map myCovariantInfoMap; + + public BridgeMethodGenerator(final Map covariantInfoMap) { + myCovariantInfoMap = covariantInfoMap; + } + + public void generate(ClassEmitter ce, CallbackGenerator.Context context, List methods) { + for (Iterator it = methods.iterator(); it.hasNext();) { + MethodInfo method = (MethodInfo)it.next(); + final MethodInfo delegate = myCovariantInfoMap.get(method); + + CodeEmitter e = context.beginMethod(ce, method); + Block handler = e.begin_block(); + e.load_this(); + e.invoke_virtual_this(delegate.getSignature()); + e.return_value(); + handler.end(); + e.end_method(); + } + } + + public void generateStatic(CodeEmitter e, CallbackGenerator.Context context, List methods) { + } + +} -- 2.11.4.GIT