Last of the planned major changes to the SPI. Aside from Javadoc I consider the SPI to be complete for v2. (with a possible addition of a ProviderMethodBinding feature also)


This change changes the interface of 3 methods of BindingTargetVisitor to take a Set<InjectionPoint>, which replaces the HasInjections interface.

Also, making the ProviderMethods class non-public.

Also, adding extra checks for serialization. I've still got to fix serialization of TypeLiteral.

git-svn-id: https://google-guice.googlecode.com/svn/trunk@616 d779f126-a31b-0410-b53b-1d3aecad763e
diff --git a/src/com/google/inject/BindingAnnotation.java b/src/com/google/inject/BindingAnnotation.java
index 707b3e7..110630b 100644
--- a/src/com/google/inject/BindingAnnotation.java
+++ b/src/com/google/inject/BindingAnnotation.java
@@ -28,7 +28,7 @@
  *
  * <pre>
  *   {@code @}Retention(RUNTIME)
- *   {@code @}Target({ FIELD, PARAMETER })
+ *   {@code @}Target({ FIELD, PARAMETER, METHOD })
  *   {@code @}BindingAnnotation
  *   public {@code @}interface Transactional {}
  * </pre>
diff --git a/src/com/google/inject/BindingProcessor.java b/src/com/google/inject/BindingProcessor.java
index 9ed2f37..430dd6b 100644
--- a/src/com/google/inject/BindingProcessor.java
+++ b/src/com/google/inject/BindingProcessor.java
@@ -18,10 +18,12 @@
 
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
+import com.google.inject.internal.Annotations;
 import com.google.inject.internal.Errors;
 import com.google.inject.internal.ErrorsException;
 import com.google.inject.spi.BindingScopingVisitor;
 import com.google.inject.spi.BindingTargetVisitor;
