aboutsummaryrefslogtreecommitdiff
path: root/core/src/com/google/inject/internal/BytecodeGen.java
blob: 621720f24c8cda8717a37564b7d7ad201248a447 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
/**
 * 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 com.google.inject.internal.util.Function;
import com.google.inject.internal.util.ImmutableMap;
import com.google.inject.internal.util.MapMaker;
import com.google.inject.internal.util.Nullable;

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.Map;
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 private 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 NAMING_POLICY
      = new net.sf.cglib.core.DefaultNamingPolicy() {
    @Override protected String getTag() {
      return "ByGuice";
    }
  };
  /*end[AOP]*/
  /*if[NO_AOP]
  private static final String CGLIB_PACKAGE = " "; // any string that's illegal in a package name
  end[NO_AOP]*/

  /** Use "-Dguice.custom.loader=false" to disable custom classloading. */
  private static final boolean CUSTOM_LOADER_ENABLED
      = Boolean.parseBoolean(System.getProperty("guice.custom.loader", "true"));

  /**
   * Weak cache of bridge class loaders that make the Guice implementation
   * classes visible to various code-generated proxies of client classes.
   */
  private static final Map<ClassLoader, ClassLoader> CLASS_LOADER_CACHE;

  static {
    if (CUSTOM_LOADER_ENABLED) {
      CLASS_LOADER_CACHE = new MapMaker().weakKeys().weakValues().makeComputingMap(
          new Function<ClassLoader, ClassLoader>() {
            public ClassLoader apply(final @Nullable ClassLoader typeClassLoader) {
              logger.fine("Creating a bridge ClassLoader for " + typeClassLoader);
              return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
                public ClassLoader run() {
                  return new BridgeClassLoader(typeClassLoader);
                }
              });
            }
          });
    } else {
      CLASS_LOADER_CACHE = ImmutableMap.of();
    }
  }

  /**
   * 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 (!CUSTOM_LOADER_ENABLED) {
      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.get(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 
  public static net.sf.cglib.reflect.FastClass newFastClass(Class<?> type, Visibility visibility) {
    net.sf.cglib.reflect.FastClass.Generator generator
        = new net.sf.cglib.reflect.FastClass.Generator();
    generator.setType(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 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(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 = member instanceof Constructor
          ? ((Constructor) member).getParameterTypes()
          : ((Method) member).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);
    }
  }
}