Renaming ExternalContext to InjectionPoint, and changing how it's used in ProvisionException. Also changing LoggerProvider to LoggerFactory.

git-svn-id: https://google-guice.googlecode.com/svn/trunk@378 d779f126-a31b-0410-b53b-1d3aecad763e
diff --git a/src/com/google/inject/BinderImpl.java b/src/com/google/inject/BinderImpl.java
index 0ba96ee..6e58b43 100644
--- a/src/com/google/inject/BinderImpl.java
+++ b/src/com/google/inject/BinderImpl.java
@@ -16,7 +16,6 @@
 
 package com.google.inject;
 
-import static com.google.inject.Nullability.NULLABLE;
 import com.google.inject.InjectorImpl.SingleMemberInjector;
 import static com.google.inject.Scopes.SINGLETON;
 import com.google.inject.internal.Annotations;
@@ -94,7 +93,7 @@
     bindScope(Singleton.class, SINGLETON);
 
     bind(Logger.class, SourceProviders.UNKNOWN_SOURCE)
-        .toProvider(new LoggerProvider());
+        .toInternalFactory(new LoggerFactory());
     bind(Stage.class, SourceProviders.UNKNOWN_SOURCE).toInstance(stage);
 
     this.proxyFactoryBuilder = new ProxyFactoryBuilder();
@@ -674,14 +673,19 @@
     }
 
     public Void call(InternalContext context) {
-      context.pushExternalContext(ExternalContext.newInstance(
-          null, NULLABLE, key, context.getInjectorImpl()));
+      InjectionPoint<?> injectionPoint
+          = InjectionPoint.newInstance(key, context.getInjectorImpl());
+      context.setInjectionPoint(injectionPoint);
       try {
-        factory.get(context);
+        factory.get(context, injectionPoint);
         return null;
       }
+      catch(ProvisionException provisionException) {
+        provisionException.addContext(injectionPoint);
+        throw provisionException;
+      }
       finally {
-        context.popExternalContext();
+        context.setInjectionPoint(null);
       }
     }
   }
@@ -744,12 +748,10 @@
     return (Provider<T>) ILLEGAL_PROVIDER;
   }
 
