All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.inject.internal.BytecodeGen Maven / Gradle / Ivy

There is a newer version: 7.0.0
Show newest version
/*
 * 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.
 *
 * 

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 [email protected] (Stuart McCulloch) * @author [email protected] (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 CLASS_LOADER_CACHE; static { CacheBuilder builder = CacheBuilder.newBuilder().weakKeys().weakValues(); if (getCustomClassLoadingOption() == CustomClassLoadingOption.OFF) { builder.maximumSize(0); } CLASS_LOADER_CACHE = builder.build( new CacheLoader() { @Override public ClassLoader load(final ClassLoader typeClassLoader) { logger.fine("Creating a bridge ClassLoader for " + typeClassLoader); return AccessController.doPrivileged( new PrivilegedAction() { @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. * *

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: * *

    *
  • Public: This always works since we can generate the type into the {@link 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 {@code 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. */ 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") || 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); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy