blob: beaf406e315c62b07f7a3288716e90deb45de0ac [file] [log] [blame]
limpbizkit86cb3bb2008-10-15 21:25:50 +00001/**
2 * Copyright (C) 2008 Google Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.google.inject.internal;
18
sberlinb7a02b02011-07-08 00:34:16 +000019import com.google.common.base.Objects;
Sam Berlin409e0f52014-05-10 10:34:16 -040020import com.google.common.base.Preconditions;
sberlinb7a02b02011-07-08 00:34:16 +000021import com.google.common.collect.ImmutableSet;
limpbizkit86cb3bb2008-10-15 21:25:50 +000022import com.google.inject.Binder;
limpbizkitfcbdf992008-11-26 02:37:35 +000023import com.google.inject.Exposed;
24import com.google.inject.Key;
crazyboblee91193702009-02-05 22:18:55 +000025import com.google.inject.PrivateBinder;
limpbizkit53664a72009-02-21 00:25:27 +000026import com.google.inject.Provider;
samebe18906a2015-02-02 14:07:53 -080027import com.google.inject.Provides;
Sam Berlin409e0f52014-05-10 10:34:16 -040028import com.google.inject.internal.BytecodeGen.Visibility;
limpbizkit@gmail.com9a227be2010-07-03 15:51:31 +000029import com.google.inject.internal.util.StackTraceElements;
Sam Berlin29ce12b2014-03-10 12:52:01 -040030import com.google.inject.spi.BindingTargetVisitor;
limpbizkit76c24b12008-12-25 04:32:41 +000031import com.google.inject.spi.Dependency;
Sam Berlin29ce12b2014-03-10 12:52:01 -040032import com.google.inject.spi.HasDependencies;
33import com.google.inject.spi.ProviderInstanceBinding;
34import com.google.inject.spi.ProviderWithExtensionVisitor;
35import com.google.inject.spi.ProvidesMethodBinding;
36import com.google.inject.spi.ProvidesMethodTargetVisitor;
sberlinb7a02b02011-07-08 00:34:16 +000037
limpbizkit86cb3bb2008-10-15 21:25:50 +000038import java.lang.annotation.Annotation;
limpbizkit86cb3bb2008-10-15 21:25:50 +000039import java.lang.reflect.InvocationTargetException;
limpbizkitfcbdf992008-11-26 02:37:35 +000040import java.lang.reflect.Method;
Sam Berlin409e0f52014-05-10 10:34:16 -040041import java.lang.reflect.Modifier;
limpbizkit86cb3bb2008-10-15 21:25:50 +000042import java.util.List;
limpbizkit76c24b12008-12-25 04:32:41 +000043import java.util.Set;
limpbizkit86cb3bb2008-10-15 21:25:50 +000044
45/**
46 * A provider that invokes a method and returns its result.
47 *
48 * @author jessewilson@google.com (Jesse Wilson)
49 */
Sam Berlin409e0f52014-05-10 10:34:16 -040050public abstract class ProviderMethod<T> implements ProviderWithExtensionVisitor<T>, HasDependencies,
Sam Berlin29ce12b2014-03-10 12:52:01 -040051 ProvidesMethodBinding<T> {
Sam Berlin409e0f52014-05-10 10:34:16 -040052
53 /**
54 * Creates a {@link ProviderMethod}.
55 *
samebe18906a2015-02-02 14:07:53 -080056 * <p>Unless {@code skipFastClassGeneration} is set, this will use
57 * {@link net.sf.cglib.reflect.FastClass} to invoke the actual method, since it is significantly
58 * faster. However, this will fail if the method is {@code private} or {@code protected}, since
59 * fastclass is subject to java access policies.
Sam Berlin409e0f52014-05-10 10:34:16 -040060 */
61 static <T> ProviderMethod<T> create(Key<T> key, Method method, Object instance,
62 ImmutableSet<Dependency<?>> dependencies, List<Provider<?>> parameterProviders,
samebe18906a2015-02-02 14:07:53 -080063 Class<? extends Annotation> scopeAnnotation, boolean skipFastClassGeneration,
64 Annotation annotation) {
Sam Berlin409e0f52014-05-10 10:34:16 -040065 int modifiers = method.getModifiers();
66 /*if[AOP]*/
Sam Berlin58d60742014-07-21 17:13:22 -040067 if (!skipFastClassGeneration && !Modifier.isPrivate(modifiers)
68 && !Modifier.isProtected(modifiers)) {
Sam Berlin409e0f52014-05-10 10:34:16 -040069 try {
70 // We use an index instead of FastMethod to save a stack frame.
samebe18906a2015-02-02 14:07:53 -080071 return new FastClassProviderMethod<T>(key,
72 method,
73 instance,
74 dependencies,
75 parameterProviders,
76 scopeAnnotation,
77 annotation);
Sam Berlin409e0f52014-05-10 10:34:16 -040078 } catch (net.sf.cglib.core.CodeGenerationException e) {/* fall-through */}
79 }
80 /*end[AOP]*/
81
82 if (!Modifier.isPublic(modifiers) ||
83 !Modifier.isPublic(method.getDeclaringClass().getModifiers())) {
84 method.setAccessible(true);
85 }
86
samebe18906a2015-02-02 14:07:53 -080087 return new ReflectionProviderMethod<T>(key,
88 method,
89 instance,
90 dependencies,
91 parameterProviders,
92 scopeAnnotation,
93 annotation);
Sam Berlin409e0f52014-05-10 10:34:16 -040094 }
95
96 protected final Object instance;
97 protected final Method method;
98
limpbizkit86cb3bb2008-10-15 21:25:50 +000099 private final Key<T> key;
100 private final Class<? extends Annotation> scopeAnnotation;
limpbizkit76c24b12008-12-25 04:32:41 +0000101 private final ImmutableSet<Dependency<?>> dependencies;
limpbizkit86cb3bb2008-10-15 21:25:50 +0000102 private final List<Provider<?>> parameterProviders;
limpbizkitfcbdf992008-11-26 02:37:35 +0000103 private final boolean exposed;
samebe18906a2015-02-02 14:07:53 -0800104 private final Annotation annotation;
limpbizkit86cb3bb2008-10-15 21:25:50 +0000105
106 /**
107 * @param method the method to invoke. It's return type must be the same type as {@code key}.
108 */
Sam Berlin409e0f52014-05-10 10:34:16 -0400109 private ProviderMethod(Key<T> key, Method method, Object instance,
limpbizkit76c24b12008-12-25 04:32:41 +0000110 ImmutableSet<Dependency<?>> dependencies, List<Provider<?>> parameterProviders,
samebe18906a2015-02-02 14:07:53 -0800111 Class<? extends Annotation> scopeAnnotation, Annotation annotation) {
limpbizkit86cb3bb2008-10-15 21:25:50 +0000112 this.key = key;
113 this.scopeAnnotation = scopeAnnotation;
114 this.instance = instance;
limpbizkit76c24b12008-12-25 04:32:41 +0000115 this.dependencies = dependencies;
limpbizkit86cb3bb2008-10-15 21:25:50 +0000116 this.method = method;
117 this.parameterProviders = parameterProviders;
limpbizkitfcbdf992008-11-26 02:37:35 +0000118 this.exposed = method.isAnnotationPresent(Exposed.class);
samebe18906a2015-02-02 14:07:53 -0800119 this.annotation = annotation;
limpbizkit86cb3bb2008-10-15 21:25:50 +0000120 }
121
samebe18906a2015-02-02 14:07:53 -0800122 @Override
limpbizkit86cb3bb2008-10-15 21:25:50 +0000123 public Key<T> getKey() {
124 return key;
125 }
126
samebe18906a2015-02-02 14:07:53 -0800127 @Override
limpbizkit86cb3bb2008-10-15 21:25:50 +0000128 public Method getMethod() {
129 return method;
130 }
131
limpbizkit714df3c2009-01-08 02:40:04 +0000132 // exposed for GIN
133 public Object getInstance() {
134 return instance;
135 }
Sam Berlin29ce12b2014-03-10 12:52:01 -0400136
samebe18906a2015-02-02 14:07:53 -0800137 @Override
Sam Berlin29ce12b2014-03-10 12:52:01 -0400138 public Object getEnclosingInstance() {
139 return instance;
140 }
samebe18906a2015-02-02 14:07:53 -0800141
142 @Override
143 public Annotation getAnnotation() {
144 return annotation;
145 }
limpbizkit714df3c2009-01-08 02:40:04 +0000146
limpbizkit86cb3bb2008-10-15 21:25:50 +0000147 public void configure(Binder binder) {
limpbizkitfcbdf992008-11-26 02:37:35 +0000148 binder = binder.withSource(method);
149
limpbizkit86cb3bb2008-10-15 21:25:50 +0000150 if (scopeAnnotation != null) {
limpbizkitfcbdf992008-11-26 02:37:35 +0000151 binder.bind(key).toProvider(this).in(scopeAnnotation);
limpbizkit86cb3bb2008-10-15 21:25:50 +0000152 } else {
limpbizkitfcbdf992008-11-26 02:37:35 +0000153 binder.bind(key).toProvider(this);
154 }
155
156 if (exposed) {
limpbizkit0551b642008-11-26 22:25:05 +0000157 // the cast is safe 'cause the only binder we have implements PrivateBinder. If there's a
158 // misplaced @Exposed, calling this will add an error to the binder's error queue
limpbizkitfcbdf992008-11-26 02:37:35 +0000159 ((PrivateBinder) binder).expose(key);
limpbizkit86cb3bb2008-10-15 21:25:50 +0000160 }
161 }
162
samebe18906a2015-02-02 14:07:53 -0800163 @Override
limpbizkit86cb3bb2008-10-15 21:25:50 +0000164 public T get() {
165 Object[] parameters = new Object[parameterProviders.size()];
166 for (int i = 0; i < parameters.length; i++) {
167 parameters[i] = parameterProviders.get(i).get();
168 }
169
170 try {
limpbizkit86cb3bb2008-10-15 21:25:50 +0000171 @SuppressWarnings({ "unchecked", "UnnecessaryLocalVariable" })
Sam Berlin409e0f52014-05-10 10:34:16 -0400172 T result = (T) doProvision(parameters);
limpbizkit86cb3bb2008-10-15 21:25:50 +0000173 return result;
174 } catch (IllegalAccessException e) {
175 throw new AssertionError(e);
176 } catch (InvocationTargetException e) {
sberlin132a5db2011-06-05 18:32:05 +0000177 throw Exceptions.rethrowCause(e);
limpbizkit86cb3bb2008-10-15 21:25:50 +0000178 }
179 }
limpbizkit76c24b12008-12-25 04:32:41 +0000180
Sam Berlin409e0f52014-05-10 10:34:16 -0400181 /** Extension point for our subclasses to implement the provisioning strategy. */
182 abstract Object doProvision(Object[] parameters)
183 throws IllegalAccessException, InvocationTargetException;
184
samebe18906a2015-02-02 14:07:53 -0800185 @Override
limpbizkit76c24b12008-12-25 04:32:41 +0000186 public Set<Dependency<?>> getDependencies() {
187 return dependencies;
188 }
Sam Berlin29ce12b2014-03-10 12:52:01 -0400189
samebe18906a2015-02-02 14:07:53 -0800190 @Override
Sam Berlin29ce12b2014-03-10 12:52:01 -0400191 @SuppressWarnings("unchecked")
192 public <B, V> V acceptExtensionVisitor(BindingTargetVisitor<B, V> visitor,
193 ProviderInstanceBinding<? extends B> binding) {
194 if (visitor instanceof ProvidesMethodTargetVisitor) {
195 return ((ProvidesMethodTargetVisitor<T, V>)visitor).visit(this);
196 }
197 return visitor.visit(binding);
198 }
limpbizkitf03bdb82009-10-05 21:54:38 +0000199
200 @Override public String toString() {
samebe18906a2015-02-02 14:07:53 -0800201 String annotationString = annotation.toString();
202 // Show @Provides w/o the com.google.inject prefix.
203 if (annotation.annotationType() == Provides.class) {
204 annotationString = "@Provides";
205 } else if (annotationString.endsWith("()")) {
206 // Remove the common "()" suffix if there are no values.
207 annotationString = annotationString.substring(0, annotationString.length() - 2);
208 }
209 return annotationString + " " + StackTraceElements.forMember(method);
limpbizkitf03bdb82009-10-05 21:54:38 +0000210 }
sberlin1068a4a2010-12-16 01:38:08 +0000211
212 @Override
213 public boolean equals(Object obj) {
214 if (obj instanceof ProviderMethod) {
samebe18906a2015-02-02 14:07:53 -0800215 ProviderMethod<?> o = (ProviderMethod<?>) obj;
sberlin7691f9e2010-12-16 02:34:54 +0000216 return method.equals(o.method)
samebe18906a2015-02-02 14:07:53 -0800217 && instance.equals(o.instance)
218 && annotation.equals(o.annotation);
sberlin1068a4a2010-12-16 01:38:08 +0000219 } else {
220 return false;
221 }
222 }
sberlin672d0742010-12-16 14:46:42 +0000223
224 @Override
225 public int hashCode() {
226 // Avoid calling hashCode on 'instance', which is a user-object
227 // that might not be expecting it.
228 // (We need to call equals, so we do. But we can avoid hashCode.)
samebe18906a2015-02-02 14:07:53 -0800229 return Objects.hashCode(method, annotation);
sberlin672d0742010-12-16 14:46:42 +0000230 }
Sam Berlin409e0f52014-05-10 10:34:16 -0400231
232 /*if[AOP]*/
233 /**
234 * A {@link ProviderMethod} implementation that uses {@link net.sf.cglib.reflect.FastClass#invoke}
235 * to invoke the provider method.
236 */
237 private static final class FastClassProviderMethod<T> extends ProviderMethod<T> {
238 final net.sf.cglib.reflect.FastClass fastClass;
239 final int methodIndex;
240
241 FastClassProviderMethod(Key<T> key,
242 Method method,
243 Object instance,
244 ImmutableSet<Dependency<?>> dependencies,
245 List<Provider<?>> parameterProviders,
samebe18906a2015-02-02 14:07:53 -0800246 Class<? extends Annotation> scopeAnnotation,
247 Annotation annotation) {
248 super(key,
249 method,
250 instance,
251 dependencies,
252 parameterProviders,
253 scopeAnnotation,
254 annotation);
Sam Berlin409e0f52014-05-10 10:34:16 -0400255 // We need to generate a FastClass for the method's class, not the object's class.
256 this.fastClass =
257 BytecodeGen.newFastClass(method.getDeclaringClass(), Visibility.forMember(method));
258 // Use the Signature overload of getIndex because it properly uses return types to identify
259 // particular methods. This is normally irrelevant, except in the case of covariant overrides
260 // which java implements with a compiler generated bridge method to implement the override.
261 this.methodIndex = fastClass.getIndex(
262 new net.sf.cglib.core.Signature(
263 method.getName(), org.objectweb.asm.Type.getMethodDescriptor(method)));
264 Preconditions.checkArgument(this.methodIndex >= 0,
265 "Could not find method %s in fast class for class %s",
266 method,
267 method.getDeclaringClass());
268 }
269
270 @Override public Object doProvision(Object[] parameters)
271 throws IllegalAccessException, InvocationTargetException {
272 return fastClass.invoke(methodIndex, instance, parameters);
273 }
274 }
275 /*end[AOP]*/
276
277 /**
278 * A {@link ProviderMethod} implementation that invokes the method using normal java reflection.
279 */
280 private static final class ReflectionProviderMethod<T> extends ProviderMethod<T> {
281 ReflectionProviderMethod(Key<T> key,
282 Method method,
283 Object instance,
284 ImmutableSet<Dependency<?>> dependencies,
285 List<Provider<?>> parameterProviders,
samebe18906a2015-02-02 14:07:53 -0800286 Class<? extends Annotation> scopeAnnotation,
287 Annotation annotation) {
288 super(key,
289 method,
290 instance,
291 dependencies,
292 parameterProviders,
293 scopeAnnotation,
294 annotation);
Sam Berlin409e0f52014-05-10 10:34:16 -0400295 }
296
297 @Override Object doProvision(Object[] parameters) throws IllegalAccessException,
298 InvocationTargetException {
299 return method.invoke(instance, parameters);
300 }
301 }
limpbizkit86cb3bb2008-10-15 21:25:50 +0000302}