-  static class LoggerProvider implements Provider<Logger> {
+  static class LoggerFactory implements InternalFactory<Logger> {
 
-    @Inject Injector injector;
-    public Logger get() {
-      InternalContext context = ((InjectorImpl) injector).getContext();
-      Member member = context.getExternalContext().getMember();
+    public Logger get(InternalContext context, InjectionPoint<?> injectionPoint) {
+      Member member = injectionPoint.getMember();
       return member == null
           ? Logger.getAnonymousLogger()
           : Logger.getLogger(member.getDeclaringClass().getName());
diff --git a/src/com/google/inject/BindingBuilderImpl.java b/src/com/google/inject/BindingBuilderImpl.java
index 8b0fb6b..dfe4942 100644
--- a/src/com/google/inject/BindingBuilderImpl.java
+++ b/src/com/google/inject/BindingBuilderImpl.java
@@ -166,6 +166,17 @@
     return this;
   }
 
+  public BindingBuilderImpl<T> toInternalFactory(InternalFactory<T> internalFactory) {
+    ensureImplementationIsNotSet();
+    this.factory = internalFactory;
+    this.providerInstance = new Provider<T>() {
+      public T get() {
+        throw new UnsupportedOperationException("?");
+      }
+    };
+    return this;
+  }
+
   public BindingBuilderImpl<T> toProvider(
       Class<? extends Provider<? extends T>> providerType) {
     return toProvider(Key.get(providerType));
@@ -411,8 +422,8 @@
       });
     }
 
-    public T get(InternalContext context) {
-      return targetFactory.get(context);
+    public T get(InternalContext context, InjectionPoint<?> injectionPoint) {
+      return targetFactory.get(context, injectionPoint);
     }
 
     public String toString() {
diff --git a/src/com/google/inject/BoundProviderFactory.java b/src/com/google/inject/BoundProviderFactory.java
index 8cb9a4c..f0708e0 100644
--- a/src/com/google/inject/BoundProviderFactory.java
+++ b/src/com/google/inject/BoundProviderFactory.java
@@ -56,15 +56,14 @@
     return providerKey.toString();
   }
 
-  public T get(InternalContext context) {
-    Provider<? extends T> provider = providerFactory.get(context);
+  public T get(InternalContext context, InjectionPoint<?> injectionPoint) {
+    Provider<? extends T> provider = providerFactory.get(context, injectionPoint);
     try {
-      return context.checkForNull(provider.get(), source);
+      return injectionPoint.checkForNull(provider.get(), source);
     } catch(ProvisionException e) {
       throw e;
     } catch(RuntimeException e) {
-      throw new ProvisionException(context.getExternalContextStack(),
-          e, ErrorMessages.ERROR_IN_PROVIDER);
+      throw new ProvisionException(e, ErrorMessages.ERROR_IN_PROVIDER);
     }
   }
 }
diff --git a/src/com/google/inject/ClassBindingImpl.java b/src/com/google/inject/ClassBindingImpl.java
index cb727c1..edf776f 100644
--- a/src/com/google/inject/ClassBindingImpl.java
+++ b/src/com/google/inject/ClassBindingImpl.java
@@ -53,7 +53,7 @@
     if (constructor.parameterInjectors != null) {
       for (SingleParameterInjector<?> parameterInjector
           : constructor.parameterInjectors) {
-        injectors.add(parameterInjector.externalContext);
+        injectors.add(parameterInjector.injectionPoint);
       }
     }
     return injectors;
diff --git a/src/com/google/inject/ConstantFactory.java b/src/com/google/inject/ConstantFactory.java
index 31a1b95..6d743fa 100644
--- a/src/com/google/inject/ConstantFactory.java
+++ b/src/com/google/inject/ConstantFactory.java
@@ -16,7 +16,6 @@
 
 package com.google.inject;
 
-import com.google.inject.internal.Objects;
 import com.google.inject.internal.ToStringBuilder;
 
 /**
@@ -30,7 +29,7 @@
     this.value = value;
   }
 
-  public T get(InternalContext context) {
+  public T get(InternalContext context, InjectionPoint injectionPoint) {
     return value;
   }
 
diff --git a/src/com/google/inject/ConstructorInjector.java b/src/com/google/inject/ConstructorInjector.java
index 35c0757..5375154 100644
--- a/src/com/google/inject/ConstructorInjector.java
+++ b/src/com/google/inject/ConstructorInjector.java
@@ -159,8 +159,8 @@
     }
     catch (InvocationTargetException e) {
       Throwable cause = e.getCause() != null ? e.getCause() : e;
-      throw new ProvisionException(context.getExternalContextStack(),
-          cause, ErrorMessages.ERROR_INJECTING_CONSTRUCTOR);
+      throw new ProvisionException(cause,
+          ErrorMessages.ERROR_INJECTING_CONSTRUCTOR);
     }
     finally {
       constructionContext.removeCurrentReference();
diff --git a/src/com/google/inject/ExternalContext.java b/src/com/google/inject/ExternalContext.java
deleted file mode 100644
index c5617d3..0000000
--- a/src/com/google/inject/ExternalContext.java
+++ /dev/null
@@ -1,93 +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 java.lang.reflect.Member;
-import java.util.LinkedHashMap;
-import com.google.inject.internal.Objects;
-import com.google.inject.spi.Dependency;
-
-/**
- * An immutable snapshot of the current context which is safe to expose to
- * client code.
- *
- * @author crazybob@google.com (Bob Lee)
- */
-class ExternalContext<T> implements Dependency<T> {
-
-  final Member member;
-  final Key<T> key;
-  final InjectorImpl injector;
-  final int parameterIndex;
-  final Nullability nullability;
-
-  public ExternalContext(Member member, int paramterIndex,
-      Nullability nullability, Key<T> key, InjectorImpl injector) {
-    this.member = member;
-    this.parameterIndex = paramterIndex;
-    this.nullability = Objects.nonNull(nullability, "nullability");
-    this.key = key;
-    this.injector = injector;
-  }
-
-  public Key<T> getKey() {
-    return this.key;
-  }
-
-  public Binding<T> getBinding() {
-    return injector.getBinding(key);
-  }
-
-  public boolean allowsNull() {
-    return getNullability() == Nullability.NULLABLE;
-  }
-
-  public Injector getInjector() {
-    return injector;
-  }
-
-  public Member getMember() {
-    return member;
-  }
-
-  public int getParameterIndex() {
-    return parameterIndex;
-  }
-
-  public Nullability getNullability() {
-    return nullability;
-  }
-
-  public String toString() {
-    return "Context" + new LinkedHashMap<String, Object>() {{
-      put("member", member);
-      put("key", getKey());
-      put("injector", injector);
-    }}.toString();
-  }
-
-  static <T> ExternalContext<T> newInstance(Member member,
-      Nullability nullability, Key<T> key, InjectorImpl injector) {
-    return new ExternalContext<T>(member, -1, nullability, key, injector);
-  }
-
-  static <T> ExternalContext<T> newInstance(Member member, int parameterIndex,
-      Nullability nullability, Key<T> key, InjectorImpl injector) {
-    return new ExternalContext<T>(member, parameterIndex, nullability, key,
-        injector);
-  }
-}
diff --git a/src/com/google/inject/InjectionPoint.java b/src/com/google/inject/InjectionPoint.java
new file mode 100644
index 0000000..378bfa4
--- /dev/null
+++ b/src/com/google/inject/InjectionPoint.java
@@ -0,0 +1,119 @@
+/**
+ * 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 java.lang.reflect.Member;
+import java.lang.reflect.Field;
+import java.util.LinkedHashMap;
+import com.google.inject.internal.Objects;
+import com.google.inject.spi.Dependency;
+
+/**
+ * An immutable snapshot of where the value is to be injected.
+ *
+ * @author crazybob@google.com (Bob Lee)
+ */
+class InjectionPoint<T> implements Dependency<T> {
+
+  final Member member;
+  final Key<T> key;
+  final InjectorImpl injector;
+  final int parameterIndex;
+  final Nullability nullability;
+
+  private InjectionPoint(Member member, int paramterIndex,
+      Nullability nullability, Key<T> key, InjectorImpl injector) {
+    this.member = member;
+    this.parameterIndex = paramterIndex;
+    this.nullability = Objects.nonNull(nullability, "nullability");
+    this.key = key;
+    this.injector = injector;
+  }
+
+  public Key<T> getKey() {
+    return this.key;
+  }
+
+  public Binding<T> getBinding() {
+    return injector.getBinding(key);
+  }
+
+  public boolean allowsNull() {
+    return getNullability() == Nullability.NULLABLE;
+  }
+
+  public Injector getInjector() {
+    return injector;
+  }
+
+  public Member getMember() {
+    return member;
+  }
+
+  public int getParameterIndex() {
+    return parameterIndex;
+  }
+
+  public Nullability getNullability() {
+    return nullability;
+  }
+
+  public String toString() {
+    return "Context" + new LinkedHashMap<String, Object>() {{
+      put("member", member);
+      put("key", getKey());
+      put("injector", injector);
+    }}.toString();
+  }
+
+  <T> T checkForNull(T value, Object source) {
+    if (value != null
+        || getNullability() == Nullability.NULLABLE
+        || allowNullsBadBadBad()) {
+      return value;
+    }
+
+    String message = getMember() != null
+        ? String.format(ErrorMessages.CANNOT_INJECT_NULL_INTO_MEMBER, source,
+            getMember())
+        : String.format(ErrorMessages.CANNOT_INJECT_NULL, source);
+
+    throw new ProvisionException(new NullPointerException(message),
+        String.format(ErrorMessages.CANNOT_INJECT_NULL, source));
+  }
+
+  // TODO(kevinb): gee, ya think we might want to remove this?
+  private static boolean allowNullsBadBadBad() {
+    return "I'm a bad hack".equals(
+          System.getProperty("guice.allow.nulls.bad.bad.bad"));
+  }
+
+  static <T> InjectionPoint<T> newInstance(Field field,
+      Nullability nullability, Key<T> key, InjectorImpl injector) {
+    return new InjectionPoint<T>(field, -1, nullability, key, injector);
+  }
+
+  static <T> InjectionPoint<T> newInstance(Key<T> key, InjectorImpl injector) {
+    return new InjectionPoint<T>(null, -1, Nullability.NULLABLE, key, injector);
+  }
+
+  static <T> InjectionPoint<T> newInstance(Member member, int parameterIndex,
+      Nullability nullability, Key<T> key, InjectorImpl injector) {
+    return new InjectionPoint<T>(member, parameterIndex, nullability, key,
+        injector);
+  }
+}
diff --git a/src/com/google/inject/InjectorImpl.java b/src/com/google/inject/InjectorImpl.java
index 52cc5f9..a9ff505 100644
--- a/src/com/google/inject/InjectorImpl.java
+++ b/src/com/google/inject/InjectorImpl.java
@@ -16,16 +16,16 @@
 
 package com.google.inject;
 
+import com.google.inject.internal.Classes;
 import com.google.inject.internal.GuiceFastClass;
 import com.google.inject.internal.ReferenceCache;
 import com.google.inject.internal.StackTraceElements;
 import com.google.inject.internal.ToStringBuilder;
-import com.google.inject.internal.Classes;
-import com.google.inject.spi.SourceProviders;
-import com.google.inject.spi.ProviderBinding;
 import com.google.inject.spi.BindingVisitor;
 import com.google.inject.spi.ConvertedConstantBinding;
 import com.google.inject.spi.Dependency;
+import com.google.inject.spi.ProviderBinding;
+import com.google.inject.spi.SourceProviders;
 import com.google.inject.util.Providers;
 import java.lang.annotation.Annotation;
 import java.lang.reflect.AnnotatedElement;
@@ -39,12 +39,12 @@
 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.Collection;
 import java.util.concurrent.Callable;
 import net.sf.cglib.reflect.FastClass;
 import net.sf.cglib.reflect.FastMethod;
@@ -252,7 +252,8 @@
         Binding<T> providedBinding) {
       final Provider<T> provider = providedBinding.getProvider();
       return new InternalFactory<Provider<T>>() {
-        public Provider<T> get(InternalContext context) {
+        public Provider<T> get(InternalContext context,
+            InjectionPoint injectionPoint) {
           return provider;
         }
       };
@@ -506,12 +507,12 @@
     }
 
     @SuppressWarnings("unchecked")
-    public T get(InternalContext context) {
+    public T get(InternalContext context, InjectionPoint<?> injectionPoint) {
       // This may not actually be safe because it could return a super type
       // of T (if that's all the client needs), but it should be OK in
       // practice thanks to the wonders of erasure.
       return (T) constructorInjector.construct(
-          context, context.getExpectedType());
+          context, injectionPoint.getKey().getRawType());
     }
   }
 
@@ -547,8 +548,9 @@
     }
 
     InternalFactory<T> internalFactory = new InternalFactory<T>() {
-      public T get(InternalContext context) {
-        Provider<?> provider = providerBinding.internalFactory.get(context);
+      public T get(InternalContext context, InjectionPoint injectionPoint) {
+        Provider<?> provider
+            = providerBinding.internalFactory.get(context, injectionPoint);
         Object o = provider.get();
         try {
           return type.cast(o);
@@ -604,8 +606,8 @@
     }
 
     InternalFactory<T> internalFactory = new InternalFactory<T>() {
-      public T get(InternalContext context) {
-        return targetBinding.internalFactory.get(context);
+      public T get(InternalContext context, InjectionPoint<?> injectionPoint) {
+        return targetBinding.internalFactory.get(context, injectionPoint);
       }
     };
 
@@ -786,7 +788,7 @@
 
     final Field field;
     final InternalFactory<?> factory;
-    final ExternalContext<?> externalContext;
+    final InjectionPoint<?> injectionPoint;
 
     public SingleFieldInjector(final InjectorImpl injector, Field field)
         throws MissingDependencyException {
@@ -809,18 +811,18 @@
         throw new MissingDependencyException(key, field);
       }
 
-      this.externalContext = ExternalContext.newInstance(field,
+      this.injectionPoint = InjectionPoint.newInstance(field,
           Nullability.forAnnotations(field.getAnnotations()), key, injector);
     }
 
     public Collection<Dependency<?>> getDependencies() {
-      return Collections.<Dependency<?>>singleton(externalContext);
+      return Collections.<Dependency<?>>singleton(injectionPoint);
     }
 
     public void inject(InternalContext context, Object o) {
-      context.pushExternalContext(externalContext);
+      context.setInjectionPoint(injectionPoint);
       try {
-        Object value = factory.get(context);
+        Object value = factory.get(context, injectionPoint);
         field.set(o, value);
       }
       catch (IllegalAccessException e) {
@@ -830,14 +832,15 @@
         throw e;
       }
       catch (ProvisionException provisionException) {
+        provisionException.addContext(injectionPoint);
         throw provisionException;
       }
       catch (RuntimeException runtimeException) {
-        throw new ProvisionException(context.getExternalContextStack(),
-            runtimeException, ErrorMessages.ERROR_INJECTING_FIELD);
+        throw new ProvisionException(runtimeException,
+            ErrorMessages.ERROR_INJECTING_FIELD);
       }
       finally {
-        context.popExternalContext();
+        context.setInjectionPoint(null);
       }
     }
   }
@@ -886,9 +889,9 @@
       throw new MissingDependencyException(key, member);
     }
 
-    ExternalContext<T> externalContext = ExternalContext.newInstance(
+    InjectionPoint<T> injectionPoint = InjectionPoint.newInstance(
         member, index, Nullability.forAnnotations(annotations), key, this);
-    return new SingleParameterInjector<T>(externalContext, factory);
+    return new SingleParameterInjector<T>(injectionPoint, factory);
   }
 
   static class SingleMethodInjector implements SingleMemberInjector {
@@ -940,15 +943,15 @@
       }
       catch (InvocationTargetException e) {
         Throwable cause = e.getCause() != null ? e.getCause() : e;
-        throw new ProvisionException(context.getExternalContextStack(),
-            cause, ErrorMessages.ERROR_INJECTING_METHOD);
+        throw new ProvisionException(cause,
+            ErrorMessages.ERROR_INJECTING_METHOD);
       }
     }
 
     public Collection<Dependency<?>> getDependencies() {
       List<Dependency<?>> dependencies = new ArrayList<Dependency<?>>();
       for (SingleParameterInjector<?> parameterInjector : parameterInjectors) {
-        dependencies.add(parameterInjector.externalContext);
+        dependencies.add(parameterInjector.injectionPoint);
       }
       return Collections.unmodifiableList(dependencies);
     }
@@ -1003,32 +1006,33 @@
 
   static class SingleParameterInjector<T> {
 
-    final ExternalContext<T> externalContext;
+    final InjectionPoint<T> injectionPoint;
     final InternalFactory<? extends T> factory;
 
-    public SingleParameterInjector(ExternalContext<T> externalContext,
+    public SingleParameterInjector(InjectionPoint<T> injectionPoint,
         InternalFactory<? extends T> factory) {
-      this.externalContext = externalContext;
+      this.injectionPoint = injectionPoint;
       this.factory = factory;
     }
 
     T inject(InternalContext context) {
-      context.pushExternalContext(externalContext);
+      context.setInjectionPoint(injectionPoint);
       try {
-        return factory.get(context);
+        return factory.get(context, injectionPoint);
       }
       catch (ConfigurationException e) {
         throw e;
       }
-      catch (ProvisionException e) {
-        throw e;
+      catch (ProvisionException provisionException) {
+        provisionException.addContext(injectionPoint);
+        throw provisionException;
       }
       catch (RuntimeException runtimeException) {
-        throw new ProvisionException(context.getExternalContextStack(),
-            runtimeException, ErrorMessages.ERROR_INJECTING_METHOD);
+        throw new ProvisionException(runtimeException,
+            ErrorMessages.ERROR_INJECTING_METHOD);
       }
       finally {
-        context.popExternalContext();
+        context.setInjectionPoint(injectionPoint);
       }
     }
   }
@@ -1086,13 +1090,18 @@
       public T get() {
         return callInContext(new ContextualCallable<T>() {
           public T call(InternalContext context) {
-            context.pushExternalContext(ExternalContext.newInstance(
-                null, Nullability.NOT_NULLABLE, key, InjectorImpl.this));
+            InjectionPoint<T> injectionPoint
+                = InjectionPoint.newInstance(key, InjectorImpl.this);
+            context.setInjectionPoint(injectionPoint);
             try {
-              return factory.get(context);
+              return factory.get(context, injectionPoint);
+            }
+            catch(ProvisionException provisionException) {
+              provisionException.addContext(injectionPoint);
+              throw provisionException;
             }
             finally {
-              context.popExternalContext();
+              context.setInjectionPoint(null);
             }
           }
         });
@@ -1131,14 +1140,6 @@
   };
 
   /**
-   * Gets context for the current thread. Returns null if no context has been
-   * set up.
-   */
-  InternalContext getContext() {
-    return localContext.get()[0];
-  }
-
-  /**
    * Looks up thread local context. Creates (and removes) a new context if
    * necessary.
    */
diff --git a/src/com/google/inject/InternalContext.java b/src/com/google/inject/InternalContext.java
index 2fae544..3f94320 100644
--- a/src/com/google/inject/InternalContext.java
+++ b/src/com/google/inject/InternalContext.java
@@ -32,8 +32,7 @@
 
   final InjectorImpl injector;
   Map<Object, ConstructionContext<?>> constructionContexts;
-  final List<ExternalContext<?>> externalContextStack =
-      new ArrayList<ExternalContext<?>>(5);
+  InjectionPoint injectionPoint;
 
   InternalContext(InjectorImpl injector) {
     this.injector = injector;
@@ -62,60 +61,11 @@
     }
   }
 
-  @SuppressWarnings("unchecked")
-  <T> ExternalContext<T> getExternalContext() {
-    if (externalContextStack.isEmpty()) {
-      throw new IllegalStateException("No external context on stack");
-    }
-    return (ExternalContext<T>) externalContextStack.get(
-        externalContextStack.size() - 1);
+  public InjectionPoint getInjectionPoint() {
+    return injectionPoint;
   }
 
-  public List<ExternalContext<?>> getExternalContextStack() {
-    return Collections.unmodifiableList(
-        new ArrayList<ExternalContext<?>>(externalContextStack));
-  }
-
-  Class<?> getExpectedType() {
-    return getExternalContext().getKey().getRawType();
-  }
-
-  /**
-   * Push a new external context onto the stack. Each call to {@code #push()}
-   * requires a matching call to {@code #pop()} so that the contexts are
-   * balanced.
-   */
-  void pushExternalContext(ExternalContext<?> externalContext) {
-    externalContextStack.add(externalContext);
-  }
-
-  /**
-   * Pop the external context off the stack.
-   */
-  void popExternalContext() {
-    externalContextStack.remove(externalContextStack.size() - 1);
-  }
-
-  <T> T checkForNull(T value, Object source) {
-    if (value != null
-        || getExternalContext().getNullability() == Nullability.NULLABLE
-        || allowNullsBadBadBad()) {
-      return value;
-    }
-
-    String message = getExternalContext().getMember() != null
-        ? String.format(ErrorMessages.CANNOT_INJECT_NULL_INTO_MEMBER, source,
-            getExternalContext().getMember())
-        : String.format(ErrorMessages.CANNOT_INJECT_NULL, source);
-
-    throw new ProvisionException(getExternalContextStack(),
-        new NullPointerException(message),
-        String.format(ErrorMessages.CANNOT_INJECT_NULL, source));
-  }
-
-  // TODO(kevinb): gee, ya think we might want to remove this?
-  private static boolean allowNullsBadBadBad() {
-    return "I'm a bad hack".equals(
-          System.getProperty("guice.allow.nulls.bad.bad.bad"));
+  public void setInjectionPoint(InjectionPoint injectionPoint) {
+    this.injectionPoint = injectionPoint;
   }
 }
diff --git a/src/com/google/inject/InternalFactory.java b/src/com/google/inject/InternalFactory.java
index ffb58ba..8af2bfb 100644
--- a/src/com/google/inject/InternalFactory.java
+++ b/src/com/google/inject/InternalFactory.java
@@ -29,5 +29,5 @@
    * @param context of this injection
    * @return instance to be injected; never null
    */
-  T get(InternalContext context);
+  T get(InternalContext context, InjectionPoint<?> injectionPoint);
 }
diff --git a/src/com/google/inject/InternalFactoryToProviderAdapter.java b/src/com/google/inject/InternalFactoryToProviderAdapter.java
index 67fb9d9..8037910 100644
--- a/src/com/google/inject/InternalFactoryToProviderAdapter.java
+++ b/src/com/google/inject/InternalFactoryToProviderAdapter.java
@@ -37,9 +37,9 @@
     this.source = Objects.nonNull(source, "source");
   }
   
-  public T get(InternalContext context) {
+  public T get(InternalContext context, InjectionPoint<?> injectionPoint) {
     T provided = provider.get();
-    return context.checkForNull(provided, source);
+    return injectionPoint.checkForNull(provided, source);
   }
 
   public String toString() {
diff --git a/src/com/google/inject/InvalidBindingImpl.java b/src/com/google/inject/InvalidBindingImpl.java
index ffa5278..a833143 100644
--- a/src/com/google/inject/InvalidBindingImpl.java
+++ b/src/com/google/inject/InvalidBindingImpl.java
@@ -22,7 +22,7 @@
 
   InvalidBindingImpl(InjectorImpl injector, Key<T> key, Object source) {
     super(injector, key, source, new InternalFactory<T>() {
-      public T get(InternalContext context) {
+      public T get(InternalContext context, InjectionPoint<?> injectionPoint) {
         throw new AssertionError();
       }
     }, Scopes.NO_SCOPE);
diff --git a/src/com/google/inject/ProviderToInternalFactoryAdapter.java b/src/com/google/inject/ProviderToInternalFactoryAdapter.java
index 08b5484..9229b4f 100644
--- a/src/com/google/inject/ProviderToInternalFactoryAdapter.java
+++ b/src/com/google/inject/ProviderToInternalFactoryAdapter.java
@@ -34,7 +34,8 @@
   public T get() {
     return injector.callInContext(new ContextualCallable<T>() {
       public T call(InternalContext context) {
-        return internalFactory.get(context);
+        InjectionPoint injectionPoint = context.getInjectionPoint();
+        return internalFactory.get(context, injectionPoint);
       }
     });
   }
diff --git a/src/com/google/inject/ProvisionException.java b/src/com/google/inject/ProvisionException.java
index 87e75f1..26a044d 100644
--- a/src/com/google/inject/ProvisionException.java
+++ b/src/com/google/inject/ProvisionException.java
@@ -35,14 +35,20 @@
 public class ProvisionException extends RuntimeException {
 
   private final String errorMessage;
-  private final List<ExternalContext<?>> contexts;
+  private final List<InjectionPoint<?>> contexts
+      = new ArrayList<InjectionPoint<?>>(5);
 
-  ProvisionException(List<ExternalContext<?>> externalContextStack,
-      Throwable cause, String errorMessage) {
+  ProvisionException(Throwable cause, String errorMessage) {
     super(errorMessage, cause);
     this.errorMessage = errorMessage;
-    this.contexts = Collections.unmodifiableList(
-        new ArrayList<ExternalContext<?>>(externalContextStack));
+  }
+
+  /**
+   * Add an injection point that was being resolved when this exception
+   * occurred.
+   */
+  public void addContext(InjectionPoint<?> injectionPoint) {
+    this.contexts.add(injectionPoint);
   }
 
   @Override
@@ -51,9 +57,9 @@
     result.append(errorMessage);
 
     for (int i = contexts.size() - 1; i >= 0; i--) {
-      ExternalContext externalContext = contexts.get(i);
+      InjectionPoint injectionPoint = contexts.get(i);
       result.append(String.format("%n"));
-      result.append(contextToSnippet(externalContext));
+      result.append(contextToSnippet(injectionPoint));
     }
 
     return result.toString();
@@ -63,10 +69,10 @@
    * Returns a snippet to include in the stacktrace message that describes the
    * specified context.
    */
-  private String contextToSnippet(ExternalContext externalContext) {
-    Key<?> key = externalContext.getKey();
+  private String contextToSnippet(InjectionPoint injectionPoint) {
+    Key<?> key = injectionPoint.getKey();
     Object keyDescription = ErrorMessages.convert(key);
-    Member member = externalContext.getMember();
+    Member member = injectionPoint.getMember();
 
     if (member instanceof Field) {
       return String.format(ERROR_WHILE_LOCATING_FIELD,
@@ -74,7 +80,7 @@
 
     } else if (member instanceof Method || member instanceof Constructor) {
       return String.format(ERROR_WHILE_LOCATING_PARAMETER,
-          keyDescription, externalContext.getParameterIndex(),
+          keyDescription, injectionPoint.getParameterIndex(),
           StackTraceElements.forMember(member));
 
     } else {
diff --git a/test/com/google/inject/AllTests.java b/test/com/google/inject/AllTests.java
index 988b156..ba782a1 100644
--- a/test/com/google/inject/AllTests.java
+++ b/test/com/google/inject/AllTests.java
@@ -55,6 +55,7 @@
     suite.addTestSuite(TypeLiteralTest.class);
     suite.addTestSuite(BoundInstanceInjectionTest.class);
     suite.addTestSuite(BindingAnnotationTest.class);
+    suite.addTestSuite(LoggerInjectionTest.class);
 
     suite.addTestSuite(MatcherTest.class);
 
diff --git a/test/com/google/inject/LoggerInjectionTest.java b/test/com/google/inject/LoggerInjectionTest.java
new file mode 100644
index 0000000..79d6e45
--- /dev/null
+++ b/test/com/google/inject/LoggerInjectionTest.java
@@ -0,0 +1,26 @@
+package com.google.inject;
+
+import junit.framework.TestCase;
+import java.util.logging.Logger;
+
+/**
+ * Test built-in injection of loggers.
+ *
+ * @author jessewilson
+ */
+public class LoggerInjectionTest extends TestCase {
+
+  @Inject Logger logger;
+
+  public void testLoggerWithMember() {
+    Injector injector = Guice.createInjector();
+    injector.injectMembers(this);
+    assertEquals("com.google.inject.LoggerInjectionTest", logger.getName());
+  }
+  
+  public void testLoggerWithoutMember() {
+    Injector injector = Guice.createInjector();
+    Logger loggerWithoutMember = injector.getInstance(Logger.class);
+    assertNull(loggerWithoutMember.getName());
+  }
+}