Fix issue 779 -- deduplicate listeners & interceptors.  Using a slightly modified patch from Tavian Barnes, thanks Tavian!
-------------
Created by MOE: http://code.google.com/p/moe-java
MOE_MIGRATED_REVID=57798796
diff --git a/core/src/com/google/inject/internal/EncounterImpl.java b/core/src/com/google/inject/internal/EncounterImpl.java
index 957743b..8aa3e94 100644
--- a/core/src/com/google/inject/internal/EncounterImpl.java
+++ b/core/src/com/google/inject/internal/EncounterImpl.java
@@ -19,6 +19,7 @@
 import static com.google.common.base.Preconditions.checkState;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
 import com.google.inject.Key;
 import com.google.inject.MembersInjector;
@@ -76,16 +77,16 @@
   }
   /*end[AOP]*/
 
-  ImmutableList<MembersInjector<? super T>> getMembersInjectors() {
+  ImmutableSet<MembersInjector<? super T>> getMembersInjectors() {
     return membersInjectors == null
-        ? ImmutableList.<MembersInjector<? super T>>of()
-        : ImmutableList.copyOf(membersInjectors);
+        ? ImmutableSet.<MembersInjector<? super T>>of()
+        : ImmutableSet.copyOf(membersInjectors);
   }
 
-  ImmutableList<InjectionListener<? super T>> getInjectionListeners() {
+  ImmutableSet<InjectionListener<? super T>> getInjectionListeners() {
     return injectionListeners == null
-        ? ImmutableList.<InjectionListener<? super T>>of()
-        : ImmutableList.copyOf(injectionListeners);
+        ? ImmutableSet.<InjectionListener<? super T>>of()
+        : ImmutableSet.copyOf(injectionListeners);
   }
 
   public void register(MembersInjector<? super T> membersInjector) {
diff --git a/core/src/com/google/inject/internal/MembersInjectorImpl.java b/core/src/com/google/inject/internal/MembersInjectorImpl.java
index ba32d28..498b441 100644
--- a/core/src/com/google/inject/internal/MembersInjectorImpl.java
+++ b/core/src/com/google/inject/internal/MembersInjectorImpl.java
@@ -34,8 +34,8 @@
   private final TypeLiteral<T> typeLiteral;
   private final InjectorImpl injector;
   private final ImmutableList<SingleMemberInjector> memberInjectors;
-  private final ImmutableList<MembersInjector<? super T>> userMembersInjectors;
-  private final ImmutableList<InjectionListener<? super T>> injectionListeners;
+  private final ImmutableSet<MembersInjector<? super T>> userMembersInjectors;
+  private final ImmutableSet<InjectionListener<? super T>> injectionListeners;
   /*if[AOP]*/
   private final ImmutableList<MethodAspect> addedAspects;
   /*end[AOP]*/
@@ -135,9 +135,7 @@
 
     // TODO: There's no way to know if a user's MembersInjector wants toolable injections.
     if(!toolableOnly) {
-    // optimization: use manual for/each to save allocating an iterator here
-      for (int i = 0, size = userMembersInjectors.size(); i < size; i++) {
-        MembersInjector<? super T> userMembersInjector = userMembersInjectors.get(i);
+      for (MembersInjector<? super T> userMembersInjector : userMembersInjectors) {
         try {
           userMembersInjector.injectMembers(t);
         } catch (RuntimeException e) {
diff --git a/core/src/com/google/inject/internal/MembersInjectorStore.java b/core/src/com/google/inject/internal/MembersInjectorStore.java
index 7604d4c..8e2acdd 100644
--- a/core/src/com/google/inject/internal/MembersInjectorStore.java
+++ b/core/src/com/google/inject/internal/MembersInjectorStore.java
@@ -18,9 +18,11 @@
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
 import com.google.inject.ConfigurationException;
 import com.google.inject.TypeLiteral;
 import com.google.inject.spi.InjectionPoint;
+import com.google.inject.spi.TypeListener;
 import com.google.inject.spi.TypeListenerBinding;
 
 import java.lang.reflect.Field;
@@ -97,12 +99,15 @@
     errors.throwIfNewErrors(numErrorsBefore);
 
     EncounterImpl<T> encounter = new EncounterImpl<T>(errors, injector.lookups);
-    for (TypeListenerBinding typeListener : typeListenerBindings) {
-      if (typeListener.getTypeMatcher().matches(type)) {
+    Set<TypeListener> alreadySeenListeners = Sets.newHashSet();
+    for (TypeListenerBinding binding : typeListenerBindings) {
+      TypeListener typeListener = binding.getListener();
+      if (!alreadySeenListeners.contains(typeListener) && binding.getTypeMatcher().matches(type)) {
+        alreadySeenListeners.add(typeListener);
         try {
-          typeListener.getListener().hear(type, encounter);
+          typeListener.hear(type, encounter);
         } catch (RuntimeException e) {
-          errors.errorNotifyingTypeListener(typeListener, type, e);
+          errors.errorNotifyingTypeListener(binding, type, e);
         }
       }
     }
diff --git a/core/src/com/google/inject/internal/ProvisionListenerStackCallback.java b/core/src/com/google/inject/internal/ProvisionListenerStackCallback.java
index a99c513..ad292a6 100644
--- a/core/src/com/google/inject/internal/ProvisionListenerStackCallback.java
+++ b/core/src/com/google/inject/internal/ProvisionListenerStackCallback.java
@@ -17,12 +17,14 @@
 package com.google.inject.internal;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Sets;
 import com.google.inject.Binding;
 import com.google.inject.ProvisionException;
 import com.google.inject.spi.DependencyAndSource;
 import com.google.inject.spi.ProvisionListener;
 
 import java.util.List;
+import java.util.Set;
 
 /**
  * Intercepts provisions with a stack of listeners.
@@ -49,7 +51,8 @@
     if (listeners.isEmpty()) {
       this.listeners = EMPTY_LISTENER;
     } else {
-      this.listeners = listeners.toArray(new ProvisionListener[listeners.size()]);
+      Set<ProvisionListener> deDuplicated = Sets.newLinkedHashSet(listeners);
+      this.listeners = deDuplicated.toArray(new ProvisionListener[deDuplicated.size()]);
     }
   }
   
diff --git a/core/src/com/google/inject/internal/ProxyFactory.java b/core/src/com/google/inject/internal/ProxyFactory.java
index 0b39f76..13aff81 100644
--- a/core/src/com/google/inject/internal/ProxyFactory.java
+++ b/core/src/com/google/inject/internal/ProxyFactory.java
@@ -20,6 +20,7 @@
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.inject.spi.InjectionPoint;
@@ -135,8 +136,10 @@
         interceptorsMapBuilder = ImmutableMap.builder();
       }
 
-      interceptorsMapBuilder.put(pair.method, ImmutableList.copyOf(pair.interceptors));
-      callbacks[i] = new InterceptorStackCallback(pair.method, pair.interceptors);
+      ImmutableList<MethodInterceptor> deDuplicated =
+          ImmutableSet.copyOf(pair.interceptors).asList();
+      interceptorsMapBuilder.put(pair.method, deDuplicated);
+      callbacks[i] = new InterceptorStackCallback(pair.method, deDuplicated);
     }
 
     interceptors = interceptorsMapBuilder != null
diff --git a/core/test/com/google/inject/MethodInterceptionTest.java b/core/test/com/google/inject/MethodInterceptionTest.java
index 6fe8b4e..3a0eef5 100644
--- a/core/test/com/google/inject/MethodInterceptionTest.java
+++ b/core/test/com/google/inject/MethodInterceptionTest.java
@@ -313,4 +313,17 @@
     }
   }
   
+  public void testDeDuplicateInterceptors() throws Exception {
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      @Override protected void configure() {
+        CountingInterceptor interceptor = new CountingInterceptor();
+        bindInterceptor(Matchers.any(), Matchers.any(), interceptor);
+        bindInterceptor(Matchers.any(), Matchers.any(), interceptor);
+      }
+    });
+
+    Interceptable interceptable = injector.getInstance(Interceptable.class);
+    interceptable.foo();
+    assertEquals(1, count.get());
+  }
 }
diff --git a/core/test/com/google/inject/ProvisionListenerTest.java b/core/test/com/google/inject/ProvisionListenerTest.java
index 7cf317d..a66d346 100644
--- a/core/test/com/google/inject/ProvisionListenerTest.java
+++ b/core/test/com/google/inject/ProvisionListenerTest.java
@@ -695,4 +695,17 @@
       this.x = xProvider.get();
     }
   }
+  
+  public void testDeDuplicateProvisionListeners() {
+    final Counter counter = new Counter();
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      @Override
+      protected void configure() {
+        bindListener(Matchers.any(), counter);
+        bindListener(Matchers.any(), counter);
+      }
+    });
+    injector.getInstance(Many.class);
+    assertEquals("ProvisionListener not de-duplicated", 1, counter.count);
+  }
 }
diff --git a/core/test/com/google/inject/TypeListenerTest.java b/core/test/com/google/inject/TypeListenerTest.java
index e4b5f63..ba7520d 100644
--- a/core/test/com/google/inject/TypeListenerTest.java
+++ b/core/test/com/google/inject/TypeListenerTest.java
@@ -630,6 +630,49 @@
     }
   }
 
+  private static class CountingMembersInjector implements MembersInjector<D> {
+    public void injectMembers(D instance) {
+      ++instance.userInjected;
+    }
+  }
+
+  private static class CountingInjectionListener implements InjectionListener<D> {
+    public void afterInjection(D injectee) {
+      ++injectee.listenersNotified;
+    }
+  }
+
+  private static class DuplicatingTypeListener implements TypeListener {
+    int count = 0;
+
+    @SuppressWarnings({"rawtypes", "unchecked"})
+    public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
+      ++count;
+
+      MembersInjector membersInjector = new CountingMembersInjector();
+      encounter.register(membersInjector);
+      encounter.register(membersInjector);
+
+      InjectionListener injectionListener = new CountingInjectionListener();
+      encounter.register(injectionListener);
+      encounter.register(injectionListener);
+    }
+  }
+
+  public void testDeDuplicateTypeListeners() {
+    final DuplicatingTypeListener typeListener = new DuplicatingTypeListener();
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      @Override
+      protected void configure() {
+        bindListener(any(), typeListener);
+        bindListener(only(new TypeLiteral<D>() {}), typeListener);
+      }
+    });
+    D d = injector.getInstance(D.class);
+    d.assertAllCounts(1);
+    assertEquals(1, typeListener.count);
+  }
+
   // TODO: recursively accessing a lookup should fail
 
   static class A {