/* * Copyright (C) 2008 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.inject.internal; import static com.google.inject.internal.InternalFlags.getCustomClassLoadingOption; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.inject.internal.InternalFlags.CustomClassLoadingOption; import java.lang.reflect.Constructor; import java.lang.reflect.Member; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.logging.Level; import java.util.logging.Logger; /** * Utility methods for runtime code generation and class loading. We use this stuff for {@link * net.sf.cglib.reflect.FastClass faster reflection}, {@link net.sf.cglib.proxy.Enhancer method * interceptors} and to proxy circular dependencies. * * <p>When loading classes, we need to be careful of: * * <ul> * <li><strong>Memory leaks.</strong> Generated classes need to be garbage collected in long-lived * applications. Once an injector and any instances it created can be garbage collected, the * corresponding generated classes should be collectable. * <li><strong>Visibility.</strong> Containers like <code>OSGi</code> use class loader boundaries to * enforce modularity at runtime. * </ul> * * <p>For each generated class, there's multiple class loaders involved: * * <ul> * <li><strong>The related class's class loader.</strong> Every generated class services exactly one * user-supplied class. This class loader must be used to access members with protected and * package visibility. * <li><strong>Guice's class loader.</strong> * <li><strong>Our bridge class loader.</strong> This is a child of the user's class loader. It * selectively delegates to either the user's class loader (for user classes) or the Guice class * loader (for internal classes that are used by the generated classes). This class loader that * owns the classes generated by Guice. * </ul> * * @author mcculls@gmail.com (Stuart McCulloch) * @author jessewilson@google.com (Jesse Wilson) */ public final class BytecodeGen { static final Logger logger = Logger.getLogger(BytecodeGen.class.getName()); static final ClassLoader GUICE_CLASS_LOADER = canonicalize(BytecodeGen.class.getClassLoader()); // initialization-on-demand... private static class SystemBridgeHolder { static final BridgeClassLoader SYSTEM_BRIDGE = new BridgeClassLoader(); } /** ie. "com.google.inject.internal" */ static final String GUICE_INTERNAL_PACKAGE = BytecodeGen.class.getName().replaceFirst("\\.internal\\..*$", ".internal"); /*if[AOP]*/ /** either "net.sf.cglib", or "com.google.inject.internal.cglib" */ static final String CGLIB_PACKAGE = net.sf.cglib.proxy.Enhancer.class.getName().replaceFirst("\\.cglib\\..*$", ".cglib"); static final net.sf.cglib.core.NamingPolicy FASTCLASS_NAMING_POLICY = new net.sf.cglib.core.DefaultNamingPolicy() { @Override protected String getTag() { return "ByGuice"; } @Override public String getClassName( String prefix, String source, Object key, net.sf.cglib.core.Predicate names) { // we explicitly set the source here to "FastClass" so that our jarjar renaming // to $FastClass doesn't leak into the class names. if we did not do this, // classes would end up looking like $$$FastClassByGuice$$, with the extra $ // at the front. return super.getClassName(prefix, "FastClass", key, names); } }; static final net.sf.cglib.core.NamingPolicy ENHANCER_NAMING_POLICY = new net.sf.cglib.core.DefaultNamingPolicy() { @Override protected String getTag() { return "ByGuice"; } @Override public String getClassName( String prefix, String source, Object key, net.sf.cglib.core.Predicate names) { // we explicitly set the source here to "Enhancer" so that our jarjar renaming // to $Enhancer doesn't leak into the class names. if we did not do this, // classes would end up looking like $$$EnhancerByGuice$$, with the extra $ // at the front. return super.getClassName(prefix, "Enhancer", key, names); } }; /*end[AOP]*/ /*if[NO_AOP] private static final String CGLIB_PACKAGE = " "; // any string that's illegal in a package name end[NO_AOP]*/ /** * Weak cache of bridge class loaders that make the Guice implementation classes visible to * various code-generated proxies of client classes. */ private static final LoadingCache<ClassLoader, ClassLoader> CLASS_LOADER_CACHE; static { CacheBuilder<Object, Object> builder = CacheBuilder.newBuilder().weakKeys().weakValues(); if (getCustomClassLoadingOption() == CustomClassLoadingOption.OFF) { builder.maximumSize(0); } CLASS_LOADER_CACHE = builder.build( new CacheLoader<ClassLoader, ClassLoader>() { @Override public ClassLoader load(final ClassLoader typeClassLoader) { logger.fine("Creating a bridge ClassLoader for " + typeClassLoader); return AccessController.doPrivileged( new PrivilegedAction<ClassLoader>() { @Override public ClassLoader run() { return new BridgeClassLoader(typeClassLoader); } }); } }); } /** * Attempts to canonicalize null references to the system class loader. May return null if for * some reason the system loader is unavailable. */ private static ClassLoader canonicalize(ClassLoader classLoader) { return classLoader != null ? classLoader : SystemBridgeHolder.SYSTEM_BRIDGE.getParent(); } /** Returns the class loader to host generated classes for {@code type}. */ public static ClassLoader getClassLoader(Class<?> type) { return getClassLoader(type, type.getClassLoader()); } private static ClassLoader getClassLoader(Class<?> type, ClassLoader delegate) { // simple case: do nothing! if (getCustomClassLoadingOption() == CustomClassLoadingOption.OFF) { return delegate; } // java.* types can be seen everywhere if (type.getName().startsWith("java.")) { return GUICE_CLASS_LOADER; } delegate = canonicalize(delegate); // no need for a bridge if using same class loader, or it's already a bridge if (delegate == GUICE_CLASS_LOADER || delegate instanceof BridgeClassLoader) { return delegate; } // don't try bridging private types as it won't work if (Visibility.forType(type) == Visibility.PUBLIC) { if (delegate != SystemBridgeHolder.SYSTEM_BRIDGE.getParent()) { // delegate guaranteed to be non-null here return CLASS_LOADER_CACHE.getUnchecked(delegate); } // delegate may or may not be null here return SystemBridgeHolder.SYSTEM_BRIDGE; } return delegate; // last-resort: do nothing! } /*if[AOP]*/ // use fully-qualified names so imports don't need preprocessor statements /** * Returns a FastClass proxy for invoking the given member or {@code null} if access rules * disallow it. * * @see #newFastClassForMember(Class, Member) for a full description */ public static net.sf.cglib.reflect.FastClass newFastClassForMember(Member member) { return newFastClassForMember(member.getDeclaringClass(), member); } /** * Returns a FastClass proxy for invoking the given member or {@code null} if access rules * disallow it. * * <p>FastClass works by generating a type in the same package as the target {@code type}. This * may or may not work depending on the access level of the class/member. It breaks down into the * following cases depending on accessibility: * * <ul> * <li>Public: This always works since we can generate the type into the {@link BridgeClassLoader} * which ensures there are no versioning issues. * <li>Package private and Protected: This works as long as: * <ul> * <li>We can generate into the same classloader as the type. This is not possible for JDK * types which use the 'bootstrap' loader. * <li>The classloader of the type has the same version of {@code FastClass} as we do. This * may be violated when running in OSGI bundles. * </ul> * * <li>Private: This never works. * </ul> * * If we are unable to generate the type, then we return null and callers should work around by * using normal java reflection. */ public static net.sf.cglib.reflect.FastClass newFastClassForMember(Class<?> type, Member member) { if (!new net.sf.cglib.core.VisibilityPredicate(type, false).evaluate(member)) { // the member cannot be indexed by fast class. Bail out. return null; } boolean publiclyCallable = isPubliclyCallable(member); if (!publiclyCallable && !hasSameVersionOfCglib(type.getClassLoader())) { // The type is in a classloader with a different version of cglib and is not publicly visible // (so we can't use the bridge classloader to work around). Bail out. return null; } net.sf.cglib.reflect.FastClass.Generator generator = new net.sf.cglib.reflect.FastClass.Generator(); if (publiclyCallable) { // Use the bridge classloader if we can generator.setClassLoader(getClassLoader(type)); } generator.setType(type); generator.setNamingPolicy(FASTCLASS_NAMING_POLICY); if (logger.isLoggable(Level.FINE)) { logger.fine("Loading " + type + " FastClass with " + generator.getClassLoader()); } return generator.create(); } /** * Returns true if the types classloader has the same version of cglib that BytecodeGen has. This * only returns false in strange OSGI situations, but it prevents us from using FastClass for non * public members. */ private static boolean hasSameVersionOfCglib(ClassLoader classLoader) { Class<?> fc = net.sf.cglib.reflect.FastClass.class; try { return classLoader.loadClass(fc.getName()) == fc; } catch (ClassNotFoundException e) { return false; } } /** * Returns true if the member can be called by a fast class generated in a different classloader. */ private static boolean isPubliclyCallable(Member member) { if (!Modifier.isPublic(member.getModifiers())) { return false; } Class<?>[] parameterTypes; if (member instanceof Constructor) { parameterTypes = ((Constructor) member).getParameterTypes(); } else { Method method = (Method) member; if (!Modifier.isPublic(method.getReturnType().getModifiers())) { return false; } parameterTypes = method.getParameterTypes(); } for (Class<?> type : parameterTypes) { if (!Modifier.isPublic(type.getModifiers())) { return false; } } return true; } public static net.sf.cglib.proxy.Enhancer newEnhancer(Class<?> type, Visibility visibility) { net.sf.cglib.proxy.Enhancer enhancer = new net.sf.cglib.proxy.Enhancer(); enhancer.setSuperclass(type); enhancer.setUseFactory(false); if (visibility == Visibility.PUBLIC) { enhancer.setClassLoader(getClassLoader(type)); } enhancer.setNamingPolicy(ENHANCER_NAMING_POLICY); logger.fine("Loading " + type + " Enhancer with " + enhancer.getClassLoader()); return enhancer; } /*end[AOP]*/ /** * 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 { @Override 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 { @Override public Visibility and(Visibility that) { return this; } }; public static Visibility forMember(Member member) { if ((member.getModifiers() & (Modifier.PROTECTED | Modifier.PUBLIC)) == 0) { return SAME_PACKAGE; } Class[] parameterTypes; if (member instanceof Constructor) { parameterTypes = ((Constructor) member).getParameterTypes(); } else { Method method = (Method) member; if (forType(method.getReturnType()) == SAME_PACKAGE) { return SAME_PACKAGE; } parameterTypes = method.getParameterTypes(); } for (Class<?> type : parameterTypes) { if (forType(type) == SAME_PACKAGE) { return SAME_PACKAGE; } } return PUBLIC; } 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. */ private static class BridgeClassLoader extends ClassLoader { BridgeClassLoader() { // use system loader as parent } BridgeClassLoader(ClassLoader usersClassLoader) { super(usersClassLoader); } @Override protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { if (name.startsWith("sun.reflect")) { // these reflection classes must be loaded from bootstrap class loader return SystemBridgeHolder.SYSTEM_BRIDGE.classicLoadClass(name, resolve); } if (name.startsWith(GUICE_INTERNAL_PACKAGE) || name.startsWith(CGLIB_PACKAGE)) { if (null == GUICE_CLASS_LOADER) { // use special system bridge to load classes from bootstrap class loader return SystemBridgeHolder.SYSTEM_BRIDGE.classicLoadClass(name, resolve); } try { Class<?> clazz = GUICE_CLASS_LOADER.loadClass(name); if (resolve) { resolveClass(clazz); } return clazz; } catch (Throwable e) { // fall-back to classic delegation } } return classicLoadClass(name, resolve); } // make the classic delegating loadClass method visible Class<?> classicLoadClass(String name, boolean resolve) throws ClassNotFoundException { return super.loadClass(name, resolve); } } }