Fix for https://github.com/google/guice/issues/884 -- don't let
half-initialized objects leak out to ProvisionListeners.  Throw
ProvisionException if there's any new errors during provision.
-------------
Created by MOE: http://code.google.com/p/moe-java
MOE_MIGRATED_REVID=81455693
diff --git a/core/src/com/google/inject/internal/ProvisionListenerStackCallback.java b/core/src/com/google/inject/internal/ProvisionListenerStackCallback.java
index ad292a6..f72da25 100644
--- a/core/src/com/google/inject/internal/ProvisionListenerStackCallback.java
+++ b/core/src/com/google/inject/internal/ProvisionListenerStackCallback.java
@@ -92,6 +92,7 @@
   private class Provision extends ProvisionListener.ProvisionInvocation<T> {
 
     final Errors errors;
+    final int numErrorsBefore;
     final InternalContext context;
     final ProvisionCallback<T> callable;
     int index = -1;
@@ -103,6 +104,7 @@
       this.callable = callable;
       this.context = context;
       this.errors = errors;
+      this.numErrorsBefore = errors.size();
     }
 
     @Override
@@ -111,6 +113,9 @@
       if (index == listeners.length) {
         try {
           result = callable.call();
+          // Make sure we don't return the provisioned object if there were any errors
+          // injecting its field/method dependencies.
+          errors.throwIfNewErrors(numErrorsBefore);
         } catch(ErrorsException ee) {
           exceptionDuringProvision = ee;
           throw new ProvisionException(errors.merge(ee.getErrors()).getMessages());
@@ -118,7 +123,6 @@
       } else if (index < listeners.length) {
         int currentIdx = index;
         try {
-
           listeners[index].onProvision(this);
         } catch(RuntimeException re) {
           erredListener = listeners[currentIdx];
diff --git a/core/test/com/google/inject/ProvisionListenerTest.java b/core/test/com/google/inject/ProvisionListenerTest.java
index f70ada5..6e1be84 100644
--- a/core/test/com/google/inject/ProvisionListenerTest.java
+++ b/core/test/com/google/inject/ProvisionListenerTest.java
@@ -38,6 +38,7 @@
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
 
 /**
  * Tests for {@link Binder#bindListener(Matcher, ProvisionListener...)}
@@ -129,6 +130,66 @@
     }
   }
   
+  public void testExceptionInFieldProvision() throws Exception {
+    final CountAndCaptureExceptionListener listener = new CountAndCaptureExceptionListener();
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      @Override protected void configure() {
+        bindListener(new AbstractMatcher<Binding<?>>() {
+          @Override public boolean matches(Binding<?> binding) {
+            return binding.getKey().getRawType().equals(DependsOnFooBombInField.class);
+          }
+        }, listener);
+      }
+    });
+    assertEquals(0, listener.beforeProvision);
+    String expectedMsg = null;
+    try {
+      injector.getInstance(DependsOnFooBombInField.class);
+      fail();
+    } catch (ProvisionException expected) {
+      assertEquals(1, expected.getErrorMessages().size());
+      expectedMsg = expected.getMessage();
+      assertContains(listener.capture.get().getMessage(),
+          "1) Error injecting constructor, java.lang.RuntimeException: Retry, Abort, Fail",
+          " at " + FooBomb.class.getName(),
+          " while locating " + FooBomb.class.getName(),
+          " while locating " + DependsOnFooBombInField.class.getName());
+    }
+    assertEquals(1, listener.beforeProvision);
+    assertEquals(expectedMsg, listener.capture.get().getMessage());
+    assertEquals(0, listener.afterProvision);
+  }
+  
+  public void testExceptionInCxtorProvision() throws Exception {
+    final CountAndCaptureExceptionListener listener = new CountAndCaptureExceptionListener();
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      @Override protected void configure() {
+        bindListener(new AbstractMatcher<Binding<?>>() {
+          @Override public boolean matches(Binding<?> binding) {
+            return binding.getKey().getRawType().equals(DependsOnFooBombInCxtor.class);
+          }
+        }, listener);
+      }
+    });
+    assertEquals(0, listener.beforeProvision);
+    String expectedMsg = null;
+    try {
+      injector.getInstance(DependsOnFooBombInCxtor.class);
+      fail();
+    } catch (ProvisionException expected) {
+      assertEquals(1, expected.getErrorMessages().size());
+      expectedMsg = expected.getMessage();
+      assertContains(listener.capture.get().getMessage(),
+          "1) Error injecting constructor, java.lang.RuntimeException: Retry, Abort, Fail",
+          " at " + FooBomb.class.getName(),
+          " while locating " + FooBomb.class.getName(),
+          " while locating " + DependsOnFooBombInCxtor.class.getName());
+    }
+    assertEquals(1, listener.beforeProvision);
+    assertEquals(expectedMsg, listener.capture.get().getMessage());
+    assertEquals(0, listener.afterProvision);
+  }
+
   public void testListenerCallsProvisionTwice() {
     Injector injector = Guice.createInjector(new AbstractModule() {
       @Override
@@ -374,6 +435,14 @@
     }
   }
   
+  static class DependsOnFooBombInField {
+    @Inject FooBomb fooBomb;
+  }
+  
+  static class DependsOnFooBombInCxtor {
+    @Inject DependsOnFooBombInCxtor(FooBomb fooBomb) {}
+  }
+  
   private static class Counter implements ProvisionListener {
     int count = 0;
     public <T> void onProvision(ProvisionInvocation<T> provision) {
@@ -381,6 +450,22 @@
     }
   }
   
+  private static class CountAndCaptureExceptionListener implements ProvisionListener {
+    int beforeProvision = 0;
+    int afterProvision = 0;
+    AtomicReference<RuntimeException> capture = new AtomicReference<RuntimeException>();
+    public <T> void onProvision(ProvisionInvocation<T> provision) {
+      beforeProvision++;
+      try {
+        provision.provision();
+      } catch (RuntimeException re) {
+        capture.set(re);
+        throw re;
+      }
+      afterProvision++;
+    }
+  }
+  
   private static class Capturer implements ProvisionListener {
     List<Key> keys = Lists.newArrayList(); 
     public <T> void onProvision(ProvisionInvocation<T> provision) {