Fixing the Provider injection bug, wherein we were using a Provider before it had been itself injected.

The fix requires keeping track of all instances yet-to-be-injected, and making two of the InternalFactories ensure their objects are injected before use.

git-svn-id: https://google-guice.googlecode.com/svn/trunk@421 d779f126-a31b-0410-b53b-1d3aecad763e
diff --git a/src/com/google/inject/BindCommandProcessor.java b/src/com/google/inject/BindCommandProcessor.java
index 458e174..ddf9c06 100644
--- a/src/com/google/inject/BindCommandProcessor.java
+++ b/src/com/google/inject/BindCommandProcessor.java
@@ -41,20 +41,22 @@
   private final Map<Class<? extends Annotation>, Scope> scopes;
   private final List<CreationListener> creationListeners
       = new ArrayList<CreationListener>();
-  private final List<ContextualCallable<Void>> preloaders
+  private final List<ContextualCallable<Void>> eagerSingletonCreators
       = new ArrayList<ContextualCallable<Void>>();
   private final Stage stage;
   private final Map<Key<?>, BindingImpl<?>> bindings;
-  private final List<MembersInjector> membersInjectors = new ArrayList<MembersInjector>();
+  private final Map<Object, Void> outstandingInjections;
 
   BindCommandProcessor(InjectorImpl injector,
       Map<Class<? extends Annotation>, Scope> scopes,
       Stage stage,
-      Map<Key<?>, BindingImpl<?>> bindings) {
+      Map<Key<?>, BindingImpl<?>> bindings,
+      Map<Object, Void> outstandingInjections) {
     this.injector = injector;
     this.scopes = scopes;
     this.stage = stage;
     this.bindings = bindings;
+    this.outstandingInjections = outstandingInjections;
   }
 
   @Override public <T> Boolean visitBind(BindCommand<T> command) {
@@ -105,8 +107,8 @@
 
     command.getTarget().acceptVisitor(new BindTarget.Visitor<T, Void>() {
       public Void visitToInstance(T instance) {
-        InternalFactory<? extends T> factory = new ConstantFactory<T>(instance);
-        registerInstanceForInjection(instance);
+        ConstantFactory<? extends T> factory = new ConstantFactory<T>(instance);
+        outstandingInjections.put(instance, null);
         InternalFactory<? extends T> scopedFactory
             = Scopes.scope(key, injector, factory, scope);
         createBinding(source, shouldPreload, new InstanceBindingImpl<T>(
@@ -115,9 +117,9 @@
       }
 
       public Void visitToProvider(Provider<? extends T> provider) {
-        InternalFactory<? extends T> factory
+        InternalFactoryToProviderAdapter<? extends T> factory
             = new InternalFactoryToProviderAdapter<T>(provider, source);
-        registerInstanceForInjection(provider);
+        outstandingInjections.put(provider, null);
         InternalFactory<? extends T> scopedFactory
             = Scopes.scope(key, injector, factory, scope);
         createBinding(source, shouldPreload, new ProviderInstanceBindingImpl<T>(
@@ -204,38 +206,6 @@
     return new InvalidBindingImpl<T>(injector, key, source);
   }
 
-  void registerInstanceForInjection(final Object o) {
-    membersInjectors.add(new MembersInjector(o));
-  }
-
-  public void validate(InjectorImpl injector) {
-    for (MembersInjector membersInjector : membersInjectors) {
-      membersInjector.checkDependencies(injector);
-    }
-  }
-
-  public void injectMembers(InjectorImpl injector) {
-    for (MembersInjector membersInjector : membersInjectors) {
-      membersInjector.injectMembers(injector);
-    }
-  }
-
-  private static class MembersInjector {
-    final Object o;
-
-    MembersInjector(Object o) {
-      this.o = o;
-    }
-
-    void checkDependencies(InjectorImpl injector) {
-      injector.injectors.get(o.getClass());
-    }
-
-    void injectMembers(InjectorImpl injector) {
-      injector.injectMembers(o);
-    }
-  }
-
   @Override public Boolean visitBindConstant(BindConstantCommand command) {
     Object value = command.getTarget().get();
     if (value == null) {
@@ -257,7 +227,7 @@
     // Register to preload if necessary.
     if (binding.getScope() == Scopes.SINGLETON) {
       if (stage == Stage.PRODUCTION || shouldPreload) {
-        preloaders.add(new BindingPreloader(binding.key, binding.internalFactory));
+        eagerSingletonCreators.add(new EagerSingletonCreator(binding.key, binding.internalFactory));
       }
     } else {
       if (shouldPreload) {
@@ -266,15 +236,10 @@
     }
   }
 
-  public void runPreloaders(InjectorImpl injector) {
-    injector.callInContext(new ContextualCallable<Void>() {
-      public Void call(InternalContext context) {
-        for (ContextualCallable<Void> preloader : preloaders) {
-          preloader.call(context);
-        }
-        return null;
-      }
-    });
+  public void createEagerSingletons(InjectorImpl injector) {
+    for (ContextualCallable<Void> preloader : eagerSingletonCreators) {
+      injector.callInContext(preloader);
+    }
   }
 
   public void runCreationListeners(InjectorImpl injector) {
@@ -283,11 +248,11 @@
     }
   }
 
-  private static class BindingPreloader implements ContextualCallable<Void> {
+  private static class EagerSingletonCreator implements ContextualCallable<Void> {
     private final Key<?> key;
     private final InternalFactory<?> factory;
 
-    public BindingPreloader(Key<?> key, InternalFactory<?> factory) {
+    public EagerSingletonCreator(Key<?> key, InternalFactory<?> factory) {
       this.key = key;
       this.factory = Objects.nonNull(factory, "factory");
     }
diff --git a/src/com/google/inject/BinderImpl.java b/src/com/google/inject/BinderImpl.java
deleted file mode 100644
index c24f04c..0000000
--- a/src/com/google/inject/BinderImpl.java
+++ /dev/null
@@ -1,794 +0,0 @@
-/**
- * Copyright (C) 2006 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;
-
-import com.google.inject.InjectorImpl.SingleMemberInjector;
-import static com.google.inject.Scopes.SINGLETON;
-import com.google.inject.internal.Annotations;
-import static com.google.inject.internal.Objects.nonNull;
-import com.google.inject.internal.StackTraceElements;
-import com.google.inject.internal.Stopwatch;
-import com.google.inject.internal.Objects;
-import com.google.inject.internal.Strings;
-import com.google.inject.matcher.Matcher;
-import com.google.inject.matcher.Matchers;
-import com.google.inject.matcher.AbstractMatcher;
-import com.google.inject.spi.Message;
-import com.google.inject.spi.SourceProviders;
-import com.google.inject.spi.TypeConverter;
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Member;
-import java.lang.reflect.Method;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Type;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.LinkedList;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import org.aopalliance.intercept.MethodInterceptor;
-
-/**
- * Builds a dependency injection {@link Injector}. Binds {@link Key}s to
- * implementations.
- *
- * @author crazybob@google.com (Bob Lee)
- */
-class BinderImpl implements Binder {
-
-  static {
-    SourceProviders.skip(BinderImpl.class);
-  }
-
-  private static final Logger logger
-      = Logger.getLogger(BinderImpl.class.getName());
-
-  final LinkedList<BindingBuilderImpl<?>> bindingBuilders
-      = new LinkedList<BindingBuilderImpl<?>>();
-  final List<ConstantBindingBuilderImpl> constantBindingBuilders
-      = new ArrayList<ConstantBindingBuilderImpl>();
-  final Map<Class<? extends Annotation>, Scope> scopes =
-      new HashMap<Class<? extends Annotation>, Scope>();
-
-  final List<StaticInjection> staticInjections
-      = new ArrayList<StaticInjection>();
-
-  final Set<Module> modulesInstalled = new HashSet<Module>();
-
-  InjectorImpl injector;
-
-  final Stage stage;
-
-  final Collection<Message> errorMessages = new ArrayList<Message>();
-
-  final ProxyFactoryBuilder proxyFactoryBuilder;
-
-  /**
-   * Constructs a new builder.
-   *
-   * @param stage we're running in. If the stage is {@link Stage#PRODUCTION},
-   *  we will eagerly load singletons.
-   */
-  public BinderImpl(Stage stage) {
-    bindScope(Singleton.class, SINGLETON);
-
-    bind(Logger.class, SourceProviders.UNKNOWN_SOURCE)
-        .toInternalFactory(new LoggerFactory());
-    bind(Stage.class, SourceProviders.UNKNOWN_SOURCE).toInstance(stage);
-
-    this.proxyFactoryBuilder = new ProxyFactoryBuilder();
-
-    this.stage = stage;
-
-    // Configure type converters.
-    convertToPrimitiveType(int.class, Integer.class);
-    convertToPrimitiveType(long.class, Long.class);
-    convertToPrimitiveType(boolean.class, Boolean.class);
-    convertToPrimitiveType(byte.class, Byte.class);
-    convertToPrimitiveType(short.class, Short.class);
-    convertToPrimitiveType(float.class, Float.class);
-    convertToPrimitiveType(double.class, Double.class);
-
-    TypeConverter characterConverter = new TypeConverter() {
-      public Object convert(String value, TypeLiteral<?> toType) {
-        value = value.trim();
-        if (value.length() != 1) {
-          throw new RuntimeException("Length != 1.");
-        }
-        return value.charAt(0);
-      }
-
-      @Override
-      public String toString() {
-        return "TypeConverter<Character>";
-      }
-    };
-
-    convertToClass(char.class, characterConverter);
-    convertToClass(Character.class, characterConverter);
-
-    convertToClasses(Matchers.subclassesOf(Enum.class), new TypeConverter() {
-      @SuppressWarnings("unchecked")
-      public Object convert(String value, TypeLiteral<?> toType) {
-        return Enum.valueOf((Class) toType.getRawType(), value);
-      }
-
-      @Override
-      public String toString() {
-        return "TypeConverter<E extends Enum<E>>";
-      }
-    });
-
-    internalConvertToTypes(
-      new AbstractMatcher<TypeLiteral<?>>() {
-        public boolean matches(TypeLiteral<?> typeLiteral) {
-          return typeLiteral.getRawType() == Class.class;
-        }
-
-        @Override
-        public String toString() {
-          return "Class<?>";
-        }
-      },
-      new TypeConverter() {
-        @SuppressWarnings("unchecked")
-        public Object convert(String value, TypeLiteral<?> toType) {
-          try {
-            return Class.forName(value);
-          }
-          catch (ClassNotFoundException e) {
-            throw new RuntimeException(e.getMessage());
-          }
-        }
-
-        @Override
-        public String toString() {
-          return "TypeConverter<Class<?>>";
-        }
-      }
-    );
-  }
-
-  /**
-   * Constructs a new builder for a development environment (see
-   * {@link Stage#DEVELOPMENT}).
-   */
-  public BinderImpl() {
-    this(Stage.DEVELOPMENT);
-  }
-
-  final List<MatcherAndConverter<?>> converters
-      = new ArrayList<MatcherAndConverter<?>>();
-
-  <T> void convertToType(TypeLiteral<T> type,
-      TypeConverter converter) {
-    convertToClasses(Matchers.identicalTo(type), converter);
-  }
-
-  <T> void convertToClass(Class<T> type,
-      TypeConverter converter) {
-    convertToClasses(Matchers.identicalTo(type), converter);
-  }
-
-  void convertToClasses(final Matcher<? super Class<?>> typeMatcher,
-      TypeConverter converter) {
-    internalConvertToTypes(new AbstractMatcher<TypeLiteral<?>>() {
-      public boolean matches(TypeLiteral<?> typeLiteral) {
-        Type type = typeLiteral.getType();
-        if (!(type instanceof Class)) {
-          return false;
-        }
-        Class<?> clazz = (Class<?>) type;
-        return typeMatcher.matches(clazz);
-      }
-
-      public String toString() {
-        return typeMatcher.toString();
-      }
-    }, converter);
-  }
-
-  public void internalConvertToTypes(Matcher<? super TypeLiteral<?>> typeMatcher,
-      TypeConverter converter) {
-    converters.add(MatcherAndConverter.newInstance(typeMatcher, converter,
-        SourceProviders.UNKNOWN_SOURCE));
-  }
-
-  public void convertToTypes(Matcher<? super TypeLiteral<?>> typeMatcher,
-      TypeConverter converter) {
-    converters.add(MatcherAndConverter.newInstance(typeMatcher, converter));
-  }
-
-  <T> void convertToPrimitiveType(Class<T> primitiveType,
-      final Class<T> wrapperType) {
-    try {
-      final Method parser = wrapperType.getMethod(
-        "parse" + Strings.capitalize(primitiveType.getName()), String.class);
-
-      TypeConverter typeConverter = new TypeConverter() {
-        @SuppressWarnings("unchecked")
-        public Object convert(String value, TypeLiteral<?> toType) {
-          try {
-            return parser.invoke(null, value);
-          }
-          catch (IllegalAccessException e) {
-            throw new AssertionError(e);
-          }
-          catch (InvocationTargetException e) {
-            throw new RuntimeException(e.getTargetException().getMessage());
-          }
-        }
-
-        @Override
-        public String toString() {
-          return "TypeConverter<" + wrapperType.getSimpleName() + ">";
-        }
-      };
-
-      convertToClass(primitiveType, typeConverter);
-      convertToClass(wrapperType, typeConverter);
-    }
-    catch (NoSuchMethodException e) {
-      throw new AssertionError(e);
-    }
-  }
-
-  public Stage currentStage() {
-    return stage;
-  }
-
-  final List<CreationListener> creationListeners
-      = new ArrayList<CreationListener>();
-
-  interface CreationListener {
-    void notify(InjectorImpl injector);
-  }
-
-  final List<MembersInjector> membersInjectors
-      = new ArrayList<MembersInjector>();
-
-  static class MembersInjector {
-
-    final Object o;
-
-    MembersInjector(Object o) {
-      this.o = o;
-    }
-
-    void checkDependencies(InjectorImpl injector) {
-      injector.injectors.get(o.getClass());
-    }
-
-    void injectMembers(InjectorImpl injector) {
-      injector.injectMembers(o);
-    }    
-  }
-
-  public void bindInterceptor(Matcher<? super Class<?>> classMatcher,
-      Matcher<? super Method> methodMatcher, MethodInterceptor... interceptors) {
-    proxyFactoryBuilder.intercept(classMatcher, methodMatcher, interceptors);
-  }
-
-  public void bindScope(Class<? extends Annotation> annotationType,
-      Scope scope) {
-    if (!Scopes.isScopeAnnotation(annotationType)) {
-      addError(StackTraceElements.forType(annotationType),
-          ErrorMessages.MISSING_SCOPE_ANNOTATION);
-      // Go ahead and bind anyway so we don't get collateral errors.
-    }
-
-    if (!Annotations.isRetainedAtRuntime(annotationType)) {
-      addError(StackTraceElements.forType(annotationType),
-          ErrorMessages.MISSING_RUNTIME_RETENTION, source());
-      // Go ahead and bind anyway so we don't get collateral errors.
-    }
-
-    Scope existing = scopes.get(nonNull(annotationType, "annotation type"));
-    if (existing != null) {
-      addError(source(), ErrorMessages.DUPLICATE_SCOPES, existing,
-          annotationType, scope);
-    }
-    else {
-      scopes.put(annotationType, nonNull(scope, "scope"));
-    }
-  }
-
-  static boolean inGuiceNamespace(Class<?> clazz) {
-    return clazz == Provider.class;
-  }
-
-  public <T> BindingBuilderImpl<T> bind(Key<T> key) {
-    Object source = source();
-
-    BindingBuilderImpl<T> builder =
-        new BindingBuilderImpl<T>(this, key, source);
-
-    Class<? super T> rawType = key.getTypeLiteral().getRawType();
-
-    if (rawType == Provider.class) {
-      addError(source, ErrorMessages.BINDING_TO_PROVIDER);
-    } else if (Logger.class == rawType) {
-      addError(source, ErrorMessages.LOGGER_ALREADY_BOUND);
-    } else {
-      bindingBuilders.add(builder);
-    }
-
-    return builder;
-  }
-
-  public <T> BindingBuilderImpl<T> bind(TypeLiteral<T> typeLiteral) {
-    return bind(Key.get(typeLiteral));
-  }
-
-  public <T> BindingBuilderImpl<T> bind(Class<T> clazz) {
-    return bind(Key.get(clazz));
-  }
-
-  /**
-   * Internal use only.
-   */
-  <T> BindingBuilderImpl<T> bind(Class<T> clazz, Object source) {
-    Key<T> key = Key.get(clazz);
-    BindingBuilderImpl<T> builder =
-        new BindingBuilderImpl<T>(this, key, source);
-    // Put Guice's bindings in front of user's bindings.
-    bindingBuilders.addFirst(builder);
-    return builder;
-  }
-
-  public ConstantBindingBuilderImpl bindConstant() {
-    ConstantBindingBuilderImpl constantBuilder
-        = new ConstantBindingBuilderImpl(this, source());
-    constantBindingBuilders.add(constantBuilder);
-    return constantBuilder;
-  }
-
-  public void requestStaticInjection(Class<?>... types) {
-    staticInjections.add(new StaticInjection(source(), types));
-  }
-
-  public void install(Module module) {
-    if (modulesInstalled.add(module)) {
-      module.configure(this);
-    }
-  }
-
-  public void addError(String message, Object... arguments) {
-    configurationErrorHandler.handle(source(), message, arguments);
-  }
-
-  public void addError(Throwable t) {
-    Object source = source();
-    String message = ErrorMessages.getRootMessage(t);
-    String logMessage = String.format(
-        ErrorMessages.EXCEPTION_REPORTED_BY_MODULE, message);
-    logger.log(Level.INFO, logMessage, t);
-    addError(source, ErrorMessages.EXCEPTION_REPORTED_BY_MODULE_SEE_LOG,
-        message);
-  }
-
-  void addError(Object source, String message, Object... arguments) {
-    configurationErrorHandler.handle(source, message, arguments);
-  }
-
-  void addError(Object source, String message) {
-    configurationErrorHandler.handle(source, message);
-  }
-
-  /**
-   * Adds an error message to be reported at creation time.
-   */
-  void add(Message errorMessage) {
-    errorMessages.add(errorMessage);
-  }
-
-  final Stopwatch stopwatch = new Stopwatch();
-
-  /**
-   * Creates a {@link Injector} instance. Injects static members for classes
-   * which were registered using {@link #requestStaticInjection(Class...)}.
-   *
-   * @throws CreationException if configuration errors are found. The
-   *     expectation is that the application will log this exception and exit.
-   * @throws IllegalStateException if called more than once
-   */
-  Injector createInjector() throws CreationException {
-    return createInjector(null);
-  }
-
-  /**
-   * Creates a {@link Injector} instance. Injects static members for classes
-   * which were registered using {@link #requestStaticInjection(Class...)}.
-   *
-   * @param parent The parent injector to chain object creation to.
-   * @throws CreationException if configuration errors are found. The
-   *     expectation is that the application will log this exception and exit.
-   * @throws IllegalStateException if called more than once
-   */
-  Injector createInjector(Injector parent) throws CreationException {
-    stopwatch.resetAndLog(logger, "Configuration (running the modules)");
-
-    Map<Key<?>, BindingImpl<?>> bindings
-        = new HashMap<Key<?>, BindingImpl<?>>();
-    injector = new InjectorImpl(
-        parent, proxyFactoryBuilder.create(), bindings, scopes, converters);
-
-    // Create default bindings.
-    // We use toProvider() instead of toInstance() to avoid infinite recursion
-    // in toString().
-    bind(Injector.class, SourceProviders.UNKNOWN_SOURCE)
-        .toProvider(new InjectorProvider(injector));
-
-    injector.setErrorHandler(configurationErrorHandler);
-
-    createConstantBindings();
-
-    // Commands to execute before returning the Injector instance.
-    final List<ContextualCallable<Void>> preloaders
-        = new ArrayList<ContextualCallable<Void>>();
-
-    createBindings(preloaders);
-
-    stopwatch.resetAndLog(logger, "Binding creation");
-
-    injector.index();
-
-    stopwatch.resetAndLog(logger, "Binding indexing");
-
-    for (CreationListener creationListener : creationListeners) {
-      creationListener.notify(injector);
-    }
-
-    stopwatch.resetAndLog(logger, "Validation");
-
-    for (StaticInjection staticInjection : staticInjections) {
-      staticInjection.createMemberInjectors(injector);
-    }
-
-    stopwatch.resetAndLog(logger, "Static validation");
-
-    for (MembersInjector membersInjector : membersInjectors) {
-      membersInjector.checkDependencies(injector);
-    }
-
-    stopwatch.resetAndLog(logger, "Instance member validation");
-
-    // Blow up if we encountered errors.
-    if (!errorMessages.isEmpty()) {
-      throw new CreationException(errorMessages);
-    }
-
-    // Switch to runtime error handling.
-    injector.setErrorHandler(new RuntimeErrorHandler());
-
-    // If we're in the tool stage, stop here. Don't eagerly inject or load
-    // anything.
-    if (stage == Stage.TOOL) {
-      // TODO: Wrap this and prevent usage of anything besides getBindings().
-      return injector;
-    }
-
-    // Inject static members.
-    for (StaticInjection staticInjection : staticInjections) {
-      staticInjection.runMemberInjectors(injector);
-    }
-
-    stopwatch.resetAndLog(logger, "Static member injection");
-
-    // Inject pre-existing instances.
-    for (MembersInjector membersInjector : membersInjectors) {
-      membersInjector.injectMembers(injector);
-    }
-
-    stopwatch.resetAndLog(logger, "Instance injection");
-
-    // Run preloading commands.
-    runPreloaders(injector, preloaders);
-
-    stopwatch.resetAndLog(logger, "Preloading");
-
-    return injector;
-  }
-
-  private void runPreloaders(InjectorImpl injector,
-      final List<ContextualCallable<Void>> preloaders) {
-    injector.callInContext(new ContextualCallable<Void>() {
-      public Void call(InternalContext context) {
-        for (ContextualCallable<Void> preloader : preloaders) {
-          preloader.call(context);
-        }
-        return null;
-      }
-    });
-  }
-
-  private void createBindings(List<ContextualCallable<Void>> preloaders) {
-    for (BindingBuilderImpl<?> builder : bindingBuilders) {
-      createBinding(builder, preloaders);
-    }
-  }
-
-  private <T> void createBinding(BindingBuilderImpl<T> builder,
-      List<ContextualCallable<Void>> preloaders) {
-    BindingImpl<T> binding = builder.build(injector);
-
-    putBinding(binding);
-
-    // Register to preload if necessary.
-    boolean preload = stage == Stage.PRODUCTION;
-    if (binding.getScope() == Scopes.SINGLETON) {
-      if (preload || builder.shouldPreload()) {
-        preloaders.add(
-            new BindingPreloader(binding.key, binding.internalFactory));
-      }
-    }
-    else {
-      if (builder.shouldPreload()) {
-        addError(builder.getSource(), ErrorMessages.PRELOAD_NOT_ALLOWED);
-      }
-    }
-  }
-
-  private void createConstantBindings() {
-    for (ConstantBindingBuilderImpl builder : constantBindingBuilders) {
-      createConstantBinding(builder);
-    }
-  }
-
-  private void createConstantBinding(ConstantBindingBuilderImpl builder) {
-    if (builder.hasValue()) {
-      putBinding(builder.createBinding(injector));
-    }
-    else {
-      addError(builder.getSource(), ErrorMessages.MISSING_CONSTANT_VALUE);
-    }
-  }
-
-  private static Set<Class<?>> FORBIDDEN_TYPES = forbiddenTypes();
-
-  @SuppressWarnings("unchecked") // For generic array creation.
-  private static Set<Class<?>> forbiddenTypes() {
-    Set<Class<?>> set = new HashSet<Class<?>>();
-    
-    Collections.addAll(set,
-
-        // It's unfortunate that we have to maintain a blacklist of specific
-        // classes, but we can't easily block the whole package because of
-        // all our unit tests.
-
-        AbstractModule.class,
-        Binder.class,
-        Binding.class,
-        Key.class,
-        Module.class,
-        Provider.class,
-        Scope.class,
-        TypeLiteral.class);
-    return Collections.unmodifiableSet(set);
-  }
-
-  void putBinding(BindingImpl<?> binding) {
-    Key<?> key = binding.getKey();
-    Map<Key<?>, BindingImpl<?>> bindings = injector.internalBindings();
-    Binding<?> original = bindings.get(key);
-
-    Class<?> rawType = key.getRawType();
-    if (FORBIDDEN_TYPES.contains(rawType)) {
-      addError(binding.getSource(), ErrorMessages.CANNOT_BIND_TO_GUICE_TYPE,
-          rawType.getSimpleName());
-      return;
-    }
-
-    if (bindings.containsKey(key)) {
-      addError(binding.getSource(), ErrorMessages.BINDING_ALREADY_SET, key,
-          original.getSource());
-    }
-    else {
-      bindings.put(key, binding);
-    }
-  }
-
-  /**
-   * Gets the current source.
-   */
-  Object source() {
-    return SourceProviders.defaultSource();    
-  }
-
-  ErrorHandler configurationErrorHandler = new AbstractErrorHandler() {
-
-    public void handle(Object source, String message) {
-      add(new Message(source, message));
-    }
-  };
-
-  /**
-   * Handles errors after the injector is created.
-   */
-  static class RuntimeErrorHandler extends AbstractErrorHandler {
-
-    static ErrorHandler INSTANCE = new RuntimeErrorHandler();
-
-    public void handle(Object source, String message) {
-      throw new ConfigurationException("Error at " + source + " " + message);
-    }
-  }
-
-  /**
-   * A requested static injection.
-   */
-  class StaticInjection {
-
-    final Object source;
-    final Class<?>[] types;
-    final List<SingleMemberInjector> memberInjectors
-        = new ArrayList<SingleMemberInjector>();
-
-    public StaticInjection(Object source, Class<?>[] types) {
-      this.source = source;
-      this.types = types;
-    }
-
-    void createMemberInjectors(final InjectorImpl injector) {
-      injector.withDefaultSource(source,
-          new Runnable() {
-            public void run() {
-              for (Class<?> clazz : types) {
-                injector.addSingleInjectorsForFields(
-                    clazz.getDeclaredFields(), true, memberInjectors);
-                injector.addSingleInjectorsForMethods(
-                    clazz.getDeclaredMethods(), true, memberInjectors);
-              }
-            }
-          });
-    }
-
-    void runMemberInjectors(InjectorImpl injector) {
-      injector.callInContext(new ContextualCallable<Void>() {
-        public Void call(InternalContext context) {
-          for (SingleMemberInjector injector : memberInjectors) {
-            injector.inject(context, null);
-          }
-          return null;
-        }
-      });
-    }
-  }
-
-  static class BindingPreloader implements ContextualCallable<Void> {
-
-    private final Key<?> key;
-    private final InternalFactory<?> factory;
-
-    public BindingPreloader(Key<?> key, InternalFactory<?> factory) {
-      this.key = key;
-      this.factory = Objects.nonNull(factory, "factory");
-    }
-
-    public Void call(InternalContext context) {
-      InjectionPoint<?> injectionPoint
-          = InjectionPoint.newInstance(key, context.getInjectorImpl());
-      context.setInjectionPoint(injectionPoint);
-      try {
-        factory.get(context, injectionPoint);
-        return null;
-      }
-      catch(ProvisionException provisionException) {
-        provisionException.addContext(injectionPoint);
-        throw provisionException;
-      }
-      finally {
-        context.setInjectionPoint(null);
-      }
-    }
-  }
-
-  public <T> Provider<T> getProvider(Key<T> key) {
-    ProviderProxy<T> providerProxy = new ProviderProxy<T>(source(), key);
-    creationListeners.add(providerProxy);
-    return providerProxy;
-  }
-
-  public <T> Provider<T> getProvider(Class<T> type) {
-    return getProvider(Key.get(type));
-  }
-
-  /**
-   * A reference to a provider which can be filled in once the Injector has
-   * been created.
-   */
-  static class ProviderProxy<T> implements Provider<T>, CreationListener {
-
-    final Object source;
-    final Key<T> key;
-
-    // We don't have to synchronize access to this field because it doesn't
-    // change after the Injector has been created.
-    Provider<T> delegate = illegalProvider();
-
-    public ProviderProxy(Object source, Key<T> key) {
-      this.source = source;
-      this.key = key;
-    }
-
-    public void notify(final InjectorImpl injector) {
-      injector.withDefaultSource(source, new Runnable() {
-        public void run() {
-          try {
-            delegate = injector.getProvider(key);
-          }
-          catch (ConfigurationException e) {
-            ErrorMessages.handleMissingBinding(injector, source, key);
-          }
-        }
-      });
-    }
-
-    public T get() {
-      return delegate.get();
-    }
-  }
-
-  static final Provider<Object> ILLEGAL_PROVIDER = new Provider<Object>() {
-    public Object get() {
-      throw new IllegalStateException("This provider cannot be used until the"
-          + " Injector has been created.");
-    }
-  };
-
-  @SuppressWarnings("unchecked")
-  static <T> Provider<T> illegalProvider() {
-    return (Provider<T>) ILLEGAL_PROVIDER;
-  }
-
-  static class LoggerFactory implements InternalFactory<Logger> {
-
-    public Logger get(InternalContext context, InjectionPoint<?> injectionPoint) {
-      Member member = injectionPoint.getMember();
-      return member == null
-          ? Logger.getAnonymousLogger()
-          : Logger.getLogger(member.getDeclaringClass().getName());
-    }
-
-    public String toString() {
-      return "Provider<Logger>";
-    }
-  }
-
-  static class InjectorProvider implements Provider<Injector> {
-
-    final Injector injector;
-
-    InjectorProvider(Injector injector) {
-      this.injector = injector;
-    }
-
-    public Injector get() {
-      return injector;
-    }
-
-    public String toString() {
-      return "Provider<Injector>";
-    }
-  }
-}
diff --git a/src/com/google/inject/ConstantFactory.java b/src/com/google/inject/ConstantFactory.java
index 6d743fa..f02e6cf 100644
--- a/src/com/google/inject/ConstantFactory.java
+++ b/src/com/google/inject/ConstantFactory.java
@@ -30,6 +30,7 @@
   }
 
   public T get(InternalContext context, InjectionPoint injectionPoint) {
+    context.ensureMemberInjected(value);
     return value;
   }
 
diff --git a/src/com/google/inject/InjectorBuilder.java b/src/com/google/inject/InjectorBuilder.java
index cf02d38..4d01aa5 100644
--- a/src/com/google/inject/InjectorBuilder.java
+++ b/src/com/google/inject/InjectorBuilder.java
@@ -135,7 +135,8 @@
     stopwatch.resetAndLog("Converters creation");
 
     bindCommandProcesor = new BindCommandProcessor(
-        injector, injector.scopes, stage, injector.explicitBindings);
+        injector, injector.scopes, stage, injector.explicitBindings,
+        injector.outstandingInjections);
     bindCommandProcesor.processCommands(commands, configurationErrorHandler);
     stopwatch.resetAndLog("Binding creation");
 
@@ -159,7 +160,7 @@
     requestStaticInjectionCommandProcessor.validate(injector);
     stopwatch.resetAndLog("Static validation");
 
-    bindCommandProcesor.validate(injector);
+    injector.validateOustandingInjections();
     stopwatch.resetAndLog("Instance member validation");
 
     new GetProviderProcessor(injector)
@@ -175,13 +176,12 @@
   private void fulfillInjectionRequests() {
     futureInjector.initialize(injector);
 
-    // TODO(jessewilson): sort these injections by their dependencies
     requestStaticInjectionCommandProcessor.injectMembers(injector);
     stopwatch.resetAndLog("Static member injection");
-    bindCommandProcesor.injectMembers(injector);
+    injector.fulfillOutstandingInjections();
     stopwatch.resetAndLog("Instance injection");
 
-    bindCommandProcesor.runPreloaders(injector);
+    bindCommandProcesor.createEagerSingletons(injector);
     stopwatch.resetAndLog("Preloading");
   }
 
diff --git a/src/com/google/inject/InjectorImpl.java b/src/com/google/inject/InjectorImpl.java
index ed34caf..4c5b7a5 100644
--- a/src/com/google/inject/InjectorImpl.java
+++ b/src/com/google/inject/InjectorImpl.java
@@ -37,14 +37,7 @@
 import java.lang.reflect.Modifier;
 import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 import java.util.concurrent.Callable;
 import net.sf.cglib.reflect.FastClass;
 import net.sf.cglib.reflect.FastMethod;
@@ -96,6 +89,8 @@
       = new ArrayList<MatcherAndConverter<?>>();
   final Map<Key<?>, BindingImpl<?>> parentBindings
       = new HashMap<Key<?>, BindingImpl<?>>();
+  final Map<Object, Void> outstandingInjections
+      = new IdentityHashMap<Object, Void>();
 
   ErrorHandler errorHandler = new InvalidErrorHandler();
 
@@ -103,6 +98,38 @@
     this.parentInjector = parentInjector;
   }
 
+  void validateOustandingInjections() {
+    for (Object toInject : outstandingInjections.keySet()) {
+      injectors.get(toInject.getClass());
+    }
+  }
+
+  /**
+   * Performs creation-time injections on all objects that require it. Whenever
+   * fulfilling an injection depends on another object that requires injection,
+   * we use {@link InternalContext#ensureMemberInjected} to inject that member
+   * first.
+   *
+   * <p>If the two objects are codependent (directly or transitively), ordering
+   * of injection is arbitrary.
+   */
+  void fulfillOutstandingInjections() {
+    callInContext(new ContextualCallable<Void>() {
+      public Void call(InternalContext context) {
+        // loop over a defensive copy, since ensureMemberInjected() mutates the
+        // outstandingInjections set
+        for (Object toInject : new ArrayList<Object>(outstandingInjections.keySet())) {
+          context.ensureMemberInjected(toInject);
+        }
+        return null;
+      }
+    });
+
+    if (!outstandingInjections.isEmpty()) {
+      throw new IllegalStateException("failed to satisfy " + outstandingInjections);
+    }
+  }
+
   /**
    * Indexes bindings by type.
    */
diff --git a/src/com/google/inject/InternalContext.java b/src/com/google/inject/InternalContext.java
index 3f94320..3b0def1 100644
--- a/src/com/google/inject/InternalContext.java
+++ b/src/com/google/inject/InternalContext.java
@@ -18,9 +18,6 @@
 
 import java.util.HashMap;
 import java.util.Map;
-import java.util.List;
-import java.util.ArrayList;
-import java.util.Collections;
 
 /**
  * Internal context. Used to coordinate injections and support circular
@@ -68,4 +65,16 @@
   public void setInjectionPoint(InjectionPoint injectionPoint) {
     this.injectionPoint = injectionPoint;
   }
+
+  /**
+   * Ensures that an object requiring injection at Injector-creation time has
+   * been injected before its use.
+   */
+  public void ensureMemberInjected(Object toInject) {
+    if (!injector.outstandingInjections.keySet().remove(toInject)) {
+      return;
+    }
+
+    injector.injectMembers(toInject);
+  }
 }
diff --git a/src/com/google/inject/InternalFactoryToProviderAdapter.java b/src/com/google/inject/InternalFactoryToProviderAdapter.java
index 8037910..59ae441 100644
--- a/src/com/google/inject/InternalFactoryToProviderAdapter.java
+++ b/src/com/google/inject/InternalFactoryToProviderAdapter.java
@@ -36,8 +36,9 @@
     this.provider = Objects.nonNull(provider, "provider");
     this.source = Objects.nonNull(source, "source");
   }
-  
+
   public T get(InternalContext context, InjectionPoint<?> injectionPoint) {
+    context.ensureMemberInjected(provider);
     T provided = provider.get();
     return injectionPoint.checkForNull(provided, source);
   }