+import com.google.inject.spi.InjectionPoint;
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Type;
@@ -119,29 +121,24 @@
     });
 
     command.acceptTargetVisitor(new BindingTargetVisitor<T, Void>() {
-      public Void visitInstance(T instance) {
-        if (instance == null) {
-          errors.cannotBindToNullInstance();
-          putBinding(invalidBinding(injector, key, source));
-          return null;
-        }
-
+      public Void visitInstance(T instance, Set<InjectionPoint> injectionPoints) {
         ConstantFactory<? extends T> factory = new ConstantFactory<T>(instance);
-        memberInjector.requestInjection(instance, source);
-        InternalFactory<? extends T> scopedFactory
-            = Scopes.scope(key, injector, factory, scope);
-        putBinding(new InstanceBindingImpl<T>(injector, key, source, scopedFactory, instance));
+        memberInjector.requestInjection(instance, source, injectionPoints);
+        InternalFactory<? extends T> scopedFactory = Scopes.scope(key, injector, factory, scope);
+        putBinding(new InstanceBindingImpl<T>(injector, key, source, scopedFactory, injectionPoints,
+            instance));
         return null;
       }
 
-      public Void visitProvider(Provider<? extends T> provider) {
+      public Void visitProvider(Provider<? extends T> provider,
+          Set<InjectionPoint> injectionPoints) {
         InternalFactoryToProviderAdapter<? extends T> factory
             = new InternalFactoryToProviderAdapter<T>(provider, source);
-        memberInjector.requestInjection(provider, source);
+        memberInjector.requestInjection(provider, source, injectionPoints);
         InternalFactory<? extends T> scopedFactory
             = Scopes.scope(key, injector, factory, scope);
-        putBinding(new ProviderInstanceBindingImpl<T>(
-                injector, key, source, scopedFactory, scope, provider, loadStrategy));
+        putBinding(new ProviderInstanceBindingImpl<T>(injector, key, source, scopedFactory, scope,
+            provider, loadStrategy, injectionPoints));
         return null;
       }
 
@@ -214,7 +211,8 @@
         throw new IllegalArgumentException("Cannot apply a non-module element");
       }
 
-      public Void visitConstructor(Constructor<? extends T> constructor) {
+      public Void visitConstructor(Constructor<? extends T> constructor,
+          Set<InjectionPoint> injectionPoints) {
         throw new IllegalArgumentException("Cannot apply a non-module element");
       }
 
@@ -227,7 +225,7 @@
   }
 
   private <T> void validateKey(Object source, Key<T> key) {
-    Scopes.checkForMisplacedScopeAnnotations(key.getRawType(), source, errors);
+    Annotations.checkForMisplacedScopeAnnotations(key.getRawType(), source, errors);
   }
 
   <T> InvalidBindingImpl<T> invalidBinding(InjectorImpl injector, Key<T> key, Object source) {
diff --git a/src/com/google/inject/ClassBindingImpl.java b/src/com/google/inject/ClassBindingImpl.java
index cd13461..cb2f8f8 100644
--- a/src/com/google/inject/ClassBindingImpl.java
+++ b/src/com/google/inject/ClassBindingImpl.java
@@ -16,27 +16,27 @@
 
 package com.google.inject;
 
+import static com.google.common.base.Preconditions.checkState;
 import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Sets;
+import com.google.inject.InjectorImpl.LateBoundConstructor;
 import com.google.inject.internal.Errors;
 import com.google.inject.internal.ErrorsException;
 import com.google.inject.internal.ToStringBuilder;
 import com.google.inject.spi.BindingTargetVisitor;
-import com.google.inject.spi.HasInjections;
 import com.google.inject.spi.InjectionPoint;
-import java.util.Set;
 
 /**
  *
  *
  */
-class ClassBindingImpl<T> extends BindingImpl<T> implements HasInjections {
+class ClassBindingImpl<T> extends BindingImpl<T> {
 
-  private final InjectorImpl.LateBoundConstructor<T> lateBoundConstructor;
+  private final LateBoundConstructor<T> lateBoundConstructor;
+  private ImmutableSet<InjectionPoint> injectionPoints;
 
   ClassBindingImpl(InjectorImpl injector, Key<T> key, Object source,
       InternalFactory<? extends T> internalFactory, Scope scope,
-      InjectorImpl.LateBoundConstructor<T> lateBoundConstructor,
+      LateBoundConstructor<T> lateBoundConstructor,
       LoadStrategy loadStrategy) {
     super(injector, key, source, internalFactory, scope, loadStrategy);
     this.lateBoundConstructor = lateBoundConstructor;
@@ -44,10 +44,12 @@
 
   @Override void initialize(InjectorImpl injector, Errors errors) throws ErrorsException {
     lateBoundConstructor.bind(injector, getBoundClass(), errors);
+    injectionPoints = lateBoundConstructor.constructorInjector.getInjectionPoints();
   }
 
   public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
-    return visitor.visitConstructor(lateBoundConstructor.getConstructor());
+    checkState(injectionPoints != null, "not initialized");
+    return visitor.visitConstructor(lateBoundConstructor.getConstructor(), injectionPoints);
   }
 
   @SuppressWarnings("unchecked")
@@ -56,26 +58,6 @@
     return (Class<T>) key.getRawType();
   }
 
-  public Set<InjectionPoint> getInjectionPoints() {
-    if (lateBoundConstructor == null) {
-      throw new AssertionError();
-    }
-
-    Set<InjectionPoint> injectionPoints = Sets.newLinkedHashSet();
-
-    Class<T> boundClass = getBoundClass();
-    ConstructorInjector<T> constructor = lateBoundConstructor.constructorInjector;
-    injectionPoints.add(constructor.getInjectionPoint());
-
-    try {
-      injectionPoints.addAll(injector.getFieldAndMethodInjectionsFor(boundClass));
-    } catch (ErrorsException e) {
-      throw new AssertionError("This should have failed at CreationTime");
-    }
-
-    return ImmutableSet.copyOf(injectionPoints);
-  }
-
   @Override public String toString() {
     return new ToStringBuilder(Binding.class)
         .add("class", getBoundClass())
diff --git a/src/com/google/inject/ConstructorInjector.java b/src/com/google/inject/ConstructorInjector.java
index 1db97ef..de36367 100644
--- a/src/com/google/inject/ConstructorInjector.java
+++ b/src/com/google/inject/ConstructorInjector.java
@@ -16,6 +16,7 @@
 
 package com.google.inject;
 
+import com.google.common.collect.ImmutableSet;
 import com.google.inject.InjectorImpl.SingleMemberInjector;
 import com.google.inject.InjectorImpl.SingleParameterInjector;
 import com.google.inject.internal.Errors;
@@ -41,8 +42,7 @@
     this.implementation = implementation;
     constructionProxy = injector.reflection.getConstructionProxy(errors, implementation);
     parameterInjectors = createParameterInjector(errors, injector, constructionProxy);
-    List<SingleMemberInjector> memberInjectorsList
-        = injector.getMemberInjectors(implementation, errors);
+    List<SingleMemberInjector> memberInjectorsList = injector.injectors.get(implementation, errors);
     memberInjectors = memberInjectorsList.toArray(
         new SingleMemberInjector[memberInjectorsList.size()]);
   }
@@ -53,8 +53,13 @@
     return injector.getParametersInjectors(constructionProxy.getInjectionPoint(), errors);
   }
 
-  InjectionPoint getInjectionPoint() {
-    return constructionProxy.getInjectionPoint();
+  ImmutableSet<InjectionPoint> getInjectionPoints() {
+    InjectionPoint[] injectionPoints = new InjectionPoint[memberInjectors.length + 1];
+    injectionPoints[0] = constructionProxy.getInjectionPoint();
+    for (int i = 0; i < memberInjectors.length; i++) {
+      injectionPoints[i+1] = memberInjectors[i].getInjectionPoint();
+    }
+    return ImmutableSet.of(injectionPoints);
   }
 
   /**
diff --git a/src/com/google/inject/CreationException.java b/src/com/google/inject/CreationException.java
index 799a3bc..94440ba 100644
--- a/src/com/google/inject/CreationException.java
+++ b/src/com/google/inject/CreationException.java
@@ -58,4 +58,6 @@
   @Override public String getMessage() {
     return Errors.format("Guice configuration errors", errorMessages);
   }
+
+  private static final long serialVersionUID = 0;
 }
diff --git a/src/com/google/inject/CreationTimeMemberInjector.java b/src/com/google/inject/CreationTimeMemberInjector.java
index c81b762..15d9639 100644
--- a/src/com/google/inject/CreationTimeMemberInjector.java
+++ b/src/com/google/inject/CreationTimeMemberInjector.java
@@ -20,8 +20,10 @@
 import com.google.common.collect.Maps;
 import com.google.inject.internal.Errors;
 import com.google.inject.internal.ErrorsException;
+import com.google.inject.spi.InjectionPoint;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.Set;
 import java.util.concurrent.CountDownLatch;
 
 /**
@@ -51,7 +53,8 @@
    *      @Inject).
    * @param source the source location that this injection was requested
    */
-  public void requestInjection(Object instance, Object source) {
+  public void requestInjection(Object instance, Object source,
+      Set<InjectionPoint> injectionPoints) {
     checkNotNull(source);
     outstandingInjections.put(instance, source);
   }
@@ -65,7 +68,7 @@
       try {
         Object toInject = entry.getKey();
         Object source = entry.getValue();
-        injector.getMemberInjectors(toInject.getClass(), errors.withSource(source));
+        injector.injectors.get(toInject.getClass(), errors.withSource(source));
       } catch (ErrorsException e) {
         errors.merge(e.getErrors());
       }
diff --git a/src/com/google/inject/InjectionRequestProcessor.java b/src/com/google/inject/InjectionRequestProcessor.java
index 7b55a48..f7c8042 100644
--- a/src/com/google/inject/InjectionRequestProcessor.java
+++ b/src/com/google/inject/InjectionRequestProcessor.java
@@ -17,11 +17,12 @@
 package com.google.inject;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
 import com.google.inject.InjectorImpl.SingleMemberInjector;
+import com.google.inject.internal.ConfigurationException;
 import com.google.inject.internal.Errors;
 import com.google.inject.internal.ErrorsException;
-import com.google.inject.internal.ConfigurationException;
 import com.google.inject.spi.InjectionPoint;
 import com.google.inject.spi.InjectionRequest;
 import com.google.inject.spi.StaticInjectionRequest;
@@ -46,16 +47,21 @@
   }
 
   @Override public Boolean visitStaticInjectionRequest(StaticInjectionRequest command) {
-    for (Class<?> type : command.getTypes()) {
-      staticInjections.add(new StaticInjection(command.getSource(), type));
-    }
+    staticInjections.add(new StaticInjection(command.getSource(), command.getType()));
     return true;
   }
 
   @Override public Boolean visitInjectionRequest(InjectionRequest command) {
-    for (Object instance : command.getInstances()) {
-      memberInjector.requestInjection(instance, command.getSource());
+    List<InjectionPoint> injectionPointsList = Lists.newArrayList();
+    try {
+      InjectionPoint.addForInstanceMethodsAndFields(
+          command.getInstance().getClass(), injectionPointsList);
+    } catch (ConfigurationException e) {
+      errors.merge(e.getErrorMessages());
     }
+
+    memberInjector.requestInjection(command.getInstance(), command.getSource(),
+        ImmutableSet.copyOf(injectionPointsList));
     return true;
   }
 
diff --git a/src/com/google/inject/InjectorBuilder.java b/src/com/google/inject/InjectorBuilder.java
index c4dcb00..8e92beb 100644
--- a/src/com/google/inject/InjectorBuilder.java
+++ b/src/com/google/inject/InjectorBuilder.java
@@ -17,6 +17,7 @@
 package com.google.inject;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 import com.google.inject.Reflection.Factory;
@@ -258,7 +259,7 @@
     injector.explicitBindings.put(key,
         new ProviderInstanceBindingImpl<Logger>(injector, key,
             SourceProvider.UNKNOWN_SOURCE, loggerFactory, Scopes.NO_SCOPE,
-            loggerFactory, LoadStrategy.LAZY));
+            loggerFactory, LoadStrategy.LAZY, ImmutableSet.<InjectionPoint>of()));
   }
 
   static class LoggerFactory implements InternalFactory<Logger>, Provider<Logger> {
diff --git a/src/com/google/inject/InjectorImpl.java b/src/com/google/inject/InjectorImpl.java
index a09583f..cca4ffd 100644
--- a/src/com/google/inject/InjectorImpl.java
+++ b/src/com/google/inject/InjectorImpl.java
@@ -23,16 +23,15 @@
 import com.google.common.collect.Maps;
 import com.google.common.collect.Multimap;
 import com.google.common.collect.Multimaps;
-import com.google.common.collect.Sets;
+import com.google.inject.internal.Annotations;
 import com.google.inject.internal.BytecodeGen.Visibility;
 import static com.google.inject.internal.BytecodeGen.newFastClass;
 import com.google.inject.internal.Classes;
 import com.google.inject.internal.ConfigurationException;
 import com.google.inject.internal.Errors;
 import com.google.inject.internal.ErrorsException;
-import com.google.inject.internal.Keys;
+import com.google.inject.internal.FailableCache;
 import com.google.inject.internal.MatcherAndConverter;
-import com.google.inject.internal.ReferenceCache;
 import com.google.inject.internal.ToStringBuilder;
 import com.google.inject.spi.BindingTargetVisitor;
 import com.google.inject.spi.Dependency;
@@ -48,11 +47,9 @@
 import java.lang.reflect.Modifier;
 import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 import net.sf.cglib.reflect.FastClass;
 import net.sf.cglib.reflect.FastMethod;
 
@@ -165,6 +162,7 @@
       if (binding != null
           && binding.getScope() != null
           && !binding.getScope().equals(Scopes.NO_SCOPE)) {
+        // TODO: this binding won't report its injection points or scoping properly
         bindingImpl = new ProviderInstanceBindingImpl(
             this,
             key,
@@ -172,7 +170,8 @@
             new InternalFactoryToProviderAdapter(binding.getProvider(), binding.getSource()),
             Scopes.NO_SCOPE,
             binding.getProvider(),
-            LoadStrategy.LAZY);
+            LoadStrategy.LAZY,
+            ImmutableSet.<InjectionPoint>of());
       }
       parentBindings.put(key, bindingImpl); // this kinda scares me
       return bindingImpl;
@@ -267,10 +266,6 @@
     public <V> V acceptTargetVisitor(BindingTargetVisitor<? super Provider<T>, V> visitor) {
       return visitor.visitProviderBinding(providedBinding.getKey());
     }
-
-    public BindingImpl<T> getTargetBinding() {
-      return providedBinding;
-    }
   }
 
   /**
@@ -352,18 +347,10 @@
       return provider;
     }
 
-    public T getValue() {
-      return value;
-    }
-
     public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
       return visitor.visitConvertedConstant(value);
     }
 
-    public BindingImpl<String> getOriginal() {
-      return (BindingImpl<String>) originalBinding;
-    }
-
     @Override public String toString() {
       return new ToStringBuilder(Binding.class)
           .add("key", key)
@@ -428,14 +415,14 @@
     // Handle @ImplementedBy
     ImplementedBy implementedBy = type.getAnnotation(ImplementedBy.class);
     if (implementedBy != null) {
-      Scopes.checkForMisplacedScopeAnnotations(type, source, errors);
+      Annotations.checkForMisplacedScopeAnnotations(type, source, errors);
       return createImplementedByBinding(type, scope, implementedBy, loadStrategy, errors);
     }
 
     // Handle @ProvidedBy.
     ProvidedBy providedBy = type.getAnnotation(ProvidedBy.class);
     if (providedBy != null) {
-      Scopes.checkForMisplacedScopeAnnotations(type, source, errors);
+      Annotations.checkForMisplacedScopeAnnotations(type, source, errors);
       return createProvidedByBinding(type, scope, providedBy, loadStrategy, errors);
     }
 
@@ -452,7 +439,7 @@
     }
 
     if (scope == null) {
-      Class<? extends Annotation> scopeAnnotation = Scopes.findScopeAnnotation(errors, type);
+      Class<? extends Annotation> scopeAnnotation = Annotations.findScopeAnnotation(errors, type);
       if (scopeAnnotation != null) {
         scope = scopes.get(scopeAnnotation);
         if (scope == null) {
@@ -473,8 +460,11 @@
   static class LateBoundConstructor<T> implements InternalFactory<T> {
     ConstructorInjector<T> constructorInjector;
 
-    void bind(InjectorImpl injector, Class<T> implementation, Errors errors) throws ErrorsException {
-      constructorInjector = injector.getConstructor(implementation, errors);
+    @SuppressWarnings("unchecked") // the constructor T is the same as the implementation T
+    void bind(InjectorImpl injector, Class<T> implementation, Errors errors)
+        throws ErrorsException {
+      constructorInjector = (ConstructorInjector<T>) injector.constructors.get(
+          implementation, errors);
     }
 
     public Constructor<T> getConstructor() {
@@ -630,13 +620,11 @@
     return getBindingOrThrow(key, errors).internalFactory;
   }
 
-  /**
-   * Field and method injectors. Each value is either an Errors or a
-   * {@code List<SingleMemberInjector>}.
-   */
-  private final Map<Class<?>, Object> injectors = new ReferenceCache<Class<?>, Object>() {
-    protected Object create(Class<?> type) {
-      Errors errors = new Errors();
+  /** Cached field and method injectors for a type. */
+  final FailableCache<Class<?>, List<SingleMemberInjector>> injectors
+      = new FailableCache<Class<?>, List<SingleMemberInjector>>() {
+    protected List<SingleMemberInjector> create(Class<?> type, Errors errors)
+        throws ErrorsException {
       List<InjectionPoint> injectionPoints = Lists.newArrayList();
       try {
         InjectionPoint.addForInstanceMethodsAndFields(type, injectionPoints);
@@ -644,28 +632,12 @@
         errors.merge(e.getErrorMessages());
       }
       ImmutableList<SingleMemberInjector> injectors = getInjectors(injectionPoints, errors);
-      return errors.hasErrors() ? errors.makeImmutable() : injectors;
+      errors.throwIfNecessary();
+      return injectors;
     }
   };
 
-  public List<SingleMemberInjector> getMemberInjectors(Class<?> type, Errors errors)
-      throws ErrorsException {
-    Object injectorsOrError = injectors.get(type);
-    if (injectorsOrError instanceof List) {
-      @SuppressWarnings("unchecked") // the only type of list we use
-      List<SingleMemberInjector> result = (List<SingleMemberInjector>) injectorsOrError;
-      return result;
-    } else if (injectorsOrError instanceof Errors) {
-      errors.merge((Errors) injectorsOrError);
-      throw errors.toException();
-    } else {
-      throw new AssertionError();
-    }
-  }
-
-  /**
-   * Returns the injectors for the specified injection points.
-   */
+  /** Returns the injectors for the specified injection points. */
   ImmutableList<SingleMemberInjector> getInjectors(
       List<InjectionPoint> injectionPoints, Errors errors) {
     List<SingleMemberInjector> injectors = Lists.newArrayList();
@@ -684,10 +656,6 @@
     return ImmutableList.copyOf(injectors);
   }
 
-  Map<Key<?>, BindingImpl<?>> internalBindings() {
-    return explicitBindings;
-  }
-
   // not test-covered
   public Map<Key<?>, Binding<?>> getBindings() {
     return Collections.<Key<?>, Binding<?>>unmodifiableMap(explicitBindings);
@@ -733,10 +701,6 @@
       return injectionPoint;
     }
 
-    public Collection<Dependency<?>> getDependencies() {
-      return injectionPoint.getDependencies();
-    }
-
     public void inject(Errors errors, InternalContext context, Object o) {
       context.setDependency(dependency);
       errors.pushSource(dependency);
@@ -765,7 +729,7 @@
     errors.pushSource(injectionPoint);
     try {
       Member member = injectionPoint.getMember();
-      Annotation misplacedBindingAnnotation = Keys.findBindingAnnotation(
+      Annotation misplacedBindingAnnotation = Annotations.findBindingAnnotation(
           errors, member, ((AnnotatedElement) member).getAnnotations());
       if (misplacedBindingAnnotation != null) {
         errors.misplacedBindingAnnotation(member, misplacedBindingAnnotation);
@@ -838,13 +802,6 @@
         };
       }
 
-      try {
-        injectionPoint = InjectionPoint.get(method);
-      } catch (ConfigurationException e) {
-        errors.merge(e.getErrorMessages());
-        throw errors.toException();
-      }
-
       parameterInjectors = injector.getParametersInjectors(injectionPoint, errors);
     }
 
@@ -875,18 +832,12 @@
         throws IllegalAccessException, InvocationTargetException;
   }
 
-  final Map<Class<?>, Object> constructors = new ReferenceCache<Class<?>, Object>() {
+  /** Cached constructor injectors for each type */
+  final FailableCache<Class<?>, ConstructorInjector<?>> constructors
+      = new FailableCache<Class<?>, ConstructorInjector<?>>() {
     @SuppressWarnings("unchecked")
-    protected Object create(Class<?> implementation) {
-      Errors errors = new Errors();
-      try {
-        ConstructorInjector result = new ConstructorInjector(
-            errors, InjectorImpl.this, implementation);
-        errors.throwIfNecessary();
-        return result;
-      } catch (ErrorsException e) {
-        return errors.merge(e.getErrors()).makeImmutable();
-      }
+    protected ConstructorInjector<?> create(Class<?> key, Errors errors) throws ErrorsException {
+      return new ConstructorInjector(errors, InjectorImpl.this, key);
     }
   };
 
@@ -939,7 +890,7 @@
       return;
     }
 
-    for (SingleMemberInjector injector : getMemberInjectors(o.getClass(), errors)) {
+    for (SingleMemberInjector injector : injectors.get(o.getClass(), errors)) {
       injector.inject(errors, context, o);
     }
   }
@@ -970,8 +921,7 @@
     return getProvider(Key.get(type));
   }
 
-  <T> Provider<T> getProviderOrThrow(final Key<T> key, Errors errors)
-      throws ErrorsException {
+  <T> Provider<T> getProviderOrThrow(final Key<T> key, Errors errors) throws ErrorsException {
     final InternalFactory<? extends T> factory = getInternalFactory(key, errors);
 
     return new Provider<T>() {
@@ -1049,40 +999,12 @@
     }
   }
 
-  /** Gets a constructor function for a given implementation class. */
-  @SuppressWarnings("unchecked")
-  <T> ConstructorInjector<T> getConstructor(Class<T> implementation, Errors errors)
-      throws ErrorsException {
-    Object o = constructors.get(implementation);
-    if (o instanceof Errors) {
-      errors.merge((Errors) o);
-      throw errors.toException();
-    }
-    else if (o instanceof ConstructorInjector<?>) {
-      return (ConstructorInjector<T>) o;
-    }
-    else {
-      throw new AssertionError();
-    }
-  }
-
   /** Injects a field or method in a given object. */
   public interface SingleMemberInjector {
     void inject(Errors errors, InternalContext context, Object o);
     InjectionPoint getInjectionPoint();
   }
 
-  Set<InjectionPoint> getFieldAndMethodInjectionsFor(Class<?> clazz)
-      throws ErrorsException {
-    Errors errors = new Errors();
-    Set<InjectionPoint> dependencies = Sets.newLinkedHashSet();
-    for (SingleMemberInjector singleMemberInjector : getMemberInjectors(clazz, errors)) {
-      dependencies.add(singleMemberInjector.getInjectionPoint());
-    }
-    errors.throwIfNecessary();
-    return ImmutableSet.copyOf(dependencies);
-  }
-
   public String toString() {
     return new ToStringBuilder(Injector.class)
         .add("bindings", explicitBindings)
diff --git a/src/com/google/inject/InstanceBindingImpl.java b/src/com/google/inject/InstanceBindingImpl.java
index aa81b4b..0588896 100644
--- a/src/com/google/inject/InstanceBindingImpl.java
+++ b/src/com/google/inject/InstanceBindingImpl.java
@@ -17,7 +17,7 @@
 
 package com.google.inject;
 
-import com.google.inject.internal.ErrorsException;
+import com.google.common.collect.ImmutableSet;
 import com.google.inject.internal.ToStringBuilder;
 import com.google.inject.spi.BindingTargetVisitor;
 import com.google.inject.spi.InjectionPoint;
@@ -28,10 +28,13 @@
 
   final T instance;
   final Provider<T> provider;
+  final ImmutableSet<InjectionPoint> injectionPoints;
 
   InstanceBindingImpl(InjectorImpl injector, Key<T> key, Object source,
-      InternalFactory<? extends T> internalFactory, T instance) {
+      InternalFactory<? extends T> internalFactory, Set<InjectionPoint> injectionPoints,
+      T instance) {
     super(injector, key, source, internalFactory, Scopes.NO_SCOPE, LoadStrategy.EAGER);
+    this.injectionPoints = ImmutableSet.copyOf(injectionPoints);
     this.instance = instance;
     this.provider = Providers.of(instance);
   }
@@ -41,21 +44,7 @@
   }
 
   public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
-    return visitor.visitInstance(instance);
-  }
-
-  public T getInstance() {
-    return this.instance;
-  }
-
-  public Set<InjectionPoint> getInjectionPoints() {
-    try {
-      return injector.getFieldAndMethodInjectionsFor(instance.getClass());
-    } catch (ErrorsException e) {
-      // this would have been a creation exception
-      // TODO: initialize the dependencies via a callback
-      throw new AssertionError(e);
-    }
+    return visitor.visitInstance(instance, injectionPoints);
   }
 
   @Override public String toString() {
diff --git a/src/com/google/inject/InternalContext.java b/src/com/google/inject/InternalContext.java
index 69eb611..bcb4e59 100644
--- a/src/com/google/inject/InternalContext.java
+++ b/src/com/google/inject/InternalContext.java
@@ -38,10 +38,6 @@
     this.injector = injector;
   }
 
-  public InjectorImpl getInjector() {
-    return injector;
-  }
-
   @SuppressWarnings("unchecked")
   public <T> ConstructionContext<T> getConstructionContext(Object key) {
     if (constructionContexts == null) {
diff --git a/src/com/google/inject/Module.java b/src/com/google/inject/Module.java
index baac103..10fcdc0 100644
--- a/src/com/google/inject/Module.java
+++ b/src/com/google/inject/Module.java
@@ -33,8 +33,11 @@
 public interface Module {
 
   /**
-   * Contributes bindings and other configurations for this module to a
-   * {@code Binder}.
+   * Contributes bindings and other configurations for this module to a {@code Binder}.
+   *
+   * <p><strong>Do not invoke this method directly</strong> to install submodules. Instead use
+   * {@link Binder#install(Module)}, which ensures that {@link Provides provider methods} are
+   * discovered.
    */
   void configure(Binder binder);
 }
diff --git a/src/com/google/inject/ProviderInstanceBindingImpl.java b/src/com/google/inject/ProviderInstanceBindingImpl.java
index 41336e4..977d3d3 100644
--- a/src/com/google/inject/ProviderInstanceBindingImpl.java
+++ b/src/com/google/inject/ProviderInstanceBindingImpl.java
@@ -16,45 +16,33 @@
 
 package com.google.inject;
 
-import com.google.inject.internal.ErrorsException;
+import com.google.common.collect.ImmutableSet;
 import com.google.inject.internal.ToStringBuilder;
 import com.google.inject.spi.BindingTargetVisitor;
-import com.google.inject.spi.HasInjections;
 import com.google.inject.spi.InjectionPoint;
 import java.util.Set;
 
 /**
  *
  */
-class ProviderInstanceBindingImpl<T> extends BindingImpl<T> implements HasInjections {
+class ProviderInstanceBindingImpl<T> extends BindingImpl<T> {
 
   final Provider<? extends T> providerInstance;
+  final ImmutableSet<InjectionPoint> injectionPoints;
 
   ProviderInstanceBindingImpl(InjectorImpl injector, Key<T> key,
       Object source,
       InternalFactory<? extends T> internalFactory, Scope scope,
       Provider<? extends T> providerInstance,
-      LoadStrategy loadStrategy) {
+      LoadStrategy loadStrategy,
+      Set<InjectionPoint> injectionPoints) {
     super(injector, key, source, internalFactory, scope, loadStrategy);
     this.providerInstance = providerInstance;
+    this.injectionPoints = ImmutableSet.copyOf(injectionPoints);
   }
 
   public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
-    return visitor.visitProvider(providerInstance);
-  }
-
-  public Provider<? extends T> getProviderInstance() {
-    return this.providerInstance;
-  }
-
-  public Set<InjectionPoint> getInjectionPoints() {
-    try {
-      return injector.getFieldAndMethodInjectionsFor(providerInstance.getClass());
-    } catch (ErrorsException e) {
-      // this would have been a creation exception
-      // TODO: initialize the dependencies via a callback
-      throw new AssertionError(e);
-    }
+    return visitor.visitProvider(providerInstance, injectionPoints);
   }
 
   @Override
diff --git a/src/com/google/inject/Provides.java b/src/com/google/inject/Provides.java
index b587765..8df6a65 100644
--- a/src/com/google/inject/Provides.java
+++ b/src/com/google/inject/Provides.java
@@ -16,15 +16,15 @@
 
 package com.google.inject;
 
-import java.lang.annotation.Target;
-import java.lang.annotation.Retention;
 import java.lang.annotation.Documented;
 import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
 
 /**
- * Annotates methods which can be used as providers. Guice will pass
- * dependencies to the method as parameters.
+ * Annotates methods of a {@link Module} to create a provider method binding. The method's return
+ * type is bound to it's returned value. Guice will pass dependencies to the method as parameters.
  *
  * @author crazybob@google.com (Bob Lee)
  */
diff --git a/src/com/google/inject/ProvisionException.java b/src/com/google/inject/ProvisionException.java
index 9a2d455..4069288 100644
--- a/src/com/google/inject/ProvisionException.java
+++ b/src/com/google/inject/ProvisionException.java
@@ -92,4 +92,6 @@
       throw new ProvisionException(errors.makeImmutable());
     }
   }
+
+  private static final long serialVersionUID = 0;
 }
diff --git a/src/com/google/inject/ProxyFactory.java b/src/com/google/inject/ProxyFactory.java
index 215679b..07c9227 100644
--- a/src/com/google/inject/ProxyFactory.java
+++ b/src/com/google/inject/ProxyFactory.java
@@ -21,10 +21,9 @@
 import com.google.inject.internal.BytecodeGen.Visibility;
 import static com.google.inject.internal.BytecodeGen.newEnhancer;
 import static com.google.inject.internal.BytecodeGen.newFastClass;
-import com.google.inject.internal.ConfigurationException;
 import com.google.inject.internal.Errors;
 import com.google.inject.internal.ErrorsException;
-import com.google.inject.internal.ReferenceCache;
+import com.google.inject.internal.FailableCache;
 import com.google.inject.spi.InjectionPoint;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
@@ -56,39 +55,20 @@
     defaultFactory = new DefaultConstructionProxyFactory();
   }
 
-  @SuppressWarnings("unchecked")
+  @SuppressWarnings("unchecked") // the constructed T is the same as the injection point's T
   public <T> ConstructionProxy<T> get(Errors errors, InjectionPoint injectionPoint)
       throws ErrorsException {
-    return (ConstructionProxy<T>) getConstructionProxy(injectionPoint, errors);
+    return (ConstructionProxy<T>) constructionProxies.get(injectionPoint, errors);
   }
 
-  Map<InjectionPoint, Object> constructionProxies = new ReferenceCache<InjectionPoint, Object>() {
-    protected Object create(InjectionPoint injectionPoint) {
-      Errors errors = new Errors();
-      try {
-        ConstructionProxy<?> result = createConstructionProxy(errors, injectionPoint);
-        errors.throwIfNecessary();
-        return result;
-      } catch (ErrorsException e) {
-        return errors.merge(e.getErrors()).makeImmutable();
-      }
+  /** Cached construction proxies for each injection point */
+  FailableCache<InjectionPoint, ConstructionProxy> constructionProxies
+      = new FailableCache<InjectionPoint, ConstructionProxy>() {
+    protected ConstructionProxy create(InjectionPoint key, Errors errors) throws ErrorsException {
+      return createConstructionProxy(errors, key);
     }
   };
 
-  public ConstructionProxy<?> getConstructionProxy(InjectionPoint injectionPoint, Errors errors)
-      throws ErrorsException {
-    Object constructionProxyOrErrors = constructionProxies.get(injectionPoint);
-
-    if (constructionProxyOrErrors instanceof ConstructionProxy<?>) {
-      return (ConstructionProxy<?>) constructionProxyOrErrors;
-    } else if (constructionProxyOrErrors instanceof Errors) {
-      errors.merge((Errors) constructionProxyOrErrors);
-      throw errors.toException();
-    } else {
-      throw new AssertionError();
-    }
-  }
-
   <T> ConstructionProxy<T> createConstructionProxy(Errors errors, InjectionPoint injectionPoint)
       throws ErrorsException {
     @SuppressWarnings("unchecked") // the member of injectionPoint is always a Constructor<T>
@@ -170,25 +150,19 @@
     // Store callbacks.
     Enhancer.registerStaticCallbacks(proxied, callbacks);
 
-    return createConstructionProxy(errors, proxied, constructor);
+    return createConstructionProxy(proxied, injectionPoint);
   }
 
   /**
    * Creates a construction proxy given a class and parameter types.
    */
-  private <T> ConstructionProxy<T> createConstructionProxy(Errors errors, final Class<?> clazz,
-      final Constructor<T> standardConstructor) throws ErrorsException {
+  private <T> ConstructionProxy<T> createConstructionProxy(final Class<?> clazz,
+      final InjectionPoint injectionPoint) throws ErrorsException {
+    @SuppressWarnings("unchecked") // injection point's member must be a Constructor<T>
+    final Constructor<T> standardConstructor = (Constructor<T>) injectionPoint.getMember();
     FastClass fastClass = newFastClass(clazz, Visibility.PUBLIC);
     final FastConstructor fastConstructor
         = fastClass.getConstructor(standardConstructor.getParameterTypes());
-    final InjectionPoint injectionPoint;
-
-    try {
-      injectionPoint = InjectionPoint.get(standardConstructor);
-    } catch (ConfigurationException e) {
-      errors.merge(e.getErrorMessages());
-      throw errors.toException();
-    }
 
     return new ConstructionProxy<T>() {
       @SuppressWarnings("unchecked")
diff --git a/src/com/google/inject/ScopeBindingProcessor.java b/src/com/google/inject/ScopeBindingProcessor.java
index 3cc5cdb..1458df5 100644
--- a/src/com/google/inject/ScopeBindingProcessor.java
+++ b/src/com/google/inject/ScopeBindingProcessor.java
@@ -43,7 +43,7 @@
     Scope scope = command.getScope();
     Class<? extends Annotation> annotationType = command.getAnnotationType();
 
-    if (!Scopes.isScopeAnnotation(annotationType)) {
+    if (!Annotations.isScopeAnnotation(annotationType)) {
       errors.withSource(annotationType).missingScopeAnnotation();
       // Go ahead and bind anyway so we don't get collateral errors.
     }
diff --git a/src/com/google/inject/Scopes.java b/src/com/google/inject/Scopes.java
index 8cb2cba..e2597cc 100644
--- a/src/com/google/inject/Scopes.java
+++ b/src/com/google/inject/Scopes.java
@@ -16,10 +16,6 @@
 
 package com.google.inject;
 
-import com.google.inject.internal.Classes;
-import com.google.inject.internal.Errors;
-import java.lang.annotation.Annotation;
-
 /**
  * Built in scope implementations.
  *
@@ -87,61 +83,7 @@
     }
   };
 
-  /**
-   * Returns the scope annotation on {@code type}, or null if none is specified.
-   */
-  static Class<? extends Annotation> findScopeAnnotation(
-      Errors errors, Class<?> implementation) {
-    return findScopeAnnotation(errors, implementation.getAnnotations());
-  }
-
-
-  /**
-   * Returns the scoping annotation, or null if there isn't one.
-   */
-  static Class<? extends Annotation> findScopeAnnotation(Errors errors, Annotation[] annotations) {
-    Class<? extends Annotation> found = null;
-
-    for (Annotation annotation : annotations) {
-      if (annotation.annotationType()
-          .isAnnotationPresent(ScopeAnnotation.class)) {
-        if (found != null) {
-          errors.duplicateScopeAnnotations(found, annotation.annotationType());
-        } else {
-          found = annotation.annotationType();
-        }
-      }
-    }
-
-    return found;
-  }
-
-  static boolean isScopeAnnotation(Annotation annotation) {
-    return isScopeAnnotation(annotation.annotationType());
-  }
-
-  static boolean isScopeAnnotation(Class<? extends Annotation> annotationType) {
-    return annotationType.isAnnotationPresent(ScopeAnnotation.class);
-  }
-
-  /**
-   * Adds an error if there is a misplaced annotations on {@code type}. Scoping
-   * annotations are not allowed on abstract classes or interfaces.
-   */
-  static void checkForMisplacedScopeAnnotations(Class<?> type, Object source, Errors errors) {
-    if (Classes.isConcrete(type)) {
-      return;
-    }
-
-    Class<? extends Annotation> scopeAnnotation = findScopeAnnotation(errors, type);
-    if (scopeAnnotation != null) {
-      errors.withSource(type).scopeAnnotationOnAbstractType(scopeAnnotation, type, source);
-    }
-  }
-
-  /**
-   * Scopes an internal factory.
-   */
+  /** Scopes an internal factory. */
   static <T> InternalFactory<? extends T> scope(Key<T> key,
       InjectorImpl injector, InternalFactory<? extends T> creator,
       Scope scope) {
diff --git a/src/com/google/inject/internal/Annotations.java b/src/com/google/inject/internal/Annotations.java
index 6248170..ba749d5 100644
--- a/src/com/google/inject/internal/Annotations.java
+++ b/src/com/google/inject/internal/Annotations.java
@@ -16,9 +16,14 @@
 
 package com.google.inject.internal;
 
+import com.google.inject.BindingAnnotation;
+import com.google.inject.Key;
+import com.google.inject.ScopeAnnotation;
 import java.lang.annotation.Annotation;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.lang.reflect.Member;
+import java.lang.reflect.Type;
 
 /**
  * Annotation utilities.
@@ -30,9 +35,79 @@
   /**
    * Returns true if the given annotation is retained at runtime.
    */
-  public static boolean isRetainedAtRuntime(
-      Class<? extends Annotation> annotationType) {
+  public static boolean isRetainedAtRuntime(Class<? extends Annotation> annotationType) {
     Retention retention = annotationType.getAnnotation(Retention.class);
-    return !(retention == null || retention.value() != RetentionPolicy.RUNTIME);
+    return retention != null && retention.value() == RetentionPolicy.RUNTIME;
+  }
+
+  /** Returns the scope annotation on {@code type}, or null if none is specified. */
+  public static Class<? extends Annotation> findScopeAnnotation(
+      Errors errors, Class<?> implementation) {
+    return findScopeAnnotation(errors, implementation.getAnnotations());
+  }
+
+  /** Returns the scoping annotation, or null if there isn't one. */
+  public static Class<? extends Annotation> findScopeAnnotation(Errors errors, Annotation[] annotations) {
+    Class<? extends Annotation> found = null;
+
+    for (Annotation annotation : annotations) {
+      if (annotation.annotationType().isAnnotationPresent(ScopeAnnotation.class)) {
+        if (found != null) {
+          errors.duplicateScopeAnnotations(found, annotation.annotationType());
+        } else {
+          found = annotation.annotationType();
+        }
+      }
+    }
+
+    return found;
+  }
+
+  public static boolean isScopeAnnotation(Class<? extends Annotation> annotationType) {
+    return annotationType.isAnnotationPresent(ScopeAnnotation.class);
+  }
+
+  /**
+   * Adds an error if there is a misplaced annotations on {@code type}. Scoping
+   * annotations are not allowed on abstract classes or interfaces.
+   */
+  public static void checkForMisplacedScopeAnnotations(Class<?> type, Object source, Errors errors) {
+    if (Classes.isConcrete(type)) {
+      return;
+    }
+
+    Class<? extends Annotation> scopeAnnotation = findScopeAnnotation(errors, type);
+    if (scopeAnnotation != null) {
+      errors.withSource(type).scopeAnnotationOnAbstractType(scopeAnnotation, type, source);
+    }
+  }
+
+  /** Gets a key for the given type, member and annotations. */
+  public static Key<?> getKey(Type type, Member member, Annotation[] annotations, Errors errors)
+      throws ErrorsException {
+    Annotation found = findBindingAnnotation(errors, member, annotations);
+    errors.throwIfNecessary();
+    return found == null ? Key.get(type) : Key.get(type, found);
+  }
+
+  /**
+   * Returns the binding annotation on {@code member}, or null if there isn't one.
+   */
+  public static Annotation findBindingAnnotation(
+      Errors errors, Member member, Annotation[] annotations) {
+    Annotation found = null;
+
+    for (Annotation annotation : annotations) {
+      if (annotation.annotationType().isAnnotationPresent(BindingAnnotation.class)) {
+        if (found != null) {
+          errors.duplicateBindingAnnotations(member,
+              found.annotationType(), annotation.annotationType());
+        } else {
+          found = annotation;
+        }
+      }
+    }
+
+    return found;
   }
 }
diff --git a/src/com/google/inject/internal/Errors.java b/src/com/google/inject/internal/Errors.java
index 9063565..228d2d1 100644
--- a/src/com/google/inject/internal/Errors.java
+++ b/src/com/google/inject/internal/Errors.java
@@ -38,8 +38,8 @@
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.Formatter;
-import java.util.List;
 import java.util.Iterator;
+import java.util.List;
 
 /**
  * A collection of error messages. If this type is passed as a method parameter, the method is
@@ -90,10 +90,6 @@
     checkArgument(source == popped);
   }
 
-  public Errors userReportedError(String messageFormat, List<Object> arguments) {
-    return addMessage(messageFormat, arguments);
-  }
-
   /**
    * We use a fairly generic error message here. The motivation is to share the
    * same message for both bind time errors:
@@ -182,11 +178,6 @@
     return addMessage("Binding to core guice framework type is not allowed: %s.", simpleName);
   }
 
-  public Errors cannotBindToNullInstance() {
-    return addMessage("Binding to null instances is not allowed. "
-        + "Use toProvider(Providers.of(null)) if this is your intended behaviour.");
-  }
-
   public Errors scopeNotFound(Class<? extends Annotation> scopeAnnotation) {
     return addMessage("No scope is bound to %s.", scopeAnnotation);
   }
@@ -294,7 +285,7 @@
     List<Object> sources = Lists.newArrayList();
     sources.addAll(this.sources);
     sources.addAll(message.getSources());
-    return new Message(message.getMessage(), message.getCause(), stripDuplicates(sources));
+    return new Message(stripDuplicates(sources), message.getMessage(), message.getCause());
   }
 
   public Errors merge(Collection<Message> messages) {
@@ -333,7 +324,7 @@
 
   private Errors addMessage(Throwable cause, String messageFormat, Object... arguments) {
     String message = format(messageFormat, arguments);
-    addMessage(new Message(message, cause, stripDuplicates(sources)));
+    addMessage(new Message(stripDuplicates(sources), message, cause));
     return this;
   }
 
diff --git a/src/com/google/inject/internal/FailableCache.java b/src/com/google/inject/internal/FailableCache.java
new file mode 100644
index 0000000..72a4bdf
--- /dev/null
+++ b/src/com/google/inject/internal/FailableCache.java
@@ -0,0 +1,51 @@
+/**
+ * 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;
+
+/**
+ * Lazily creates (and caches) values for keys. If creating the value fails (with errors), an
+ * exception is thrown on retrieval.
+ *
+ * @author jessewilson@google.com (Jesse Wilson)
+ */
+public abstract class FailableCache<K, V> {
+  
+  private final ReferenceCache<K, Object> delegate = new ReferenceCache<K, Object>() {
+    protected final Object create(K key) {
+      Errors errors = new Errors();
+      V result = null;
+      try {
+        result = FailableCache.this.create(key, errors);
+      } catch (ErrorsException e) {
+        errors.merge(e.getErrors());
+      }
+      return errors.hasErrors() ? errors.makeImmutable() : result;
+    }
+  };
+
+  protected abstract V create(K key, Errors errors) throws ErrorsException;
+  
+  public V get(K key, Errors errors) throws ErrorsException {
+    Object resultOrError = delegate.get(key);
+    if (resultOrError instanceof Errors) {
+      errors.merge((Errors) resultOrError);
+      throw errors.toException();
+    } else {
+      return (V) resultOrError;
+    }
+  }
+}
diff --git a/src/com/google/inject/internal/Keys.java b/src/com/google/inject/internal/Keys.java
deleted file mode 100644
index 0b2beac..0000000
--- a/src/com/google/inject/internal/Keys.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/**
- * 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.BindingAnnotation;
-import com.google.inject.Key;
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Member;
-import java.lang.reflect.Type;
-
-public class Keys {
-
-  /**
-   * Gets a key for the given type, member and annotations.
-   */
-  public static Key<?> get(Type type, Member member, Annotation[] annotations, Errors errors)
-      throws ErrorsException {
-    Annotation found = findBindingAnnotation(errors, member, annotations);
-    errors.throwIfNecessary();
-    return found == null ? Key.get(type) : Key.get(type, found);
-  }
-
-  /**
-   * Returns the binding annotation on {@code member}, or null if there isn't one.
-   */
-  public static Annotation findBindingAnnotation(
-      Errors errors, Member member, Annotation[] annotations) {
-    Annotation found = null;
-
-    for (Annotation annotation : annotations) {
-      if (annotation.annotationType().isAnnotationPresent(BindingAnnotation.class)) {
-        if (found != null) {
-          errors.duplicateBindingAnnotations(member,
-              found.annotationType(), annotation.annotationType());
-        } else {
-          found = annotation;
-        }
-      }
-    }
-
-    return found;
-  }
-}
diff --git a/src/com/google/inject/internal/ModuleBinding.java b/src/com/google/inject/internal/ModuleBinding.java
index f7aa503..87dc78f 100644
--- a/src/com/google/inject/internal/ModuleBinding.java
+++ b/src/com/google/inject/internal/ModuleBinding.java
@@ -17,6 +17,8 @@
 package com.google.inject.internal;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
 import com.google.inject.Binder;
 import com.google.inject.Binding;
 import com.google.inject.Key;
@@ -32,7 +34,12 @@
 import com.google.inject.spi.BindingTargetVisitor;
 import com.google.inject.spi.DefaultBindingTargetVisitor;
 import com.google.inject.spi.ElementVisitor;
+import com.google.inject.spi.InjectionPoint;
+import com.google.inject.spi.Message;
+import com.google.inject.util.Providers;
 import java.lang.annotation.Annotation;
+import java.util.List;
+import java.util.Set;
 
 /**
  * Immutable snapshot of a request to bind a value.
@@ -57,7 +64,7 @@
 
   private static final BindingTargetVisitor<Object, Boolean> SUPPORTS_SCOPES
       = new DefaultBindingTargetVisitor<Object, Boolean>() {
-    @Override public Boolean visitInstance(Object instance) {
+    @Override public Boolean visitInstance(Object instance, Set<InjectionPoint> injectionPoints) {
       return false;
     }
 
@@ -187,15 +194,47 @@
 
     public void toInstance(final T instance) {
       checkNotTargetted();
-      target = new InstanceTarget<T>(instance);
+
+      if (instance == null) {
+        binder.addError("Binding to null instances is not allowed. "
+            + "Use toProvider(Providers.of(null)) if this is your intended behaviour.");
+        // we finish the binding to prevent additional errors
+        toProvider(Providers.<T>of(null));
+        return;
+      }
+
+      // lookup the injection points, adding any errors to the binder's errors list
+      List<InjectionPoint> injectionPointsList = Lists.newArrayList();
+      try {
+        InjectionPoint.addForInstanceMethodsAndFields(instance.getClass(), injectionPointsList);
+      } catch (ConfigurationException e) {
+        for (Message message : e.getErrorMessages()) {
+          binder.addError(message);
+        }
+      }
+      ImmutableSet<InjectionPoint> injectionPoints = ImmutableSet.copyOf(injectionPointsList);
+
+      target = new InstanceTarget<T>(instance, injectionPoints);
     }
 
     public ScopedBindingBuilder toProvider(final Provider<? extends T> provider) {
       checkNotNull(provider, "provider");
       checkNotTargetted();
+
+      // lookup the injection points, adding any errors to the binder's errors list
+      List<InjectionPoint> injectionPointsList = Lists.newArrayList();
+      try {
+        InjectionPoint.addForInstanceMethodsAndFields(provider.getClass(), injectionPointsList);
+      } catch (ConfigurationException e) {
+        for (Message message : e.getErrorMessages()) {
+          binder.addError(message);
+        }
+      }
+      final ImmutableSet<InjectionPoint> injectionPoints = ImmutableSet.copyOf(injectionPointsList);
+
       target = new Target<T>() {
         public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
-          return visitor.visitProvider(provider);
+          return visitor.visitProvider(provider, injectionPoints);
         }
       };
       return this;
@@ -408,7 +447,8 @@
         key = Key.get(typeAsClassT);
       }
 
-      ModuleBinding.this.target = new InstanceTarget<T>(instanceAsT);
+      ModuleBinding.this.target = new InstanceTarget<T>(instanceAsT,
+          ImmutableSet.<InjectionPoint>of());
     }
 
     @Override public String toString() {
@@ -430,13 +470,15 @@
 
   static class InstanceTarget<T> implements Target<T> {
     private final T instance;
+    private final ImmutableSet<InjectionPoint> injectionPoints;
 
-    public InstanceTarget(T instance) {
+    public InstanceTarget(T instance, ImmutableSet<InjectionPoint> injectionPoints) {
       this.instance = instance;
+      this.injectionPoints = injectionPoints;
     }
 
     public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
-      return visitor.visitInstance(instance);
+      return visitor.visitInstance(instance, injectionPoints);
     }
   }
 }
diff --git a/src/com/google/inject/internal/SourceProvider.java b/src/com/google/inject/internal/SourceProvider.java
index 1f99ee8..741dbda 100644
--- a/src/com/google/inject/internal/SourceProvider.java
+++ b/src/com/google/inject/internal/SourceProvider.java
@@ -20,7 +20,6 @@
 import static com.google.common.collect.Iterables.concat;
 import com.google.common.collect.Lists;
 import java.util.List;
-import java.util.Set;
 
 /**
  * Provides access to the calling line of code.
@@ -59,11 +58,6 @@
     return strings;
   }
 
-  /** Returns an immutable set with the names of the classes that are skipped. */
-  public Set<String> getSkippedClassNames() {
-    return classNamesToSkip;
-  }
-
   /**
    * Returns the calling line of code. The selected line is the nearest to the top of the stack that
    * is not skipped.
diff --git a/src/com/google/inject/matcher/AbstractMatcher.java b/src/com/google/inject/matcher/AbstractMatcher.java
index 1897b1a..91ea2e6 100644
--- a/src/com/google/inject/matcher/AbstractMatcher.java
+++ b/src/com/google/inject/matcher/AbstractMatcher.java
@@ -58,6 +58,8 @@
     @Override public String toString() {
       return "and(" + a + ", " + b + ")";
     }
+
+    private static final long serialVersionUID = 0;
   }
 
   private static class OrMatcher<T> extends AbstractMatcher<T> implements Serializable {
@@ -85,5 +87,7 @@
     @Override public String toString() {
       return "or(" + a + ", " + b + ")";
     }
+
+    private static final long serialVersionUID = 0;
   }
 }
diff --git a/src/com/google/inject/matcher/Matchers.java b/src/com/google/inject/matcher/Matchers.java
index 32c1a2e..9bb5b55 100644
--- a/src/com/google/inject/matcher/Matchers.java
+++ b/src/com/google/inject/matcher/Matchers.java
@@ -54,6 +54,8 @@
     public Object readResolve() {
       return any();
     }
+
+    private static final long serialVersionUID = 0;
   }
 
   /**
@@ -86,6 +88,8 @@
     @Override public String toString() {
       return "not(" + delegate + ")";
     }
+
+    private static final long serialVersionUID = 0;
   }
 
   private static void checkForRuntimeRetention(
@@ -129,6 +133,8 @@
     @Override public String toString() {
       return "annotatedWith(" + annotationType.getSimpleName() + ".class)";
     }
+
+    private static final long serialVersionUID = 0;
   }
 
   /**
@@ -166,6 +172,8 @@
     @Override public String toString() {
       return "annotatedWith(" + annotation + ")";
     }
+
+    private static final long serialVersionUID = 0;
   }
 
   /**
@@ -200,6 +208,8 @@
     @Override public String toString() {
       return "subclassesOf(" + superclass.getSimpleName() + ".class)";
     }
+
+    private static final long serialVersionUID = 0;
   }
 
   /**
@@ -233,6 +243,8 @@
     @Override public String toString() {
       return "only(" + value + ")";
     }
+
+    private static final long serialVersionUID = 0;
   }
 
   /**
@@ -266,6 +278,8 @@
     @Override public String toString() {
       return "identicalTo(" + value + ")";
     }
+
+    private static final long serialVersionUID = 0;
   }
 
   /**
@@ -305,6 +319,8 @@
     public Object readResolve() {
       return inPackage(Package.getPackage(packageName));
     }
+
+    private static final long serialVersionUID = 0;
   }
 
   /**
@@ -340,6 +356,8 @@
     @Override public String toString() {
       return "inSubpackage(" + targetPackageName + ")";
     }
+
+    private static final long serialVersionUID = 0;
   }
 
   /**
@@ -373,5 +391,7 @@
     @Override public String toString() {
       return "returns(" + returnType + ")";
     }
+
+    private static final long serialVersionUID = 0;
   }
 }
diff --git a/src/com/google/inject/spi/BindingTargetVisitor.java b/src/com/google/inject/spi/BindingTargetVisitor.java
index ab00d1b..0da1c3e 100644
--- a/src/com/google/inject/spi/BindingTargetVisitor.java
+++ b/src/com/google/inject/spi/BindingTargetVisitor.java
@@ -19,6 +19,7 @@
 import com.google.inject.Key;
 import com.google.inject.Provider;
 import java.lang.reflect.Constructor;
+import java.util.Set;
 
 /**
  * Visits each of the strategies used to find an instance to satisfy an injection.
@@ -33,16 +34,20 @@
    * found in both module and injector bindings.
    *
    * @param instance the user-supplied value
+   * @param injectionPoints the field and method injection points of the instance, injected at
+   *      injector-creation time only.
    */
-  V visitInstance(T instance);
+  V visitInstance(T instance, Set<InjectionPoint> injectionPoints);
 
   /**
    * Visit a provider instance binding. The provider's {@code get} method is invoked to resolve
    * injections. This target is found in both module and injector bindings.
    *
    * @param provider the user-supplied, unscoped provider
+   * @param injectionPoints the field and method injection points of the provider, injected at
+   *      injector-creation time only.
    */
-  V visitProvider(Provider<? extends T> provider);
+  V visitProvider(Provider<? extends T> provider, Set<InjectionPoint> injectionPoints);
 
   /**
    * Visit a provider key binding. To resolve injections, the provider injection is first
@@ -76,8 +81,10 @@
    *
    * @param constructor the {@link com.google.inject.Inject annotated} or default constructor that
    *      is invoked for creating values
+   * @param injectionPoints the constructor, field and method injection points to create and
+   *      populate a new instance. The set contains exactly one constructor injection point.
    */
-  V visitConstructor(Constructor<? extends T> constructor);
+  V visitConstructor(Constructor<? extends T> constructor, Set<InjectionPoint> injectionPoints);
 
   /**
    * Visit a binding created from converting a bound instance to a new type. The source binding
diff --git a/src/com/google/inject/spi/DefaultBindingTargetVisitor.java b/src/com/google/inject/spi/DefaultBindingTargetVisitor.java
index c8da65c..8e30542 100644
--- a/src/com/google/inject/spi/DefaultBindingTargetVisitor.java
+++ b/src/com/google/inject/spi/DefaultBindingTargetVisitor.java
@@ -19,6 +19,7 @@
 import com.google.inject.Key;
 import com.google.inject.Provider;
 import java.lang.reflect.Constructor;
+import java.util.Set;
 
 /**
  * No-op visitor for subclassing. All interface methods simply delegate to
@@ -35,11 +36,11 @@
     return null;
   }
 
-  public V visitInstance(T instance) {
+  public V visitInstance(T instance, Set<InjectionPoint> injectionPoints) {
     return visitOther();
   }
 
-  public V visitProvider(Provider<? extends T> provider) {
+  public V visitProvider(Provider<? extends T> provider, Set<InjectionPoint> injectionPoints) {
     return visitOther();
   }
 
@@ -55,7 +56,8 @@
     return visitOther();
   }
 
-  public V visitConstructor(Constructor<? extends T> constructor) {
+  public V visitConstructor(Constructor<? extends T> constructor,
+      Set<InjectionPoint> injectionPoints) {
     return visitOther();
   }
 
diff --git a/src/com/google/inject/spi/Dependency.java b/src/com/google/inject/spi/Dependency.java
index 1d71fdd..fd77a22 100644
--- a/src/com/google/inject/spi/Dependency.java
+++ b/src/com/google/inject/spi/Dependency.java
@@ -108,4 +108,6 @@
     }
     return builder.toString();
   }
+
+  private static final long serialVersionUID = 0;
 }
diff --git a/src/com/google/inject/spi/Elements.java b/src/com/google/inject/spi/Elements.java
index 8f449f6..025674c 100644
--- a/src/com/google/inject/spi/Elements.java
+++ b/src/com/google/inject/spi/Elements.java
@@ -18,6 +18,7 @@
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkState;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
 import com.google.inject.AbstractModule;
@@ -25,7 +26,6 @@
 import com.google.inject.Key;
 import com.google.inject.Module;
 import com.google.inject.Provider;
-import com.google.inject.ProviderMethods;
 import com.google.inject.Scope;
 import com.google.inject.Stage;
 import com.google.inject.TypeLiteral;
@@ -51,7 +51,7 @@
 public final class Elements {
   private static final BindingTargetVisitor<Object, Object> GET_INSTANCE_VISITOR
       = new DefaultBindingTargetVisitor<Object, Object>() {
-    @Override public Object visitInstance(Object instance) {
+    @Override public Object visitInstance(Object instance, Set<InjectionPoint> injectionPoints) {
       return instance;
     }
 
@@ -139,11 +139,15 @@
     }
 
     public void requestInjection(Object... instances) {
-      elements.add(new InjectionRequest(getSource(), instances));
+      for (Object instance : instances) {
+        elements.add(new InjectionRequest(getSource(), instance));
+      }
     }
 
     public void requestStaticInjection(Class<?>... types) {
-      elements.add(new StaticInjectionRequest(getSource(), types));
+      for (Class<?> type : types) {
+        elements.add(new StaticInjectionRequest(getSource(), type));
+      }
     }
 
     public void install(Module module) {
@@ -166,7 +170,8 @@
     }
 
     public void addError(Throwable t) {
-      elements.add(new Message(getSource(), t));
+      String message = "An exception was caught and reported. Message: " + t.getMessage();
+      elements.add(new Message(ImmutableList.of(getSource()), message, t));
     }
 
     public void addError(Message message) {
diff --git a/src/com/google/inject/spi/HasInjections.java b/src/com/google/inject/spi/HasInjections.java
deleted file mode 100644
index 2e23769..0000000
--- a/src/com/google/inject/spi/HasInjections.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/**
- * Copyright (C) 2007 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.spi;
-
-import java.util.Set;
-
-/**
- * Implemented by bindings which have implementation dependencies on other
- * bindings.
- */
-public interface HasInjections {
-
-  /**
-   * Gets the injection points for this binding.
-   */
-  Set<InjectionPoint> getInjectionPoints();
-}
diff --git a/src/com/google/inject/spi/InjectionPoint.java b/src/com/google/inject/spi/InjectionPoint.java
index af18e9b..27c24a3 100644
--- a/src/com/google/inject/spi/InjectionPoint.java
+++ b/src/com/google/inject/spi/InjectionPoint.java
@@ -20,10 +20,10 @@
 import com.google.common.collect.Lists;
 import com.google.inject.Inject;
 import com.google.inject.Key;
+import com.google.inject.internal.Annotations;
 import com.google.inject.internal.ConfigurationException;
 import com.google.inject.internal.Errors;
 import com.google.inject.internal.ErrorsException;
-import com.google.inject.internal.Keys;
 import com.google.inject.internal.MoreTypes;
 import com.google.inject.internal.Nullability;
 import java.io.ObjectStreamException;
@@ -42,7 +42,9 @@
 import java.util.List;
 
 /**
- * A constructor, method or field that can receive injections.
+ * A constructor, field or method that can receive injections. Typically this is a member with the
+ * {@literal @}{@link Inject} annotation. For non-private, no argument constructors, the member may
+ * omit the annotation. 
  *
  * @author crazybob@google.com (Bob Lee)
  */
@@ -59,7 +61,7 @@
     this.optional = optional;
   }
 
-  private InjectionPoint(Method method) {
+  InjectionPoint(Method method) {
     this.member = method;
 
     Inject inject = method.getAnnotation(Inject.class);
@@ -69,7 +71,7 @@
         method.getParameterAnnotations());
   }
 
-  private InjectionPoint(Constructor<?> constructor) {
+  InjectionPoint(Constructor<?> constructor) {
     this.member = constructor;
     this.optional = false;
     // TODO(jessewilson): make sure that if @Inject it exists, its not optional
@@ -77,7 +79,7 @@
         constructor.getParameterAnnotations());
   }
 
-  private InjectionPoint(Field field) {
+  InjectionPoint(Field field) {
     this.member = field;
 
     Inject inject = field.getAnnotation(Inject.class);
@@ -88,7 +90,7 @@
     Errors errors = new Errors(field);
     Key<?> key = null;
     try {
-      key = Keys.get(field.getGenericType(), field, annotations, errors);
+      key = Annotations.getKey(field.getGenericType(), field, annotations, errors);
     } catch (ErrorsException e) {
       errors.merge(e.getErrors());
     }
@@ -108,7 +110,7 @@
     for (Type parameterType : genericParameterTypes) {
       try {
         Annotation[] parameterAnnotations = annotationsIterator.next();
-        Key<?> key = Keys.get(parameterType, member, parameterAnnotations, errors);
+        Key<?> key = Annotations.getKey(parameterType, member, parameterAnnotations, errors);
         dependencies.add(newDependency(key, Nullability.allowsNull(parameterAnnotations), index));
         index++;
       } catch (ErrorsException e) {
@@ -120,10 +122,14 @@
     return ImmutableList.copyOf(dependencies);
   }
 
+  // This metohd is necessary to create a Dependency<T> with proper generic type information
   private <T> Dependency<T> newDependency(Key<T> key, boolean allowsNull, int parameterIndex) {
     return new Dependency<T>(this, key, allowsNull, parameterIndex);
   }
 
+  /**
+   * Returns the injected constructor, field, or method.
+   */
   public Member getMember() {
     return member;
   }
@@ -132,11 +138,19 @@
    * Returns the dependencies for this injection point. If the injection point is for a method or
    * constructor, the dependencies will correspond to that member's parameters. Field injection
    * points always have a single dependency for the field itself.
+   *
+   * @return a possibly-empty list
    */
   public List<Dependency<?>> getDependencies() {
     return dependencies;
   }
 
+  /**
+   * Returns true if this injection point shall be skipped if the injector cannot resolve bindings
+   * for all required dependencies. Both explicit bindings (as specified in a module), and implicit
+   * bindings ({@literal @}{@link com.google.inject.ImplementedBy ImplementedBy}, default
+   * constructors etc.) may be used to satisfy optional injection points.
+   */
   public boolean isOptional() {
     return optional;
   }
@@ -160,69 +174,6 @@
   }
 
   /**
-   * Returns a new injection point for {@code constructor}.
-   *
-   * @param constructor a no arguments constructor, or a constructor with any number of arguments
-   *      and the {@literal @}{@link Inject} annotation.
-   */
-  public static InjectionPoint get(Constructor constructor) {
-    return new InjectionPoint(constructor);
-  }
-
-  /**
-   * Returns a new injection point for {@code method}.
-   *
-   * @param method a method with the {@literal @}{@link Inject} annotation.
-   */
-  public static InjectionPoint get(Method method) {
-    return new InjectionPoint(method);
-  }
-
-  /**
-   * Returns a new injection point for {@code field}.
-   *
-   * @param field a field with the {@literal @}{@link Inject} annotation.
-   */
-  public static InjectionPoint get(Field field) {
-    return new InjectionPoint(field);
-  }
-
-  /**
-   * Adds all static method and field injection points on {@code type} to {@code injectionPoints}.
-   * All fields are added first, and then all methods. Within the fields, supertype fields are added
-   * before subtype fields. Similarly, supertype methods are added before subtype methods.
-   *
-   * @throws RuntimeException if there is a malformed injection point on {@code type}, such as a
-   *      field with multiple binding annotations. When such an exception is thrown, the valid
-   *      injection points are still added to the collection.
-   */
-  public static void addForStaticMethodsAndFields(Class<?> type,
-      Collection<InjectionPoint> injectionPoints) {
-    Errors errors = new Errors();
-    addInjectionPoints(type, Factory.FIELDS, true, injectionPoints, errors);
-    addInjectionPoints(type, Factory.METHODS, true, injectionPoints, errors);
-    ConfigurationException.throwNewIfNonEmpty(errors);
-  }
-
-  /**
-   * Adds all instance method and field injection points on {@code type} to {@code injectionPoints}.
-   * All fields are added first, and then all methods. Within the fields, supertype fields are added
-   * before subtype fields. Similarly, supertype methods are added before subtype methods.
-   *
-   * @throws RuntimeException if there is a malformed injection point on {@code type}, such as a
-   *      field with multiple binding annotations. When such an exception is thrown, the valid
-   *      injection points are still added to the collection.
-   */
-  public static void addForInstanceMethodsAndFields(Class<?> type,
-      List<InjectionPoint> injectionPoints) {
-    // TODO (crazybob): Filter out overridden members.
-    Errors errors = new Errors();
-    addInjectionPoints(type, Factory.FIELDS, false, injectionPoints, errors);
-    addInjectionPoints(type, Factory.METHODS, false, injectionPoints, errors);
-    ConfigurationException.throwNewIfNonEmpty(errors);
-  }
-
-  /**
    * Returns a new injection point for the injectable constructor of {@code type}.
    *
    * @param type a concrete type with exactly one constructor annotated {@literal @}{@link Inject},
@@ -253,7 +204,7 @@
     ConfigurationException.throwNewIfNonEmpty(errors);
 
     if (found != null) {
-      return get(found);
+      return new InjectionPoint(found);
     }
 
     // If no annotated constructor is found, look for a no-arg constructor
@@ -268,13 +219,47 @@
         throw new ConfigurationException(errors);
       }
 
-      return get(noArgCtor);
+      return new InjectionPoint(noArgCtor);
     } catch (NoSuchMethodException e) {
       errors.missingConstructor(type);
       throw new ConfigurationException(errors);
     }
   }
 
+  /**
+   * Adds all static method and field injection points on {@code type} to {@code injectionPoints}.
+   * All fields are added first, and then all methods. Within the fields, supertype fields are added
+   * before subtype fields. Similarly, supertype methods are added before subtype methods.
+   *
+   * @throws RuntimeException if there is a malformed injection point on {@code type}, such as a
+   *      field with multiple binding annotations. When such an exception is thrown, the valid
+   *      injection points are still added to the collection.
+   */
+  public static void addForStaticMethodsAndFields(Class<?> type, Collection<InjectionPoint> sink) {
+    Errors errors = new Errors();
+    addInjectionPoints(type, Factory.FIELDS, true, sink, errors);
+    addInjectionPoints(type, Factory.METHODS, true, sink, errors);
+    ConfigurationException.throwNewIfNonEmpty(errors);
+  }
+
+  /**
+   * Adds all instance method and field injection points on {@code type} to {@code injectionPoints}.
+   * All fields are added first, and then all methods. Within the fields, supertype fields are added
+   * before subtype fields. Similarly, supertype methods are added before subtype methods.
+   *
+   * @throws RuntimeException if there is a malformed injection point on {@code type}, such as a
+   *      field with multiple binding annotations. When such an exception is thrown, the valid
+   *      injection points are still added to the collection.
+   */
+  public static void addForInstanceMethodsAndFields(Class<?> type,
+      Collection<InjectionPoint> sink) {
+    // TODO (crazybob): Filter out overridden members.
+    Errors errors = new Errors();
+    addInjectionPoints(type, Factory.FIELDS, false, sink, errors);
+    addInjectionPoints(type, Factory.METHODS, false, sink, errors);
+    ConfigurationException.throwNewIfNonEmpty(errors);
+  }
+
   private static <M extends Member & AnnotatedElement> void addInjectionPoints(Class<?> type,
       Factory<M> factory, boolean statics, Collection<InjectionPoint> injectionPoints,
       Errors errors) {
@@ -322,7 +307,7 @@
         return type.getDeclaredFields();
       }
       public InjectionPoint create(Field member) {
-        return get(member);
+        return new InjectionPoint(member);
       }
     };
 
@@ -331,11 +316,13 @@
         return type.getDeclaredMethods();
       }
       public InjectionPoint create(Method member) {
-        return get(member);
+        return new InjectionPoint(member);
       }
     };
 
     M[] getMembers(Class<?> type);
     InjectionPoint create(M member);
   }
+
+  private static final long serialVersionUID = 0;
 }
diff --git a/src/com/google/inject/spi/InjectionRequest.java b/src/com/google/inject/spi/InjectionRequest.java
index 53601fb..ba60f6b 100644
--- a/src/com/google/inject/spi/InjectionRequest.java
+++ b/src/com/google/inject/spi/InjectionRequest.java
@@ -17,8 +17,6 @@
 package com.google.inject.spi;
 
 import static com.google.common.base.Preconditions.checkNotNull;
-import com.google.common.collect.ImmutableList;
-import java.util.List;
 
 /**
  * A request to inject the instance fields and methods of an instance. Requests are created
@@ -31,19 +29,19 @@
  */
 public final class InjectionRequest implements Element {
   private Object source;
-  private List<Object> instances;
+  private Object instance;
 
-  public InjectionRequest(Object source, Object[] instances) {
+  public InjectionRequest(Object source, Object instance) {
     this.source = checkNotNull(source, "source");
-    this.instances = ImmutableList.of(instances);
+    this.instance = checkNotNull(instance, "instance");
   }
 
   public Object getSource() {
     return source;
   }
 
-  public List<Object> getInstances() {
-    return instances;
+  public Object getInstance() {
+    return instance;
   }
 
   public <T> T acceptVisitor(ElementVisitor<T> visitor) {
diff --git a/src/com/google/inject/spi/Message.java b/src/com/google/inject/spi/Message.java
index c5a139e..f417ed3 100644
--- a/src/com/google/inject/spi/Message.java
+++ b/src/com/google/inject/spi/Message.java
@@ -42,23 +42,18 @@
   private final Throwable cause;
   private final List<Object> sources;
 
-  public Message(String message, Throwable cause, List<Object> sources) {
+  public Message(List<Object> sources, String message, Throwable cause) {
+    this.sources = ImmutableList.copyOf(sources);
     this.message = checkNotNull(message, "message");
     this.cause = cause;
-    this.sources = ImmutableList.copyOf(sources);
-  }
-
-  public Message(Object source, Throwable throwable) {
-    this("An exception was caught and reported. Message: " + throwable.getMessage(),
-        throwable, ImmutableList.of(source));
   }
 
   public Message(Object source, String message) {
-    this(message, null, ImmutableList.of(source));
+    this(ImmutableList.of(source), message, null);
   }
 
   public Message(String message) {
-    this(message, (Throwable) null);
+    this(ImmutableList.of(), message, null);
   }
 
   public String getSource() {
diff --git a/src/com/google/inject/spi/ModuleWriter.java b/src/com/google/inject/spi/ModuleWriter.java
index cd03f78..3d43d16 100644
--- a/src/com/google/inject/spi/ModuleWriter.java
+++ b/src/com/google/inject/spi/ModuleWriter.java
@@ -28,6 +28,7 @@
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Constructor;
 import java.util.List;
+import java.util.Set;
 import org.aopalliance.intercept.MethodInterceptor;
 
 /**
@@ -122,16 +123,13 @@
 
   public void writeRequestInjection(final Binder binder,
       final InjectionRequest command) {
-    List<Object> objects = command.getInstances();
-    binder.withSource(command.getSource())
-        .requestInjection(objects.toArray());
+    binder.withSource(command.getSource()).requestInjection(command.getInstance());
   }
 
   public void writeRequestStaticInjection(final Binder binder,
       final StaticInjectionRequest element) {
-    List<Class> types = element.getTypes();
-    binder.withSource(element.getSource())
-        .requestStaticInjection(types.toArray(new Class[types.size()]));
+    Class<?> type = element.getType();
+    binder.withSource(element.getSource()).requestStaticInjection(type);
   }
 
   public void writeConvertToTypes(final Binder binder, final TypeConverterBinding element) {
@@ -152,12 +150,13 @@
   public <T> ScopedBindingBuilder applyTarget(Binding<T> binding,
       final LinkedBindingBuilder<T> linkedBindingBuilder) {
     return binding.acceptTargetVisitor(new BindingTargetVisitor<T, ScopedBindingBuilder>() {
-      public ScopedBindingBuilder visitInstance(T instance) {
+      public ScopedBindingBuilder visitInstance(T instance, Set<InjectionPoint> injectionPoints) {
         linkedBindingBuilder.toInstance(instance);
         return null;
       }
 
-      public ScopedBindingBuilder visitProvider(Provider<? extends T> provider) {
+      public ScopedBindingBuilder visitProvider(Provider<? extends T> provider,
+          Set<InjectionPoint> injectionPoints) {
         return linkedBindingBuilder.toProvider(provider);
       }
 
@@ -177,7 +176,8 @@
         throw new IllegalArgumentException("Non-module element");
       }
 
-      public ScopedBindingBuilder visitConstructor(Constructor<? extends T> constructor) {
+      public ScopedBindingBuilder visitConstructor(Constructor<? extends T> constructor,
+          Set<InjectionPoint> injectionPoints) {
         throw new IllegalArgumentException("Non-module element");
       }
 
diff --git a/src/com/google/inject/spi/ProviderMethods.java b/src/com/google/inject/spi/ProviderMethods.java
new file mode 100644
index 0000000..53506b0
--- /dev/null
+++ b/src/com/google/inject/spi/ProviderMethods.java
@@ -0,0 +1,169 @@
+/**
+ * Copyright (C) 2007 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.spi;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+import com.google.common.collect.Lists;
+import com.google.inject.Binder;
+import com.google.inject.Key;
+import com.google.inject.Module;
+import com.google.inject.Provider;
+import com.google.inject.Provides;
+import com.google.inject.TypeLiteral;
+import com.google.inject.binder.AnnotatedBindingBuilder;
+import com.google.inject.internal.Annotations;
+import com.google.inject.internal.Errors;
+import com.google.inject.internal.TypeResolver;
+import com.google.inject.util.Modules;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.util.List;
+
+/**
+ * Creates bindings to methods annotated with {@literal @}{@link Provides}. Use the scope and
+ * binding annotations on the provider method to configure the binding.
+ */
+class ProviderMethods {
+
+  /**
+   * Returns a module which creates bindings for provider methods from the
+   * given object.
+   */
+  static Module from(Object providers) {
+    // avoid infinite recursion, since installing a module always installs itself
+    if (providers instanceof ProviderMethodsModule) {
+      return Modules.EMPTY_MODULE;
+    }
+
+    return new ProviderMethodsModule(providers);
+  }
+
+  static class ProviderMethodsModule implements Module {
+    final Object providers;
+    final TypeResolver typeResolver;
+    Binder binder;
+
+    ProviderMethodsModule(Object providers) {
+      this.providers = checkNotNull(providers, "providers");
+      this.typeResolver = new TypeResolver(providers.getClass());
+    }
+
+    public synchronized void configure(Binder binder) {
+      checkState(this.binder == null, "Re-entry is not allowed.");
+
+      for (Class c = providers.getClass(); c != Object.class; c = c.getSuperclass()) {
+        for (Method method : c.getDeclaredMethods()) {
+          if (!method.isAnnotationPresent(Provides.class)) {
+            continue;
+          }
+
+          this.binder = binder.withSource(method);
+          try {
+            bindProviderMethod(method);
+          } finally {
+            this.binder = null;
+          }
+        }
+      }
+    }
+
+    <T> void bindProviderMethod(final Method method) {
+      Errors errors = new Errors(method);
+
+      method.setAccessible(true);
+
+      Class<? extends Annotation> scopeAnnotation
+          = Annotations.findScopeAnnotation(errors, method.getAnnotations());
+      Annotation bindingAnnotation
+          = Annotations.findBindingAnnotation(errors, method, method.getAnnotations());
+
+      final List<Provider<?>> parameterProviders = findParameterProviders(errors, method);
+
+      for (Message message : errors.getMessages()) {
+        binder.addError(message);
+      }
+
+      // Define T as the method's return type.
+      @SuppressWarnings("unchecked") TypeLiteral<T> returnType
+          = (TypeLiteral<T>) TypeLiteral.get(typeResolver.getReturnType(method));
+
+      Provider<T> provider = new Provider<T>() {
+        public T get() {
+          Object[] parameters = new Object[parameterProviders.size()];
+          for (int i = 0; i < parameters.length; i++) {
+            parameters[i] = parameterProviders.get(i).get();
+          }
+
+          try {
+            // We know this cast is safe becase T is the method's return type.
+            @SuppressWarnings({ "unchecked", "UnnecessaryLocalVariable" })
+            T result = (T) method.invoke(providers, parameters);
+            return result;
+          }
+          catch (IllegalAccessException e) {
+            throw new AssertionError(e);
+          }
+          catch (InvocationTargetException e) {
+            throw new RuntimeException(e);
+          }
+        }
+      };
+
+      AnnotatedBindingBuilder<T> builder = binder.bind(returnType);
+
+      if (bindingAnnotation != null) {
+        builder.annotatedWith(bindingAnnotation);
+      }
+
+      builder.toProvider(provider);
+
+      if (scopeAnnotation != null) {
+        builder.in(scopeAnnotation);
+      }
+    }
+
+    List<Provider<?>> findParameterProviders(Errors errors, Method method) {
+      List<Provider<?>> parameterProviders = Lists.newArrayList();
+
+      List<Type> parameterTypes = typeResolver.getParameterTypes(method);
+      Annotation[][] parameterAnnotations = method.getParameterAnnotations();
+      for (int i = 0; i < parameterTypes.size(); i++) {
+        Type parameterType = parameterTypes.get(i);
+        Annotation bindingAnnotation
+            = Annotations.findBindingAnnotation(errors, method, parameterAnnotations[i]);
+        Key<?> key = bindingAnnotation == null ? Key.get(parameterType)
+            : Key.get(parameterType, bindingAnnotation);
+        Provider<?> provider = binder.getProvider(key);
+        parameterProviders.add(provider);
+      }
+
+      return parameterProviders;
+    }
+
+    @Override public boolean equals(Object o) {
+      return o instanceof ProviderMethodsModule
+          && ((ProviderMethodsModule) o).providers == providers;
+    }
+
+    @Override public int hashCode() {
+      return providers.hashCode();
+    }
+  }
+}
diff --git a/src/com/google/inject/spi/StaticInjectionRequest.java b/src/com/google/inject/spi/StaticInjectionRequest.java
index acc5a5f..107cc1b 100644
--- a/src/com/google/inject/spi/StaticInjectionRequest.java
+++ b/src/com/google/inject/spi/StaticInjectionRequest.java
@@ -18,8 +18,6 @@
 
 
 import static com.google.common.base.Preconditions.checkNotNull;
-import com.google.common.collect.ImmutableList;
-import java.util.List;
 
 /**
  * A request to inject the static fields and methods of type. Requests are created
@@ -32,19 +30,19 @@
  */
 public final class StaticInjectionRequest implements Element {
   private final Object source;
-  private final List<Class> types;
+  private final Class<?> type;
 
-  StaticInjectionRequest(Object source, Class[] types) {
+  StaticInjectionRequest(Object source, Class<?> type) {
     this.source = checkNotNull(source, "source");
-    this.types = ImmutableList.of(types);
+    this.type = checkNotNull(type, "type");
   }
 
   public Object getSource() {
     return source;
   }
 
-  public List<Class> getTypes() {
-    return types;
+  public Class<?> getType() {
+    return type;
   }
 
   public <T> T acceptVisitor(ElementVisitor<T> visitor) {
diff --git a/test/com/google/inject/AllTests.java b/test/com/google/inject/AllTests.java
index 1ca0b34..6c16fb7 100644
--- a/test/com/google/inject/AllTests.java
+++ b/test/com/google/inject/AllTests.java
@@ -24,8 +24,10 @@
 import com.google.inject.internal.UniqueAnnotationsTest;
 import com.google.inject.matcher.MatcherTest;
 import com.google.inject.spi.ElementsTest;
+import com.google.inject.spi.InjectionPointTest;
 import com.google.inject.spi.ModuleRewriterTest;
 import com.google.inject.spi.ModuleWriterTest;
+import com.google.inject.spi.ProviderMethodsTest;
 import com.google.inject.spi.SpiBindingsTest;
 import com.google.inject.util.ProvidersTest;
 import com.google.inject.util.TypesTest;
diff --git a/test/com/google/inject/Asserts.java b/test/com/google/inject/Asserts.java
index 2c06282..cb2ef58 100644
--- a/test/com/google/inject/Asserts.java
+++ b/test/com/google/inject/Asserts.java
@@ -66,6 +66,7 @@
    */
   public static void assertEqualWhenReserialized(Object object)
       throws IOException {
+    Assert.assertTrue("Expected serialVersionUID", hasSerialVersionUid(object));
     Object reserialized = reserialize(object);
     Assert.assertEquals(object, reserialized);
     Assert.assertEquals(object.hashCode(), reserialized.hashCode());
@@ -75,10 +76,19 @@
    * Fails unless {@code object} has the same toString value when reserialized.
    */
   public static void assertSimilarWhenReserialized(Object object) throws IOException {
+    Assert.assertTrue("Expected serialVersionUID", hasSerialVersionUid(object));
     Object reserialized = reserialize(object);
     Assert.assertEquals(object.toString(), reserialized.toString());
   }
 
+  static boolean hasSerialVersionUid(Object object) {
+    try {
+      return null != object.getClass().getDeclaredField("serialVersionUID");
+    } catch (NoSuchFieldException e) {
+      return false;
+    }
+  }
+
   static Object reserialize(Object object) throws IOException {
     try {
       ByteArrayOutputStream out = new ByteArrayOutputStream();
@@ -91,6 +101,7 @@
   }
 
   public static void assertNotSerializable(Object object) throws IOException {
+    Assert.assertFalse("Unexpected serialVersionUID", hasSerialVersionUid(object));
     try {
       reserialize(object);
       Assert.fail();
diff --git a/test/com/google/inject/ScopesTest.java b/test/com/google/inject/ScopesTest.java
index 26b56b0..a3388e4 100644
--- a/test/com/google/inject/ScopesTest.java
+++ b/test/com/google/inject/ScopesTest.java
@@ -294,8 +294,14 @@
   }
 
   public void testDuplicateScopeAnnotations() {
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      protected void configure() {
+        bindScope(CustomScoped.class, Scopes.NO_SCOPE);
+      }
+    });
+
     try {
-      Guice.createInjector().getInstance(SingletonAndCustomScoped.class);
+      injector.getInstance(SingletonAndCustomScoped.class);
       fail();
     } catch (ProvisionException expected) {
       assertContains(expected.getMessage(),
diff --git a/test/com/google/inject/spi/ElementsTest.java b/test/com/google/inject/spi/ElementsTest.java
index 9cde48d..447cf6d 100644
--- a/test/com/google/inject/spi/ElementsTest.java
+++ b/test/com/google/inject/spi/ElementsTest.java
@@ -346,7 +346,8 @@
           @Override public <T> Void visitBinding(Binding<T> command) {
             assertEquals(Key.get(String.class), command.getKey());
             command.acceptTargetVisitor(new FailingTargetVisitor<T>() {
-              public Void visitProvider(Provider<? extends T> provider) {
+              public Void visitProvider(Provider<? extends T> provider,
+                  Set<InjectionPoint> injectionPoints) {
                 assertSame(aProvider, provider);
                 return null;
               }
@@ -637,7 +638,14 @@
 
         new FailingElementVisitor() {
           @Override public Void visitInjectionRequest(InjectionRequest command) {
-            assertEquals(Arrays.asList(firstObject, secondObject), command.getInstances());
+            assertEquals(firstObject, command.getInstance());
+            return null;
+          }
+        },
+
+        new FailingElementVisitor() {
+          @Override public Void visitInjectionRequest(InjectionRequest command) {
+            assertEquals(secondObject, command.getInstance());
             return null;
           }
         }
@@ -654,7 +662,7 @@
 
         new FailingElementVisitor() {
           @Override public Void visitStaticInjectionRequest(StaticInjectionRequest command) {
-            assertEquals(Arrays.asList(ArrayList.class), command.getTypes());
+            assertEquals(ArrayList.class, command.getType());
             return null;
           }
         }
diff --git a/test/com/google/inject/InjectionPointTest.java b/test/com/google/inject/spi/InjectionPointTest.java
similarity index 71%
rename from test/com/google/inject/InjectionPointTest.java
rename to test/com/google/inject/spi/InjectionPointTest.java
index d52753d..0b0562c 100644
--- a/test/com/google/inject/InjectionPointTest.java
+++ b/test/com/google/inject/spi/InjectionPointTest.java
@@ -14,20 +14,21 @@
  * limitations under the License.
  */
 
-package com.google.inject;
+package com.google.inject.spi;
 
 import static com.google.common.collect.Iterables.getOnlyElement;
 import static com.google.inject.Asserts.assertEqualsBothWays;
 import static com.google.inject.Asserts.assertSimilarWhenReserialized;
+import com.google.inject.Inject;
+import com.google.inject.Key;
 import com.google.inject.internal.ErrorsException;
 import com.google.inject.name.Named;
 import com.google.inject.name.Names;
-import com.google.inject.spi.Dependency;
-import com.google.inject.spi.InjectionPoint;
 import java.io.IOException;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
+import junit.framework.Assert;
 import junit.framework.TestCase;
 
 /**
@@ -45,61 +46,66 @@
   public void testFieldInjectionPoint() throws NoSuchFieldException, IOException, ErrorsException {
     Field fooField = getClass().getField("foo");
 
-    InjectionPoint injectionPoint = InjectionPoint.get(fooField);
+    InjectionPoint injectionPoint = new InjectionPoint(fooField);
     assertSame(fooField, injectionPoint.getMember());
     assertFalse(injectionPoint.isOptional());
-    assertEquals("com.google.inject.InjectionPointTest.foo", injectionPoint.toString());
-    assertEqualsBothWays(injectionPoint, InjectionPoint.get(fooField));
+    assertEquals(getClass().getName() + ".foo", injectionPoint.toString());
+    assertEqualsBothWays(injectionPoint, new InjectionPoint(fooField));
     assertSimilarWhenReserialized(injectionPoint);
 
     Dependency<?> dependency = getOnlyElement(injectionPoint.getDependencies());
-    assertEquals("Key[type=java.lang.String, annotation=@com.google.inject.name.Named(value=a)]"
-        + "@com.google.inject.InjectionPointTest.foo", dependency.toString());
+    assertEquals("Key[type=java.lang.String, annotation=@com.google.inject.name.Named(value=a)]@"
+        + getClass().getName() + ".foo", dependency.toString());
     assertEquals(fooField, dependency.getInjectionPoint().getMember());
     assertEquals(-1, dependency.getParameterIndex());
-    assertEquals(Key.get(String.class, Names.named("a")), dependency.getKey());
+    Assert.assertEquals(Key.get(String.class, Names.named("a")), dependency.getKey());
     assertEquals(false, dependency.isNullable());
     assertSimilarWhenReserialized(dependency);
+    assertEqualsBothWays(dependency,
+        getOnlyElement(new InjectionPoint(fooField).getDependencies()));
   }
 
-  public void testMethodInjectionPoint() throws NoSuchMethodException, IOException, ErrorsException {
+  public void testMethodInjectionPoint() throws Exception {
     Method barMethod = getClass().getMethod("bar", String.class);
-    InjectionPoint injectionPoint = InjectionPoint.get(barMethod);
+    InjectionPoint injectionPoint = new InjectionPoint(barMethod);
     assertSame(barMethod, injectionPoint.getMember());
     assertFalse(injectionPoint.isOptional());
-    assertEquals("com.google.inject.InjectionPointTest.bar()", injectionPoint.toString());
-    assertEqualsBothWays(injectionPoint, InjectionPoint.get(barMethod));
+    assertEquals(getClass().getName() + ".bar()", injectionPoint.toString());
+    assertEqualsBothWays(injectionPoint, new InjectionPoint(barMethod));
     assertSimilarWhenReserialized(injectionPoint);
 
     Dependency<?> dependency = getOnlyElement(injectionPoint.getDependencies());
-    assertEquals("Key[type=java.lang.String, annotation=@com.google.inject.name.Named(value=b)]"
-        + "@com.google.inject.InjectionPointTest.bar()[0]", dependency.toString());
+    assertEquals("Key[type=java.lang.String, annotation=@com.google.inject.name.Named(value=b)]@"
+        + getClass().getName() + ".bar()[0]", dependency.toString());
     assertEquals(barMethod, dependency.getInjectionPoint().getMember());
     assertEquals(0, dependency.getParameterIndex());
     assertEquals(Key.get(String.class, Names.named("b")), dependency.getKey());
     assertEquals(false, dependency.isNullable());
     assertSimilarWhenReserialized(dependency);
+    assertEqualsBothWays(dependency,
+        getOnlyElement(new InjectionPoint(barMethod).getDependencies()));
   }
 
   public void testConstructorInjectionPoint() throws NoSuchMethodException, IOException,
       ErrorsException {
     Constructor<?> constructor = Constructable.class.getConstructor(String.class);
-    InjectionPoint injectionPoint = InjectionPoint.get(constructor);
+    InjectionPoint injectionPoint = new InjectionPoint(constructor);
     assertSame(constructor, injectionPoint.getMember());
     assertFalse(injectionPoint.isOptional());
-    assertEquals("com.google.inject.InjectionPointTest$Constructable.<init>()",
-        injectionPoint.toString());
-    assertEqualsBothWays(injectionPoint, InjectionPoint.get(constructor));
+    assertEquals(Constructable.class.getName() + ".<init>()", injectionPoint.toString());
+    assertEqualsBothWays(injectionPoint, new InjectionPoint(constructor));
     assertSimilarWhenReserialized(injectionPoint);
 
     Dependency<?> dependency = getOnlyElement(injectionPoint.getDependencies());
-    assertEquals("Key[type=java.lang.String, annotation=@com.google.inject.name.Named(value=c)]"
-        + "@com.google.inject.InjectionPointTest$Constructable.<init>()[0]", dependency.toString());
+    assertEquals("Key[type=java.lang.String, annotation=@com.google.inject.name.Named(value=c)]@"
+        + Constructable.class.getName() + ".<init>()[0]", dependency.toString());
     assertEquals(constructor, dependency.getInjectionPoint().getMember());
     assertEquals(0, dependency.getParameterIndex());
     assertEquals(Key.get(String.class, Names.named("c")), dependency.getKey());
     assertEquals(false, dependency.isNullable());
     assertSimilarWhenReserialized(dependency);
+    assertEqualsBothWays(dependency,
+        getOnlyElement(new InjectionPoint(constructor).getDependencies()));
   }
   
   public void testUnattachedDependency() throws IOException {
@@ -111,5 +117,6 @@
     assertEquals(Key.get(String.class, Names.named("d")), dependency.getKey());
     assertEquals(true, dependency.isNullable());
     assertSimilarWhenReserialized(dependency);
+    assertEqualsBothWays(dependency, Dependency.get(Key.get(String.class, Names.named("d"))));
   }
 }
diff --git a/test/com/google/inject/spi/ProviderMethodsTest.java b/test/com/google/inject/spi/ProviderMethodsTest.java
new file mode 100644
index 0000000..dda600a
--- /dev/null
+++ b/test/com/google/inject/spi/ProviderMethodsTest.java
@@ -0,0 +1,303 @@
+/**
+ * Copyright (C) 2007 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.spi;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.inject.AbstractModule;
+import com.google.inject.Asserts;
+import com.google.inject.Binder;
+import com.google.inject.BindingAnnotation;
+import com.google.inject.CreationException;
+import com.google.inject.Guice;
+import com.google.inject.Inject;
+import com.google.inject.Injector;
+import com.google.inject.Key;
+import com.google.inject.Module;
+import com.google.inject.Provides;
+import com.google.inject.Singleton;
+import com.google.inject.name.Named;
+import com.google.inject.name.Names;
+import com.google.inject.util.Types;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import java.lang.annotation.Target;
+import java.util.List;
+import java.util.Set;
+import junit.framework.TestCase;
+
+/**
+ * @author crazybob@google.com (Bob Lee)
+ */
+public class ProviderMethodsTest extends TestCase implements Module {
+
+  @SuppressWarnings("unchecked")
+  public void testProviderMethods() {
+    Injector injector = Guice.createInjector(this);
+
+    Bob bob = injector.getInstance(Bob.class);
+    assertEquals("A Bob", bob.getName());
+
+    Bob clone = injector.getInstance(Bob.class);
+    assertEquals("A Bob", clone.getName());
+
+    assertNotSame(bob, clone);
+    assertSame(bob.getDaughter(), clone.getDaughter());
+
+    Key soleBobKey = Key.get(Bob.class, Sole.class);
+    assertSame(
+        injector.getInstance(soleBobKey),
+        injector.getInstance(soleBobKey)
+    );
+  }
+
+  public void configure(Binder binder) {}
+
+  interface Bob {
+    String getName();
+    Dagny getDaughter();
+  }
+
+  interface Dagny {
+    int getAge();
+  }
+
+  @Provides
+  Bob provideBob(final Dagny dagny) {
+    return new Bob() {
+      public String getName() {
+        return "A Bob";
+      }
+
+      public Dagny getDaughter() {
+        return dagny;
+      }
+    };
+  }
+
+  @Provides
+  @Singleton
+  @Sole
+  Bob provideSoleBob(final Dagny dagny) {
+    return new Bob() {
+      public String getName() {
+        return "Only Bob";
+      }
+
+      public Dagny getDaughter() {
+        return dagny;
+      }
+    };
+  }
+
+  @Provides
+  @Singleton
+  Dagny provideDagny() {
+    return new Dagny() {
+      public int getAge() {
+        return 1;
+      }
+    };
+  }
+
+  @Retention(RUNTIME)
+  @Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
+  @BindingAnnotation
+  @interface Sole {}
+
+
+
+// We'll have to make getProvider() support circular dependencies before this
+// will work.
+//
+//  public void testCircularDependency() {
+//    Injector injector = Guice.createInjector(new Module() {
+//      public void configure(Binder binder) {
+//        binder.install(ProviderMethods.from(ProviderMethodsTest.this));
+//      }
+//    });
+//
+//    Foo foo = injector.getInstance(Foo.class);
+//    assertEquals(5, foo.getI());
+//    assertEquals(10, foo.getBar().getI());
+//    assertEquals(5, foo.getBar().getFoo().getI());
+//  }
+//
+//  interface Foo {
+//    Bar getBar();
+//    int getI();
+//  }
+//
+//  interface Bar {
+//    Foo getFoo();
+//    int getI();
+//  }
+//
+//  @Provides Foo newFoo(final Bar bar) {
+//    return new Foo() {
+//
+//      public Bar getBar() {
+//        return bar;
+//      }
+//
+//      public int getI() {
+//        return 5;
+//      }
+//    };
+//  }
+//
+//  @Provides Bar newBar(final Foo foo) {
+//    return new Bar() {
+//
+//      public Foo getFoo() {
+//        return foo;
+//      }
+//
+//      public int getI() {
+//        return 10;
+//      }
+//    };
+//  }
+
+
+  public void testMultipleBindingAnnotations() {
+    try {
+      Guice.createInjector(new AbstractModule() {
+        protected void configure() {}
+
+        @Provides @Named("A") @Blue
+        public String provideString() {
+          return "a";
+        }
+      });
+      fail();
+    } catch (CreationException expected) {
+      Asserts.assertContains(expected.getMessage(),
+          "more than one annotation annotated with @BindingAnnotation:", "Named", "Blue",
+          "at " + getClass().getName(), ".provideString(ProviderMethodsTest.java:");
+    }
+
+  }
+
+  @Retention(RUNTIME)
+  @BindingAnnotation @interface Blue {}
+
+  public void testGenericProviderMethods() {
+    Injector injector = Guice.createInjector(
+        new ProvideTs<String>("A", "B") {}, new ProvideTs<Integer>(1, 2) {});
+    
+    assertEquals("A", injector.getInstance(Key.get(String.class, Names.named("First"))));
+    assertEquals("B", injector.getInstance(Key.get(String.class, Names.named("Second"))));
+    assertEquals(ImmutableSet.of("A", "B"),
+        injector.getInstance(Key.get(Types.setOf(String.class))));
+
+    assertEquals(1, injector.getInstance(Key.get(Integer.class, Names.named("First"))).intValue());
+    assertEquals(2, injector.getInstance(Key.get(Integer.class, Names.named("Second"))).intValue());
+    assertEquals(ImmutableSet.of(1, 2),
+        injector.getInstance(Key.get(Types.setOf(Integer.class))));
+  }
+
+  abstract class ProvideTs<T> extends AbstractModule {
+    final T first;
+    final T second;
+
+    protected ProvideTs(T first, T second) {
+      this.first = first;
+      this.second = second;
+    }
+
+    protected void configure() {}
+
+    @Named("First") @Provides T provideFirst() {
+      return first;
+    }
+
+    @Named("Second") @Provides T provideSecond() {
+      return second;
+    }
+
+    @Provides Set<T> provideBoth(@Named("First") T first, @Named("Second") T second) {
+      return ImmutableSet.of(first, second);
+    }
+  }
+  
+  public void testAutomaticProviderMethods() {
+    Injector injector = Guice.createInjector((Module) new AbstractModule() {
+      protected void configure() { }
+      private int next = 1;
+
+      @Provides @Named("count")
+      public Integer provideCount() {
+        return next++;
+      }
+    });
+
+    assertEquals(1, injector.getInstance(Key.get(Integer.class, Names.named("count"))).intValue());
+    assertEquals(2, injector.getInstance(Key.get(Integer.class, Names.named("count"))).intValue());
+    assertEquals(3, injector.getInstance(Key.get(Integer.class, Names.named("count"))).intValue());
+  }
+
+  /**
+   * If the user installs provider methods for the module manually, that shouldn't cause a double
+   * binding of the provider methods' types.
+   */
+  public void testAutomaticProviderMethodsDoNotCauseDoubleBinding() {
+    Module installsSelf = new AbstractModule() {
+      protected void configure() {
+        install(this);
+        bind(Integer.class).toInstance(5);
+      }
+      @Provides public String provideString(Integer count) {
+        return "A" + count;
+      }
+    };
+
+    Injector injector = Guice.createInjector(installsSelf);
+    assertEquals("A5", injector.getInstance(String.class));
+  }
+  
+  public void testWildcardProviderMethods() {
+    final List<String> strings = ImmutableList.of("A", "B", "C");
+    final List<Number> numbers = ImmutableList.<Number>of(1, 2, 3);
+
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      protected void configure() {
+        @SuppressWarnings("unchecked")
+        Key<List<? super Integer>> listOfSupertypesOfInteger = (Key<List<? super Integer>>)
+            Key.get(Types.listOf(Types.supertypeOf(Integer.class)));
+        bind(listOfSupertypesOfInteger).toInstance(numbers);
+      }
+      @Provides public List<? extends CharSequence> provideCharSequences() {
+        return strings;
+      }
+      @Provides public Class<?> provideType() {
+        return Float.class;
+      }
+    });
+
+    assertSame(strings, injector.getInstance(HasWildcardInjection.class).charSequences);
+    assertSame(numbers, injector.getInstance(HasWildcardInjection.class).numbers);
+    assertSame(Float.class, injector.getInstance(HasWildcardInjection.class).type);
+  }
+
+  static class HasWildcardInjection {
+    @Inject List<? extends CharSequence> charSequences;
+    @Inject List<? super Integer> numbers;
+    @Inject Class<?> type;
+  }
+}
\ No newline at end of file
diff --git a/test/com/google/inject/spi/SpiBindingsTest.java b/test/com/google/inject/spi/SpiBindingsTest.java
index 6628eea..5da78c3 100644
--- a/test/com/google/inject/spi/SpiBindingsTest.java
+++ b/test/com/google/inject/spi/SpiBindingsTest.java
@@ -38,6 +38,7 @@
 import com.google.inject.name.Names;
 import java.lang.reflect.Constructor;
 import java.util.List;
+import java.util.Set;
 import java.util.logging.Logger;
 import junit.framework.TestCase;
 
@@ -76,7 +77,7 @@
             assertContains(command.getSource().toString(), "SpiBindingsTest.java");
             assertEquals(Key.get(String.class), command.getKey());
             command.acceptTargetVisitor(new FailingTargetVisitor<T>() {
-              @Override public Void visitInstance(T instance) {
+              @Override public Void visitInstance(T instance, Set<InjectionPoint> injectionPoints) {
                 assertEquals("A", instance);
                 return null;
               }
@@ -102,7 +103,8 @@
             assertContains(command.getSource().toString(), "SpiBindingsTest.java");
             assertEquals(Key.get(String.class), command.getKey());
             command.acceptTargetVisitor(new FailingTargetVisitor<T>() {
-              @Override public Void visitProvider(Provider<? extends T> provider) {
+              @Override public Void visitProvider(Provider<? extends T> provider,
+                  Set<InjectionPoint> injectionPoints) {
                 assertSame(stringProvider, provider);
                 return null;
               }
@@ -185,7 +187,8 @@
             assertContains(command.getSource().toString(), "SpiBindingsTest.java");
             assertEquals(Key.get(D.class), command.getKey());
             command.acceptTargetVisitor(new FailingTargetVisitor<T>() {
-              @Override public Void visitConstructor(Constructor<? extends T> constructor) {
+              @Override public Void visitConstructor(Constructor<? extends T> constructor,
+                  Set<InjectionPoint> injectionPoints) {
                 Constructor<?> expected = D.class.getDeclaredConstructors()[0];
                 assertEquals(expected, constructor);
                 return null;
@@ -210,7 +213,7 @@
             assertContains(command.getSource().toString(), "SpiBindingsTest.java");
             assertEquals(Key.get(Integer.class, Names.named("one")), command.getKey());
             command.acceptTargetVisitor(new FailingTargetVisitor<T>() {
-              @Override public Void visitInstance(T instance) {
+              @Override public Void visitInstance(T instance, Set<InjectionPoint> injectionPoints) {
                 assertEquals((Integer) 1, instance);
                 return null;
               }