More of mcculls fixes for choosing which classloader to use for generated classes. I believe this solves the package-private problem and the memory leak problem. Nice work Stuart!

git-svn-id: https://google-guice.googlecode.com/svn/trunk@544 d779f126-a31b-0410-b53b-1d3aecad763e
diff --git a/src/com/google/inject/DefaultConstructionProxyFactory.java b/src/com/google/inject/DefaultConstructionProxyFactory.java
index ace1f93..b508431 100644
--- a/src/com/google/inject/DefaultConstructionProxyFactory.java
+++ b/src/com/google/inject/DefaultConstructionProxyFactory.java
@@ -16,6 +16,7 @@
 
 package com.google.inject;
 
+import com.google.inject.internal.BytecodeGen.Visibility;
 import static com.google.inject.internal.BytecodeGen.newFastClass;
 import com.google.inject.internal.Errors;
 import com.google.inject.internal.ErrorsException;
@@ -39,9 +40,8 @@
       throws ErrorsException {
     final List<Parameter<?>> parameters = Parameter.forConstructor(constructor, errors);
 
-    // We can't use FastConstructor if the constructor is private or protected.
-    if (Modifier.isPrivate(constructor.getModifiers())
-        || Modifier.isProtected(constructor.getModifiers())) {
+    // We can't use FastConstructor if the constructor is non-public.
+    if (!Modifier.isPublic(constructor.getModifiers())) {
       constructor.setAccessible(true);
       return new ConstructionProxy<T>() {
         public T newInstance(Object... arguments) throws
@@ -67,7 +67,7 @@
 
     return new ConstructionProxy<T>() {
       Class<T> classToConstruct = constructor.getDeclaringClass();
-      FastClass fastClass = newFastClass(classToConstruct);
+      FastClass fastClass = newFastClass(classToConstruct, Visibility.PUBLIC);
       final FastConstructor fastConstructor = fastClass.getConstructor(constructor);
 
       @SuppressWarnings("unchecked")
diff --git a/src/com/google/inject/InjectorImpl.java b/src/com/google/inject/InjectorImpl.java
index d337d85..4b3c282 100644
--- a/src/com/google/inject/InjectorImpl.java
+++ b/src/com/google/inject/InjectorImpl.java
@@ -23,6 +23,7 @@
 import com.google.common.collect.Multimap;
 import com.google.common.collect.Multimaps;
 import com.google.common.collect.Sets;
+import com.google.inject.internal.BytecodeGen.Visibility;
 import static com.google.inject.internal.BytecodeGen.newFastClass;
 import com.google.inject.internal.Classes;
 import com.google.inject.internal.Errors;
@@ -884,7 +885,8 @@
           }
         };
       } else {
-        FastClass fastClass = newFastClass(method.getDeclaringClass());
+        FastClass fastClass = newFastClass(method.getDeclaringClass(),
+            Visibility.forMember(method));
         final FastMethod fastMethod = fastClass.getMethod(method);
 
         methodInvoker = new MethodInvoker() {
diff --git a/src/com/google/inject/ProxyFactory.java b/src/com/google/inject/ProxyFactory.java
index 13bb646..f43cc9f 100644
--- a/src/com/google/inject/ProxyFactory.java
+++ b/src/com/google/inject/ProxyFactory.java
@@ -18,6 +18,7 @@
 
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
+import com.google.inject.internal.BytecodeGen.Visibility;
 import static com.google.inject.internal.BytecodeGen.newEnhancer;
 import static com.google.inject.internal.BytecodeGen.newFastClass;
 import com.google.inject.internal.Errors;
@@ -114,11 +115,16 @@
       indices.put(method, i);
     }
 
+    // true if all the methods we're intercepting are public. This impacts which classloader we
+    // should use for loading the enhanced class
+    Visibility visibility = Visibility.PUBLIC;
+
     // Iterate over aspects and add interceptors for the methods they apply to
     boolean anyMatched = false;
     for (MethodAspect methodAspect : applicableAspects) {
       for (MethodInterceptorsPair pair : methodInterceptorsPairs) {
         if (methodAspect.matches(pair.method)) {
+          visibility = visibility.and(Visibility.forMember(pair.method));
           pair.addAll(methodAspect.interceptors());
           anyMatched = true;
         }
@@ -147,7 +153,7 @@
     }
 
     // Create the proxied class.
-    Enhancer enhancer = newEnhancer(declaringClass);
+    Enhancer enhancer = newEnhancer(declaringClass, visibility);
     enhancer.setCallbackFilter(new CallbackFilter() {
       public int accept(Method method) {
         return indices.get(method);
@@ -168,7 +174,7 @@
    */
   private <T> ConstructionProxy<T> createConstructionProxy(Errors errors, final Class<?> clazz,
       final Constructor standardConstructor) throws ErrorsException {
-    FastClass fastClass = newFastClass(clazz);
+    FastClass fastClass = newFastClass(clazz, Visibility.PUBLIC);
     final FastConstructor fastConstructor
         = fastClass.getConstructor(standardConstructor.getParameterTypes());
     final List<Parameter<?>> parameters = Parameter.forConstructor(standardConstructor, errors);
diff --git a/src/com/google/inject/internal/BytecodeGen.java b/src/com/google/inject/internal/BytecodeGen.java
index 35f5458..019a8d0 100644
--- a/src/com/google/inject/internal/BytecodeGen.java
+++ b/src/com/google/inject/internal/BytecodeGen.java
@@ -17,10 +17,11 @@
 package com.google.inject.internal;
 
 import static com.google.inject.internal.ReferenceType.WEAK;
-import static java.lang.reflect.Modifier.PROTECTED;
-import static java.lang.reflect.Modifier.PUBLIC;
+import java.lang.reflect.Member;
+import java.lang.reflect.Modifier;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
+import java.util.logging.Logger;
 import net.sf.cglib.core.DefaultNamingPolicy;
 import net.sf.cglib.core.NamingPolicy;
 import net.sf.cglib.proxy.Enhancer;
@@ -57,6 +58,8 @@
  */
 public final class BytecodeGen {
 
+  private static final Logger logger = Logger.getLogger(BytecodeGen.class.getName());
+
   static final ClassLoader GUICE_CLASS_LOADER = BytecodeGen.class.getClassLoader();
 
   /** ie. "com.google.inject.internal" */
@@ -74,9 +77,8 @@
   };
 
   /** Use "-Dguice.custom.loader=false" to disable custom classloading. */
-  // disabled until assisted inject tests pass
   static final boolean HOOK_ENABLED
-      = "true".equals(System.getProperty("guice.custom.loader", "false"));
+      = "true".equals(System.getProperty("guice.custom.loader", "true"));
 
   /**
    * Weak cache of bridge class loaders that make the Guice implementation
@@ -85,6 +87,12 @@
   private static final ReferenceCache<ClassLoader, ClassLoader> CLASS_LOADER_CACHE
       = new ReferenceCache<ClassLoader, ClassLoader>(WEAK, WEAK) {
         @Override protected ClassLoader create(final ClassLoader typeClassLoader) {
+          // Don't bother bridging existing bridge classloaders
+          if (typeClassLoader instanceof BridgeClassLoader) {
+            return typeClassLoader;
+          }
+
+          logger.fine("Creating a bridge ClassLoader for " + typeClassLoader);
           return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
             public ClassLoader run() {
               return new BridgeClassLoader(typeClassLoader);
@@ -94,16 +102,6 @@
       };
 
   /**
-   * Returns true if {@code type} can be loaded from our custom class loader.
-   * Private/implementation classes are not visible to classes loaded by other
-   * class loaders, even when they are in the same package. Therefore we cannot
-   * intercept classloading requests for such types.
-   */
-  private static boolean isHookable(Class<?> type) {
-    return (type.getModifiers() & (PROTECTED | PUBLIC)) != 0;
-  }
-
-  /**
    * For class loaders, {@code null}, is always an alias to the
    * {@link ClassLoader#getSystemClassLoader() system class loader}.
    */
@@ -123,32 +121,84 @@
   private static ClassLoader getClassLoader(Class<?> type, ClassLoader delegate) {
     delegate = canonicalize(delegate);
 
-    if (HOOK_ENABLED && isHookable(type)) {
+    if (HOOK_ENABLED && Visibility.forType(type) == Visibility.PUBLIC) {
       return CLASS_LOADER_CACHE.get(delegate);
     }
 
     return delegate;
   }
 
-  public static FastClass newFastClass(Class<?> type) {
+  public static FastClass newFastClass(Class<?> type, Visibility visibility) {
     FastClass.Generator generator = new FastClass.Generator();
     generator.setType(type);
-    generator.setClassLoader(getClassLoader(type));
+    if (visibility == Visibility.PUBLIC) {
+      generator.setClassLoader(getClassLoader(type));
+    }
     generator.setNamingPolicy(NAMING_POLICY);
+    logger.fine("Loading " + type + " FastClass with " + generator.getClassLoader());
     return generator.create();
   }
 
-  public static Enhancer newEnhancer(Class<?> type) {
+  public static Enhancer newEnhancer(Class<?> type, Visibility visibility) {
     Enhancer enhancer = new Enhancer();
     enhancer.setSuperclass(type);
     enhancer.setUseCache(false); // We do enough caching.
     enhancer.setUseFactory(false);
+    if (visibility == Visibility.PUBLIC) {
+      enhancer.setClassLoader(getClassLoader(type));
+    }
     enhancer.setNamingPolicy(NAMING_POLICY);
-    enhancer.setClassLoader(getClassLoader(type));
+    logger.fine("Loading " + type + " Enhancer with " + enhancer.getClassLoader());
     return enhancer;
   }
 
   /**
+   * The required visibility of a user's class from a Guice-generated class. Visibility of
+   * package-private members depends on the loading classloader: only if two classes were loaded by
+   * the same classloader can they see each other's package-private members. We need to be careful
+   * when choosing which classloader to use for generated classes. We prefer our bridge classloader,
+   * since it's OSGi-safe and doesn't leak permgen space. But often we cannot due to visibility.
+   */
+  public enum Visibility {
+
+    /**
+     * Indicates that Guice-generated classes only need to call and override public members of the
+     * target class. These generated classes may be loaded by our bridge classloader.
+     */
+    PUBLIC {
+      public Visibility and(Visibility that) {
+        return that;
+      }
+    },
+
+    /**
+     * Indicates that Guice-generated classes need to call or override package-private members.
+     * These generated classes must be loaded in the same classloader as the target class. They
+     * won't work with OSGi, and won't get garbage collected until the target class' classloader is
+     * garbage collected.
+     */
+    SAME_PACKAGE {
+      public Visibility and(Visibility that) {
+        return this;
+      }
+    };
+
+    public static Visibility forMember(Member member) {
+      return (member.getModifiers() & (Modifier.PROTECTED | Modifier.PUBLIC)) != 0
+          ? PUBLIC
+          : SAME_PACKAGE;
+    }
+
+    public static Visibility forType(Class<?> type) {
+      return (type.getModifiers() & (Modifier.PROTECTED | Modifier.PUBLIC)) != 0
+          ? PUBLIC
+          : SAME_PACKAGE;
+    }
+
+    public abstract Visibility and(Visibility that);
+  }
+
+  /**
    * Loader for Guice-generated classes. For referenced classes, this delegates to either either the
    * user's classloader (which is the parent of this classloader) or Guice's class loader.
    */