/*
* 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);
}
}
}