/*
* 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 faster reflection
, method
interceptors
and to proxy circular dependencies. When loading classes, we need to be careful of:
- Memory leaks. 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.
- Visibility. Containers like
OSGi
use class loader boundaries to
enforce modularity at runtime.
For each generated class, there's multiple class loaders involved:
- The related class's class loader. Every generated class services exactly one
user-supplied class. This class loader must be used to access members with protected and
package visibility.
- Guice's class loader.
- Our bridge class loader. 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.
Author: mcculls@gmail.com (Stuart McCulloch), jessewilson@google.com (Jesse Wilson)
/**
* 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" /** 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" /** 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.
/**
* 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.
/**
* 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 type
. /** 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 null
if access rules disallow it. See Also:
/**
* 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 null
if access rules disallow it. FastClass works by generating a type in the same package as the target 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:
- Public: This always works since we can generate the type into the
BridgeClassLoader
which ensures there are no versioning issues. - Package private and Protected: This works as long as:
- We can generate into the same classloader as the type. This is not possible for JDK
types which use the 'bootstrap' loader.
- The classloader of the type has the same version of
FastClass
as we do. This may be violated when running in OSGI bundles.
- Private: This never works.
If we are unable to generate the type, then we return null and callers should work around by
using normal java reflection.
/**
* 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.
/**
* 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.
/**
* 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.
/**
* 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.
/**
* 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.
/**
* 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.
/**
* 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") || name.startsWith("jdk.internal.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);
}
}
}