1/**
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.assistedinject;
18
19import static com.google.common.base.Preconditions.checkState;
20import static com.google.common.collect.Iterables.getOnlyElement;
21
22import com.google.common.base.Objects;
23import com.google.common.collect.HashMultimap;
24import com.google.common.collect.ImmutableList;
25import com.google.common.collect.ImmutableMap;
26import com.google.common.collect.ImmutableSet;
27import com.google.common.collect.Iterables;
28import com.google.common.collect.Lists;
29import com.google.common.collect.Multimap;
30import com.google.common.collect.Sets;
31import com.google.inject.AbstractModule;
32import com.google.inject.Binder;
33import com.google.inject.Binding;
34import com.google.inject.ConfigurationException;
35import com.google.inject.Inject;
36import com.google.inject.Injector;
37import com.google.inject.Key;
38import com.google.inject.Module;
39import com.google.inject.Provider;
40import com.google.inject.ProvisionException;
41import com.google.inject.Scopes;
42import com.google.inject.TypeLiteral;
43import com.google.inject.internal.Annotations;
44import com.google.inject.internal.BytecodeGen;
45import com.google.inject.internal.Errors;
46import com.google.inject.internal.ErrorsException;
47import com.google.inject.internal.UniqueAnnotations;
48import com.google.inject.internal.util.Classes;
49import com.google.inject.spi.BindingTargetVisitor;
50import com.google.inject.spi.Dependency;
51import com.google.inject.spi.HasDependencies;
52import com.google.inject.spi.InjectionPoint;
53import com.google.inject.spi.Message;
54import com.google.inject.spi.ProviderInstanceBinding;
55import com.google.inject.spi.ProviderWithExtensionVisitor;
56import com.google.inject.spi.Toolable;
57import com.google.inject.util.Providers;
58
59import java.lang.annotation.Annotation;
60import java.lang.reflect.Constructor;
61import java.lang.reflect.InvocationHandler;
62import java.lang.reflect.InvocationTargetException;
63import java.lang.reflect.Method;
64import java.lang.reflect.Modifier;
65import java.lang.reflect.Proxy;
66import java.util.Arrays;
67import java.util.Collection;
68import java.util.Collections;
69import java.util.HashSet;
70import java.util.List;
71import java.util.Map;
72import java.util.Set;
73import java.util.logging.Level;
74import java.util.logging.Logger;
75
76/**
77 * The newer implementation of factory provider. This implementation uses a child injector to
78 * create values.
79 *
80 * @author jessewilson@google.com (Jesse Wilson)
81 * @author dtm@google.com (Daniel Martin)
82 * @author schmitt@google.com (Peter Schmitt)
83 * @author sameb@google.com (Sam Berlin)
84 */
85final class FactoryProvider2 <F> implements InvocationHandler,
86    ProviderWithExtensionVisitor<F>, HasDependencies, AssistedInjectBinding<F> {
87
88  /** A constant annotation to denote the return value, instead of creating a new one each time. */
89  static final Annotation RETURN_ANNOTATION = UniqueAnnotations.create();
90
91  // use the logger under a well-known name, not FactoryProvider2
92  static final Logger logger = Logger.getLogger(AssistedInject.class.getName());
93
94  /** if a factory method parameter isn't annotated, it gets this annotation. */
95  static final Assisted DEFAULT_ANNOTATION = new Assisted() {
96    public String value() {
97      return "";
98    }
99
100    public Class<? extends Annotation> annotationType() {
101      return Assisted.class;
102    }
103
104    @Override public boolean equals(Object o) {
105      return o instanceof Assisted && ((Assisted) o).value().isEmpty();
106    }
107
108    @Override public int hashCode() {
109      return 127 * "value".hashCode() ^ "".hashCode();
110    }
111
112    @Override public String toString() {
113      return "@" + Assisted.class.getName() + "(value=)";
114    }
115  };
116
117  /** All the data necessary to perform an assisted inject. */
118  private static class AssistData implements AssistedMethod {
119    /** the constructor the implementation is constructed with. */
120    final Constructor<?> constructor;
121    /** the return type in the factory method that the constructor is bound to. */
122    final Key<?> returnType;
123    /** the parameters in the factory method associated with this data. */
124    final ImmutableList<Key<?>> paramTypes;
125    /** the type of the implementation constructed */
126    final TypeLiteral<?> implementationType;
127
128    /** All non-assisted dependencies required by this method. */
129    final Set<Dependency<?>> dependencies;
130    /** The factory method associated with this data*/
131    final Method factoryMethod;
132
133    /** true if {@link #isValidForOptimizedAssistedInject} returned true. */
134    final boolean optimized;
135    /** the list of optimized providers, empty if not optimized. */
136    final List<ThreadLocalProvider> providers;
137    /** used to perform optimized factory creations. */
138    volatile Binding<?> cachedBinding; // TODO: volatile necessary?
139
140    AssistData(Constructor<?> constructor, Key<?> returnType, ImmutableList<Key<?>> paramTypes,
141        TypeLiteral<?> implementationType, Method factoryMethod,
142        Set<Dependency<?>> dependencies,
143        boolean optimized, List<ThreadLocalProvider> providers) {
144      this.constructor = constructor;
145      this.returnType = returnType;
146      this.paramTypes = paramTypes;
147      this.implementationType = implementationType;
148      this.factoryMethod = factoryMethod;
149      this.dependencies = dependencies;
150      this.optimized = optimized;
151      this.providers = providers;
152    }
153
154    @Override
155    public String toString() {
156      return Objects.toStringHelper(getClass())
157        .add("ctor", constructor)
158        .add("return type", returnType)
159        .add("param type", paramTypes)
160        .add("implementation type", implementationType)
161        .add("dependencies", dependencies)
162        .add("factory method", factoryMethod)
163        .add("optimized", optimized)
164        .add("providers", providers)
165        .add("cached binding", cachedBinding)
166        .toString();
167    }
168
169    public Set<Dependency<?>> getDependencies() {
170      return dependencies;
171    }
172
173    public Method getFactoryMethod() {
174      return factoryMethod;
175    }
176
177    public Constructor<?> getImplementationConstructor() {
178      return constructor;
179    }
180
181    public TypeLiteral<?> getImplementationType() {
182      return implementationType;
183    }
184  }
185
186  /** Mapping from method to the data about how the method will be assisted. */
187  private final ImmutableMap<Method, AssistData> assistDataByMethod;
188
189  /** Mapping from method to method handle, for generated default methods. */
190  private final ImmutableMap<Method, MethodHandleWrapper> methodHandleByMethod;
191
192  /** the hosting injector, or null if we haven't been initialized yet */
193  private Injector injector;
194
195  /** the factory interface, implemented and provided */
196  private final F factory;
197
198  /** The key that this is bound to. */
199  private final Key<F> factoryKey;
200
201  /** The binding collector, for equality/hashing purposes. */
202  private final BindingCollector collector;
203
204  /**
205   * @param factoryKey a key for a Java interface that defines one or more create methods.
206   * @param collector binding configuration that maps method return types to
207   *    implementation types.
208   */
209  FactoryProvider2(Key<F> factoryKey, BindingCollector collector) {
210    this.factoryKey = factoryKey;
211    this.collector = collector;
212
213    TypeLiteral<F> factoryType = factoryKey.getTypeLiteral();
214    Errors errors = new Errors();
215
216    @SuppressWarnings("unchecked") // we imprecisely treat the class literal of T as a Class<T>
217    Class<F> factoryRawType = (Class<F>) (Class<?>) factoryType.getRawType();
218
219    try {
220      if(!factoryRawType.isInterface()) {
221        throw errors.addMessage("%s must be an interface.", factoryRawType).toException();
222      }
223
224      Multimap<String, Method> defaultMethods = HashMultimap.create();
225      Multimap<String, Method> otherMethods = HashMultimap.create();
226      ImmutableMap.Builder<Method, AssistData> assistDataBuilder = ImmutableMap.builder();
227      // TODO: also grab methods from superinterfaces
228      for (Method method : factoryRawType.getMethods()) {
229        // Skip default methods that java8 may have created.
230        if (isDefault(method) && (method.isBridge() || method.isSynthetic())) {
231          // Even synthetic default methods need the return type validation...
232          // unavoidable consequence of javac8. :-(
233          validateFactoryReturnType(errors, method.getReturnType(), factoryRawType);
234          defaultMethods.put(method.getName(), method);
235          continue;
236        }
237        otherMethods.put(method.getName(), method);
238
239        TypeLiteral<?> returnTypeLiteral = factoryType.getReturnType(method);
240        Key<?> returnType;
241        try {
242          returnType = Annotations.getKey(returnTypeLiteral, method, method.getAnnotations(), errors);
243        } catch(ConfigurationException ce) {
244          // If this was an error due to returnTypeLiteral not being specified, rephrase
245          // it as our factory not being specified, so it makes more sense to users.
246          if(isTypeNotSpecified(returnTypeLiteral, ce)) {
247            throw errors.keyNotFullySpecified(TypeLiteral.get(factoryRawType)).toException();
248          } else {
249            throw ce;
250          }
251        }
252        validateFactoryReturnType(errors, returnType.getTypeLiteral().getRawType(), factoryRawType);
253        List<TypeLiteral<?>> params = factoryType.getParameterTypes(method);
254        Annotation[][] paramAnnotations = method.getParameterAnnotations();
255        int p = 0;
256        List<Key<?>> keys = Lists.newArrayList();
257        for (TypeLiteral<?> param : params) {
258          Key<?> paramKey = Annotations.getKey(param, method, paramAnnotations[p++], errors);
259          Class<?> underlylingType = paramKey.getTypeLiteral().getRawType();
260          if (underlylingType.equals(Provider.class)
261              || underlylingType.equals(javax.inject.Provider.class)) {
262            errors.addMessage("A Provider may not be a type in a factory method of an AssistedInject."
263                    + "\n  Offending instance is parameter [%s] with key [%s] on method [%s]",
264                    p, paramKey, method);
265          }
266          keys.add(assistKey(method, paramKey, errors));
267        }
268        ImmutableList<Key<?>> immutableParamList = ImmutableList.copyOf(keys);
269
270        // try to match up the method to the constructor
271        TypeLiteral<?> implementation = collector.getBindings().get(returnType);
272        if(implementation == null) {
273          implementation = returnType.getTypeLiteral();
274        }
275        Class<? extends Annotation> scope =
276            Annotations.findScopeAnnotation(errors, implementation.getRawType());
277        if (scope != null) {
278          errors.addMessage("Found scope annotation [%s] on implementation class "
279              + "[%s] of AssistedInject factory [%s].\nThis is not allowed, please"
280              + " remove the scope annotation.",
281              scope, implementation.getRawType(), factoryType);
282        }
283
284        InjectionPoint ctorInjectionPoint;
285        try {
286          ctorInjectionPoint =
287            findMatchingConstructorInjectionPoint(method, returnType, implementation, immutableParamList);
288        } catch(ErrorsException ee) {
289          errors.merge(ee.getErrors());
290          continue;
291        }
292
293        Constructor<?> constructor = (Constructor<?>) ctorInjectionPoint.getMember();
294        List<ThreadLocalProvider> providers = Collections.emptyList();
295        Set<Dependency<?>> deps = getDependencies(ctorInjectionPoint, implementation);
296        boolean optimized = false;
297        // Now go through all dependencies of the implementation and see if it is OK to
298        // use an optimized form of assistedinject2.  The optimized form requires that
299        // all injections directly inject the object itself (and not a Provider of the object,
300        // or an Injector), because it caches a single child injector and mutates the Provider
301        // of the arguments in a ThreadLocal.
302        if(isValidForOptimizedAssistedInject(deps, implementation.getRawType(), factoryType)) {
303          ImmutableList.Builder<ThreadLocalProvider> providerListBuilder = ImmutableList.builder();
304          for(int i = 0; i < params.size(); i++) {
305            providerListBuilder.add(new ThreadLocalProvider());
306          }
307          providers = providerListBuilder.build();
308          optimized = true;
309        }
310
311        AssistData data = new AssistData(constructor,
312            returnType,
313            immutableParamList,
314            implementation,
315            method,
316            removeAssistedDeps(deps),
317            optimized,
318            providers);
319        assistDataBuilder.put(method, data);
320      }
321
322      factory = factoryRawType.cast(Proxy.newProxyInstance(
323          BytecodeGen.getClassLoader(factoryRawType), new Class<?>[] {factoryRawType}, this));
324
325      // Now go back through default methods. Try to use MethodHandles to make things
326      // work.  If that doesn't work, fallback to trying to find compatible method
327      // signatures.
328      Map<Method, AssistData> dataSoFar = assistDataBuilder.build();
329      ImmutableMap.Builder<Method, MethodHandleWrapper> methodHandleBuilder = ImmutableMap.builder();
330      for (Map.Entry<String, Method> entry : defaultMethods.entries()) {
331        Method defaultMethod = entry.getValue();
332        MethodHandleWrapper handle = MethodHandleWrapper.create(defaultMethod, factory);
333        if (handle != null) {
334          methodHandleBuilder.put(defaultMethod, handle);
335        } else {
336          boolean foundMatch = false;
337          for (Method otherMethod : otherMethods.get(defaultMethod.getName())) {
338            if (dataSoFar.containsKey(otherMethod) && isCompatible(defaultMethod, otherMethod)) {
339              if (foundMatch) {
340                errors.addMessage("Generated default method %s with parameters %s is"
341                    + " signature-compatible with more than one non-default method."
342                    + " Unable to create factory. As a workaround, remove the override"
343                    + " so javac stops generating a default method.",
344                    defaultMethod, Arrays.asList(defaultMethod.getParameterTypes()));
345              } else {
346                assistDataBuilder.put(defaultMethod, dataSoFar.get(otherMethod));
347                foundMatch = true;
348              }
349            }
350          }
351          if (!foundMatch) {
352            throw new IllegalStateException("Can't find method compatible with: " + defaultMethod);
353          }
354        }
355      }
356
357      // If we generated any errors (from finding matching constructors, for instance), throw an exception.
358      if(errors.hasErrors()) {
359        throw errors.toException();
360      }
361
362      assistDataByMethod = assistDataBuilder.build();
363      methodHandleByMethod = methodHandleBuilder.build();
364    } catch (ErrorsException e) {
365      throw new ConfigurationException(e.getErrors().getMessages());
366    }
367  }
368
369  static boolean isDefault(Method method) {
370    // Per the javadoc, default methods are non-abstract, public, non-static.
371    // They're also in interfaces, but we can guarantee that already since we only act
372    // on interfaces.
373    return (method.getModifiers() & (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC))
374        == Modifier.PUBLIC;
375  }
376
377  private boolean isCompatible(Method src, Method dst) {
378    if (!src.getReturnType().isAssignableFrom(dst.getReturnType())) {
379      return false;
380    }
381    Class<?>[] srcParams = src.getParameterTypes();
382    Class<?>[] dstParams = dst.getParameterTypes();
383    if (srcParams.length != dstParams.length) {
384      return false;
385    }
386    for (int i = 0; i < srcParams.length; i++) {
387      if (!srcParams[i].isAssignableFrom(dstParams[i])) {
388        return false;
389      }
390    }
391    return true;
392  }
393
394  public F get() {
395    return factory;
396  }
397
398  public Set<Dependency<?>> getDependencies() {
399    Set<Dependency<?>> combinedDeps = new HashSet<Dependency<?>>();
400    for(AssistData data : assistDataByMethod.values()) {
401      combinedDeps.addAll(data.dependencies);
402    }
403    return ImmutableSet.copyOf(combinedDeps);
404  }
405
406  public Key<F> getKey() {
407    return factoryKey;
408  }
409
410  // Safe cast because values are typed to AssistedData, which is an AssistedMethod, and
411  // the collection is immutable.
412  @SuppressWarnings("unchecked")
413  public Collection<AssistedMethod> getAssistedMethods() {
414    return (Collection<AssistedMethod>) (Collection<?>) assistDataByMethod.values();
415  }
416
417  @SuppressWarnings("unchecked")
418  public <T, V> V acceptExtensionVisitor(BindingTargetVisitor<T, V> visitor,
419      ProviderInstanceBinding<? extends T> binding) {
420    if (visitor instanceof AssistedInjectTargetVisitor) {
421      return ((AssistedInjectTargetVisitor<T, V>)visitor).visit((AssistedInjectBinding<T>)this);
422    }
423    return visitor.visit(binding);
424  }
425
426  private void validateFactoryReturnType(Errors errors, Class<?> returnType, Class<?> factoryType) {
427    if (Modifier.isPublic(factoryType.getModifiers())
428        && !Modifier.isPublic(returnType.getModifiers())) {
429      errors.addMessage("%s is public, but has a method that returns a non-public type: %s. "
430          + "Due to limitations with java.lang.reflect.Proxy, this is not allowed. "
431          + "Please either make the factory non-public or the return type public.",
432          factoryType, returnType);
433    }
434  }
435
436  /**
437   * Returns true if the ConfigurationException is due to an error of TypeLiteral not being fully
438   * specified.
439   */
440  private boolean isTypeNotSpecified(TypeLiteral<?> typeLiteral, ConfigurationException ce) {
441    Collection<Message> messages = ce.getErrorMessages();
442    if (messages.size() == 1) {
443      Message msg = Iterables.getOnlyElement(
444          new Errors().keyNotFullySpecified(typeLiteral).getMessages());
445      return msg.getMessage().equals(Iterables.getOnlyElement(messages).getMessage());
446    } else {
447      return false;
448    }
449  }
450
451  /**
452   * Finds a constructor suitable for the method.  If the implementation contained any constructors
453   * marked with {@link AssistedInject}, this requires all {@link Assisted} parameters to exactly
454   * match the parameters (in any order) listed in the method.  Otherwise, if no
455   * {@link AssistedInject} constructors exist, this will default to looking for an
456   * {@literal @}{@link Inject} constructor.
457   */
458  private <T> InjectionPoint findMatchingConstructorInjectionPoint(
459      Method method, Key<?> returnType, TypeLiteral<T> implementation, List<Key<?>> paramList)
460      throws ErrorsException {
461    Errors errors = new Errors(method);
462    if(returnType.getTypeLiteral().equals(implementation)) {
463      errors = errors.withSource(implementation);
464    } else {
465      errors = errors.withSource(returnType).withSource(implementation);
466    }
467
468    Class<?> rawType = implementation.getRawType();
469    if (Modifier.isInterface(rawType.getModifiers())) {
470      errors.addMessage(
471          "%s is an interface, not a concrete class.  Unable to create AssistedInject factory.",
472          implementation);
473      throw errors.toException();
474    } else if (Modifier.isAbstract(rawType.getModifiers())) {
475      errors.addMessage(
476          "%s is abstract, not a concrete class.  Unable to create AssistedInject factory.",
477          implementation);
478      throw errors.toException();
479    } else if (Classes.isInnerClass(rawType)) {
480      errors.cannotInjectInnerClass(rawType);
481      throw errors.toException();
482    }
483
484    Constructor<?> matchingConstructor = null;
485    boolean anyAssistedInjectConstructors = false;
486    // Look for AssistedInject constructors...
487    for (Constructor<?> constructor : rawType.getDeclaredConstructors()) {
488      if (constructor.isAnnotationPresent(AssistedInject.class)) {
489        anyAssistedInjectConstructors = true;
490        if (constructorHasMatchingParams(implementation, constructor, paramList, errors)) {
491          if (matchingConstructor != null) {
492            errors
493                .addMessage(
494                    "%s has more than one constructor annotated with @AssistedInject"
495                        + " that matches the parameters in method %s.  Unable to create "
496                        + "AssistedInject factory.",
497                    implementation, method);
498            throw errors.toException();
499          } else {
500            matchingConstructor = constructor;
501          }
502        }
503      }
504    }
505
506    if(!anyAssistedInjectConstructors) {
507      // If none existed, use @Inject.
508      try {
509        return InjectionPoint.forConstructorOf(implementation);
510      } catch(ConfigurationException e) {
511        errors.merge(e.getErrorMessages());
512        throw errors.toException();
513      }
514    } else {
515      // Otherwise, use it or fail with a good error message.
516      if(matchingConstructor != null) {
517          // safe because we got the constructor from this implementation.
518          @SuppressWarnings("unchecked")
519          InjectionPoint ip = InjectionPoint.forConstructor(
520              (Constructor<? super T>) matchingConstructor, implementation);
521          return ip;
522      } else {
523        errors.addMessage(
524            "%s has @AssistedInject constructors, but none of them match the"
525            + " parameters in method %s.  Unable to create AssistedInject factory.",
526            implementation, method);
527        throw errors.toException();
528      }
529    }
530  }
531
532  /**
533   * Matching logic for constructors annotated with AssistedInject.
534   * This returns true if and only if all @Assisted parameters in the
535   * constructor exactly match (in any order) all @Assisted parameters
536   * the method's parameter.
537   */
538  private boolean constructorHasMatchingParams(TypeLiteral<?> type,
539      Constructor<?> constructor, List<Key<?>> paramList, Errors errors)
540      throws ErrorsException {
541    List<TypeLiteral<?>> params = type.getParameterTypes(constructor);
542    Annotation[][] paramAnnotations = constructor.getParameterAnnotations();
543    int p = 0;
544    List<Key<?>> constructorKeys = Lists.newArrayList();
545    for (TypeLiteral<?> param : params) {
546      Key<?> paramKey = Annotations.getKey(param, constructor, paramAnnotations[p++],
547          errors);
548      constructorKeys.add(paramKey);
549    }
550    // Require that every key exist in the constructor to match up exactly.
551    for (Key<?> key : paramList) {
552      // If it didn't exist in the constructor set, we can't use it.
553      if (!constructorKeys.remove(key)) {
554        return false;
555      }
556    }
557    // If any keys remain and their annotation is Assisted, we can't use it.
558    for (Key<?> key : constructorKeys) {
559      if (key.getAnnotationType() == Assisted.class) {
560        return false;
561      }
562    }
563    // All @Assisted params match up to the method's parameters.
564    return true;
565  }
566
567  /** Calculates all dependencies required by the implementation and constructor. */
568  private Set<Dependency<?>> getDependencies(InjectionPoint ctorPoint, TypeLiteral<?> implementation) {
569    ImmutableSet.Builder<Dependency<?>> builder = ImmutableSet.builder();
570    builder.addAll(ctorPoint.getDependencies());
571    if (!implementation.getRawType().isInterface()) {
572      for (InjectionPoint ip : InjectionPoint.forInstanceMethodsAndFields(implementation)) {
573        builder.addAll(ip.getDependencies());
574      }
575    }
576    return builder.build();
577  }
578
579  /** Return all non-assisted dependencies. */
580  private Set<Dependency<?>> removeAssistedDeps(Set<Dependency<?>> deps) {
581    ImmutableSet.Builder<Dependency<?>> builder = ImmutableSet.builder();
582    for(Dependency<?> dep : deps) {
583      Class<?> annotationType = dep.getKey().getAnnotationType();
584      if (annotationType == null || !annotationType.equals(Assisted.class)) {
585        builder.add(dep);
586      }
587    }
588    return builder.build();
589  }
590
591  /**
592   * Returns true if all dependencies are suitable for the optimized version of AssistedInject. The
593   * optimized version caches the binding & uses a ThreadLocal Provider, so can only be applied if
594   * the assisted bindings are immediately provided. This looks for hints that the values may be
595   * lazily retrieved, by looking for injections of Injector or a Provider for the assisted values.
596   */
597  private boolean isValidForOptimizedAssistedInject(Set<Dependency<?>> dependencies,
598      Class<?> implementation, TypeLiteral<?> factoryType) {
599    Set<Dependency<?>> badDeps = null; // optimization: create lazily
600    for (Dependency<?> dep : dependencies) {
601      if (isInjectorOrAssistedProvider(dep)) {
602        if (badDeps == null) {
603          badDeps = Sets.newHashSet();
604        }
605        badDeps.add(dep);
606      }
607    }
608    if (badDeps != null && !badDeps.isEmpty()) {
609      logger.log(Level.WARNING, "AssistedInject factory {0} will be slow "
610          + "because {1} has assisted Provider dependencies or injects the Injector. "
611          + "Stop injecting @Assisted Provider<T> (instead use @Assisted T) "
612          + "or Injector to speed things up. (It will be a ~6500% speed bump!)  "
613          + "The exact offending deps are: {2}",
614          new Object[] {factoryType, implementation, badDeps} );
615      return false;
616    }
617    return true;
618  }
619
620  /**
621   * Returns true if the dependency is for {@link Injector} or if the dependency
622   * is a {@link Provider} for a parameter that is {@literal @}{@link Assisted}.
623   */
624  private boolean isInjectorOrAssistedProvider(Dependency<?> dependency) {
625    Class<?> annotationType = dependency.getKey().getAnnotationType();
626    if (annotationType != null && annotationType.equals(Assisted.class)) { // If it's assisted..
627      if (dependency.getKey().getTypeLiteral().getRawType().equals(Provider.class)) { // And a Provider...
628        return true;
629      }
630    } else if (dependency.getKey().getTypeLiteral().getRawType().equals(Injector.class)) { // If it's the Injector...
631      return true;
632    }
633    return false;
634  }
635
636  /**
637   * Returns a key similar to {@code key}, but with an {@literal @}Assisted binding annotation.
638   * This fails if another binding annotation is clobbered in the process. If the key already has
639   * the {@literal @}Assisted annotation, it is returned as-is to preserve any String value.
640   */
641  private <T> Key<T> assistKey(Method method, Key<T> key, Errors errors) throws ErrorsException {
642    if (key.getAnnotationType() == null) {
643      return Key.get(key.getTypeLiteral(), DEFAULT_ANNOTATION);
644    } else if (key.getAnnotationType() == Assisted.class) {
645      return key;
646    } else {
647      errors.withSource(method).addMessage(
648          "Only @Assisted is allowed for factory parameters, but found @%s",
649          key.getAnnotationType());
650      throw errors.toException();
651    }
652  }
653
654  /**
655   * At injector-creation time, we initialize the invocation handler. At this time we make sure
656   * all factory methods will be able to build the target types.
657   */
658  @Inject @Toolable
659  void initialize(Injector injector) {
660    if (this.injector != null) {
661      throw new ConfigurationException(ImmutableList.of(new Message(FactoryProvider2.class,
662          "Factories.create() factories may only be used in one Injector!")));
663    }
664
665    this.injector = injector;
666
667    for (Map.Entry<Method, AssistData> entry : assistDataByMethod.entrySet()) {
668      Method method = entry.getKey();
669      AssistData data = entry.getValue();
670      Object[] args;
671      if(!data.optimized) {
672        args = new Object[method.getParameterTypes().length];
673        Arrays.fill(args, "dummy object for validating Factories");
674      } else {
675        args = null; // won't be used -- instead will bind to data.providers.
676      }
677      getBindingFromNewInjector(method, args, data); // throws if the binding isn't properly configured
678    }
679  }
680
681  /**
682   * Creates a child injector that binds the args, and returns the binding for the method's result.
683   */
684  public Binding<?> getBindingFromNewInjector(
685      final Method method, final Object[] args, final AssistData data) {
686    checkState(injector != null,
687        "Factories.create() factories cannot be used until they're initialized by Guice.");
688
689    final Key<?> returnType = data.returnType;
690
691    // We ignore any pre-existing binding annotation.
692    final Key<?> returnKey = Key.get(returnType.getTypeLiteral(), RETURN_ANNOTATION);
693
694    Module assistedModule = new AbstractModule() {
695      @Override
696      @SuppressWarnings({
697        "unchecked", "rawtypes"}) // raw keys are necessary for the args array and return value
698      protected void configure() {
699        Binder binder = binder().withSource(method);
700
701        int p = 0;
702        if(!data.optimized) {
703          for (Key<?> paramKey : data.paramTypes) {
704            // Wrap in a Provider to cover null, and to prevent Guice from injecting the parameter
705            binder.bind((Key) paramKey).toProvider(Providers.of(args[p++]));
706          }
707        } else {
708          for (Key<?> paramKey : data.paramTypes) {
709            // Bind to our ThreadLocalProviders.
710            binder.bind((Key) paramKey).toProvider(data.providers.get(p++));
711          }
712        }
713
714        Constructor constructor = data.constructor;
715        // Constructor *should* always be non-null here,
716        // but if it isn't, we'll end up throwing a fairly good error
717        // message for the user.
718        if(constructor != null) {
719          binder.bind(returnKey)
720              .toConstructor(constructor, (TypeLiteral)data.implementationType)
721              .in(Scopes.NO_SCOPE); // make sure we erase any scope on the implementation type
722        }
723      }
724    };
725
726    Injector forCreate = injector.createChildInjector(assistedModule);
727    Binding<?> binding = forCreate.getBinding(returnKey);
728    // If we have providers cached in data, cache the binding for future optimizations.
729    if(data.optimized) {
730      data.cachedBinding = binding;
731    }
732    return binding;
733  }
734
735  /**
736   * When a factory method is invoked, we create a child injector that binds all parameters, then
737   * use that to get an instance of the return type.
738   */
739  public Object invoke(Object proxy, final Method method, final Object[] args) throws Throwable {
740    // If we setup a method handle earlier for this method, call it.
741    // This is necessary for default methods that java8 creates, so we
742    // can call the default method implementation (and not our proxied version of it).
743    if (methodHandleByMethod.containsKey(method)) {
744      return methodHandleByMethod.get(method).invokeWithArguments(args);
745    }
746
747    if (method.getDeclaringClass().equals(Object.class)) {
748      if ("equals".equals(method.getName())) {
749        return proxy == args[0];
750      } else if ("hashCode".equals(method.getName())) {
751        return System.identityHashCode(proxy);
752      } else {
753        return method.invoke(this, args);
754      }
755    }
756
757    AssistData data = assistDataByMethod.get(method);
758    checkState(data != null, "No data for method: %s", method);
759    Provider<?> provider;
760    if(data.cachedBinding != null) { // Try to get optimized form...
761      provider = data.cachedBinding.getProvider();
762    } else {
763      provider = getBindingFromNewInjector(method, args, data).getProvider();
764    }
765    try {
766      int p = 0;
767      for(ThreadLocalProvider tlp : data.providers) {
768        tlp.set(args[p++]);
769      }
770      return provider.get();
771    } catch (ProvisionException e) {
772      // if this is an exception declared by the factory method, throw it as-is
773      if (e.getErrorMessages().size() == 1) {
774        Message onlyError = getOnlyElement(e.getErrorMessages());
775        Throwable cause = onlyError.getCause();
776        if (cause != null && canRethrow(method, cause)) {
777          throw cause;
778        }
779      }
780      throw e;
781    } finally {
782      for(ThreadLocalProvider tlp : data.providers) {
783        tlp.remove();
784      }
785    }
786  }
787
788  @Override public String toString() {
789    return factory.getClass().getInterfaces()[0].getName();
790  }
791
792  @Override
793  public int hashCode() {
794    return Objects.hashCode(factoryKey, collector);
795  }
796
797  @Override public boolean equals(Object obj) {
798    if (!(obj instanceof FactoryProvider2)) {
799      return false;
800    }
801    FactoryProvider2<?> other = (FactoryProvider2<?>) obj;
802    return factoryKey.equals(other.factoryKey) && Objects.equal(collector, other.collector);
803  }
804
805  /** Returns true if {@code thrown} can be thrown by {@code invoked} without wrapping. */
806  static boolean canRethrow(Method invoked, Throwable thrown) {
807    if (thrown instanceof Error || thrown instanceof RuntimeException) {
808      return true;
809    }
810
811    for (Class<?> declared : invoked.getExceptionTypes()) {
812      if (declared.isInstance(thrown)) {
813        return true;
814      }
815    }
816
817    return false;
818  }
819
820  // not <T> because we'll never know and this is easier than suppressing warnings.
821  private static class ThreadLocalProvider extends ThreadLocal<Object> implements Provider<Object> {
822    @Override
823    protected Object initialValue() {
824      throw new IllegalStateException(
825          "Cannot use optimized @Assisted provider outside the scope of the constructor."
826              + " (This should never happen.  If it does, please report it.)");
827    }
828  }
829
830  /** Wrapper around MethodHandles/MethodHandle, so we can compile+run on java6. */
831  private static class MethodHandleWrapper {
832    static final int ALL_MODES = Modifier.PRIVATE
833        | Modifier.STATIC /* package */
834        | Modifier.PUBLIC
835        | Modifier.PROTECTED;
836
837    static final Method unreflectSpecial;
838    static final Method bindTo;
839    static final Method invokeWithArguments;
840    static final Constructor<?> lookupCxtor;
841    static final boolean valid;
842
843    static {
844      Method unreflectSpecialTmp = null;
845      Method bindToTmp = null;
846      Method invokeWithArgumentsTmp = null;
847      boolean validTmp = false;
848      Constructor<?> lookupCxtorTmp = null;
849      try {
850        Class<?> lookupClass = Class.forName("java.lang.invoke.MethodHandles$Lookup");
851        unreflectSpecialTmp = lookupClass.getMethod("unreflectSpecial", Method.class, Class.class);
852        Class<?> methodHandleClass = Class.forName("java.lang.invoke.MethodHandle");
853        bindToTmp = methodHandleClass.getMethod("bindTo", Object.class);
854        invokeWithArgumentsTmp = methodHandleClass.getMethod("invokeWithArguments", Object[].class);
855        lookupCxtorTmp = lookupClass.getDeclaredConstructor(Class.class, int.class);
856        lookupCxtorTmp.setAccessible(true);
857        validTmp = true;
858      } catch (Exception invalid) {
859        // Ignore the exception, store the values & exit early in create(..) if invalid.
860      }
861
862      // Store refs to later.
863      valid = validTmp;
864      unreflectSpecial = unreflectSpecialTmp;
865      bindTo = bindToTmp;
866      invokeWithArguments = invokeWithArgumentsTmp;
867      lookupCxtor = lookupCxtorTmp;
868    }
869
870    static MethodHandleWrapper create(Method method, Object proxy) {
871      if (!valid) {
872        return null;
873      }
874      try {
875        Class<?> declaringClass = method.getDeclaringClass();
876        // Note: this isn't a public API, but we need to use it in order to call default methods.
877        Object lookup = lookupCxtor.newInstance(declaringClass, ALL_MODES);
878        method.setAccessible(true);
879        // These are part of the public API, but we use reflection since we run on java6
880        // and they were introduced in java7.
881        lookup = unreflectSpecial.invoke(lookup, method, declaringClass);
882        Object handle = bindTo.invoke(lookup, proxy);
883        return new MethodHandleWrapper(handle);
884      } catch (InvocationTargetException ite) {
885        return null;
886      } catch (IllegalAccessException iae) {
887        return null;
888      } catch (InstantiationException ie) {
889        return null;
890      }
891    }
892
893    final Object handle;
894
895    MethodHandleWrapper(Object handle) {
896      this.handle = handle;
897    }
898
899    Object invokeWithArguments(Object[] args) throws Exception {
900      // We must cast the args to an object so the Object[] is the first param,
901      // as opposed to each individual varargs param.
902      return invokeWithArguments.invoke(handle, (Object) args);
903    }
904
905    @Override public String toString() {
906      return handle.toString();
907    }
908  }
909}
910