[sisu-guice] 01/01: upgraded to latest upstream 3.1.9

Eugene Zhukov eugene-guest at moszumanska.debian.org
Thu Feb 6 09:31:37 UTC 2014


This is an automated email from the git hooks/post-receive script.

eugene-guest pushed a commit to branch master
in repository sisu-guice.

commit 50ed7a663e1138b5789df8ff8911eb86d020ce66
Author: Eugene Zhukov <jevgeni.zh at gmail.com>
Date:   Thu Feb 6 09:30:34 2014 +0000

    upgraded to latest upstream 3.1.9
---
 PATCHES                                            | 1368 --------------------
 .../GUICE_183_fine_grained_singleton_lock.patch    |   33 +
 PATCHES/GUICE_288_decouple_thread_local.patch      |   46 +
 PATCHES/GUICE_350_robust_system_get_property.patch |   68 +
 PATCHES/GUICE_492_slf4j_logger_injection.patch     |  173 +++
 PATCHES/GUICE_618_extensible_filter_pipeline.patch |   40 +
 ...GUICE_695_persistence_properties_provider.patch |   73 ++
 PATCHES/GUICE_697_add_missing_headers.patch        |  199 +++
 .../GUICE_709_externalize_guava_dependency.patch   |  843 ++++++++++++
 .../GUICE_718_configurable_annotation_check.patch  |   45 +
 PATCHES/GUICE_759_upgrade_to_asm5.patch            |   74 ++
 PATCHES/SISUFY.patch                               |  590 +++++++++
 README                                             |    1 -
 README.md                                          |   10 +
 build.properties                                   |    2 +-
 build.xml                                          |  136 +-
 common.xml                                         |   44 +-
 copy.sh                                            |   54 -
 core/pom.xml                                       |   76 +-
 core/src/com/google/inject/AbstractModule.java     |    6 +-
 core/src/com/google/inject/Binder.java             |   29 +-
 core/src/com/google/inject/Inject.java             |    2 +-
 core/src/com/google/inject/Key.java                |   33 +-
 core/src/com/google/inject/PrivateModule.java      |    9 +
 core/src/com/google/inject/Provides.java           |    2 +-
 core/src/com/google/inject/ProvisionException.java |    3 +-
 .../inject/internal/AbstractBindingProcessor.java  |    3 +-
 .../com/google/inject/internal/Annotations.java    |  135 +-
 .../com/google/inject/internal/BindingBuilder.java |    7 +-
 .../com/google/inject/internal/BindingImpl.java    |    4 +
 .../google/inject/internal/BindingProcessor.java   |   27 +-
 .../inject/internal/BoundProviderFactory.java      |    8 +-
 .../com/google/inject/internal/BytecodeGen.java    |   45 +-
 .../inject/internal/ConstructorBindingImpl.java    |   46 +-
 .../inject/internal/ConstructorInjector.java       |   12 +-
 .../google/inject/internal/DelayedInitialize.java  |   16 +-
 .../com/google/inject/internal/EncounterImpl.java  |   13 +-
 core/src/com/google/inject/internal/Errors.java    |  120 +-
 .../com/google/inject/internal/FailableCache.java  |   17 +-
 .../google/inject/internal/InheritingState.java    |   18 +-
 .../com/google/inject/internal/Initializer.java    |   32 +-
 .../com/google/inject/internal/InjectorImpl.java   |   68 +-
 .../inject/internal/InjectorOptionsProcessor.java  |   24 +-
 .../com/google/inject/internal/InjectorShell.java  |   28 +-
 .../inject/internal/InstanceBindingImpl.java       |   15 +-
 .../inject/internal/InterceptorStackCallback.java  |   13 +-
 .../google/inject/internal/InternalContext.java    |   41 +-
 .../InternalFactoryToInitializableAdapter.java     |    7 +-
 .../com/google/inject/internal/InternalFlags.java  |   52 +
 .../google/inject/internal/LinkedBindingImpl.java  |   23 +-
 .../inject/internal/LinkedProviderBindingImpl.java |   31 +-
 .../inject/internal/MembersInjectorImpl.java       |   34 +-
 .../inject/internal/MembersInjectorStore.java      |   13 +-
 core/src/com/google/inject/internal/MoreTypes.java |    2 +-
 .../com/google/inject/internal/Nullability.java    |   16 +
 .../inject/internal/ProvidedByInternalFactory.java |   20 +-
 .../internal/ProviderInstanceBindingImpl.java      |   15 +-
 .../inject/internal/ProviderInternalFactory.java   |   43 +-
 .../internal/ProvisionListenerCallbackStore.java   |   77 +-
 .../internal/ProvisionListenerStackCallback.java   |   35 +-
 .../com/google/inject/internal/ProxyFactory.java   |   20 +-
 .../com/google/inject/internal/RehashableKeys.java |   40 +
 .../inject/internal/ScopeBindingProcessor.java     |   10 +-
 core/src/com/google/inject/internal/Scoping.java   |   29 +-
 core/src/com/google/inject/internal/State.java     |   12 +-
 .../internal/TypeConverterBindingProcessor.java    |  113 +-
 .../inject/internal/UntargettedBindingImpl.java    |   15 +-
 .../google/inject/internal/util/LineNumbers.java   |  131 +-
 .../inject/internal/util/SourceProvider.java       |   52 +-
 .../inject/internal/util/StackTraceElements.java   |  162 ++-
 .../inject/spi/DefaultBindingTargetVisitor.java    |    4 +-
 .../google/inject/spi/DefaultElementVisitor.java   |    8 +
 .../com/google/inject/spi/DependencyAndSource.java |    4 +-
 core/src/com/google/inject/spi/ElementSource.java  |  166 +++
 core/src/com/google/inject/spi/ElementVisitor.java |   15 +
 core/src/com/google/inject/spi/Elements.java       |  188 ++-
 core/src/com/google/inject/spi/InjectionPoint.java |    4 +-
 core/src/com/google/inject/spi/Message.java        |    7 +
 core/src/com/google/inject/spi/ModuleSource.java   |  166 +++
 core/src/com/google/inject/spi/ProviderLookup.java |   15 +-
 .../com/google/inject/spi/ProvisionListener.java   |   16 +-
 .../inject/spi/ProvisionListenerBinding.java       |   20 +-
 .../spi/RequireAtInjectOnConstructorsOption.java   |   48 +
 .../spi/RequireExactBindingAnnotationsOption.java  |   47 +
 core/src/com/google/inject/spi/Toolable.java       |   16 +
 .../google/inject/spi/TypeConverterBinding.java    |    3 +-
 core/src/com/google/inject/util/Modules.java       |  288 +++--
 core/test/com/google/inject/AllTests.java          |    9 +
 core/test/com/google/inject/Asserts.java           |   48 +
 core/test/com/google/inject/BinderTest.java        |  192 ++-
 .../com/google/inject/BindingAnnotationTest.java   |   90 +-
 core/test/com/google/inject/BindingTest.java       |    3 +-
 .../com/google/inject/DuplicateBindingsTest.java   |   36 +-
 core/test/com/google/inject/ErrorHandlingTest.java |    4 +-
 core/test/com/google/inject/KeyTest.java           |   74 +-
 .../com/google/inject/MethodInterceptionTest.java  |   68 +-
 core/test/com/google/inject/ModulesTest.java       |   19 +-
 .../google/inject/NullableInjectionPointTest.java  |    3 +-
 .../test/com/google/inject/ParentInjectorTest.java |   27 +-
 core/test/com/google/inject/PrivateModuleTest.java |   91 +-
 .../com/google/inject/ProvisionExceptionTest.java  |    7 +-
 .../com/google/inject/ProvisionListenerTest.java   |  201 ++-
 core/test/com/google/inject/ReflectionTest.java    |   11 +-
 .../com/google/inject/RequestInjectionTest.java    |    3 +-
 .../inject/RequireAtInjectOnConstructorsTest.java  |  202 +++
 core/test/com/google/inject/ScopesTest.java        |  118 +-
 .../test/com/google/inject/TypeConversionTest.java |  213 ++-
 core/test/com/google/inject/TypeListenerTest.java  |  114 +-
 .../com/google/inject/internal/MoreTypesTest.java  |    6 +
 .../google/inject/internal/RehashableKeysTest.java |  132 ++
 .../inject/internal/util/LineNumbersTest.java      |    7 +-
 .../com/google/inject/spi/ElementApplyToTest.java  |    1 +
 .../com/google/inject/spi/ElementSourceTest.java   |  175 +++
 core/test/com/google/inject/spi/ElementsTest.java  |   47 +-
 .../com/google/inject/spi/ModuleSourceTest.java    |  125 ++
 .../com/google/inject/spi/ProviderMethodsTest.java |   14 +-
 .../com/google/inject/spi/SpiBindingsTest.java     |   33 +-
 .../inject/{ => util}/OverrideModuleTest.java      |  149 ++-
 .../test/com/googlecode/guice/BytecodeGenTest.java |   30 +-
 core/test/com/googlecode/guice/Jsr330Test.java     |    8 +-
 .../com/googlecode/guice/OSGiContainerTest.java    |   12 +-
 debian/changelog                                   |    4 +-
 debian/control                                     |    2 +-
 debian/maven.rules                                 |    2 +
 extensions/assistedinject/build.xml                |    5 +-
 extensions/assistedinject/pom.xml                  |    4 +-
 .../inject/assistedinject/FactoryProvider2.java    |   49 +-
 .../assistedinject/FactoryModuleBuilderTest.java   |   29 +-
 .../assistedinject/FactoryProvider2Test.java       |   66 +-
 extensions/grapher/build.xml                       |    5 +-
 extensions/grapher/pom.xml                         |    4 +-
 .../google/inject/grapher/ShortNameFactory.java    |    4 +
 .../inject/grapher/graphviz/GraphvizGrapher.java   |    4 +-
 extensions/jmx/build.xml                           |    5 +-
 extensions/jmx/pom.xml                             |    4 +-
 extensions/jndi/build.xml                          |    5 +-
 extensions/jndi/pom.xml                            |    4 +-
 extensions/jndi/test/placeholder.txt               |    1 +
 extensions/mini/pom.xml                            |    4 +-
 extensions/multibindings/build.xml                 |    5 +-
 extensions/multibindings/pom.xml                   |    4 +-
 .../com/google/inject/multibindings/Element.java   |   14 +-
 .../com/google/inject/multibindings/MapBinder.java |  137 +-
 .../google/inject/multibindings/Multibinder.java   |   98 +-
 .../google/inject/multibindings/RealElement.java   |  222 +++-
 .../google/inject/multibindings/MapBinderTest.java |  143 +-
 .../inject/multibindings/MultibinderTest.java      |  527 +++++++-
 .../com/google/inject/multibindings/SpiUtils.java  |   41 +-
 extensions/persist/build.xml                       |    5 +-
 extensions/persist/pom.xml                         |   19 +-
 .../google/inject/persist/finder/package-info.java |   18 +-
 .../inject/persist/jpa/JpaPersistModule.java       |    3 +-
 .../google/inject/persist/jpa/package-info.java    |   18 +-
 extensions/pom.xml                                 |   15 +-
 extensions/service/pom.xml                         |    4 +-
 extensions/servlet/build.xml                       |    5 +-
 extensions/servlet/pom.xml                         |    4 +-
 .../servlet/ContinuingHttpServletRequest.java      |    3 +
 .../inject/servlet/DefaultFilterPipeline.java      |    4 +
 .../inject/servlet/FilterChainInvocation.java      |   27 +-
 .../google/inject/servlet/FilterDefinition.java    |   28 +-
 .../src/com/google/inject/servlet/GuiceFilter.java |   46 +-
 .../inject/servlet/InternalServletModule.java      |    3 +
 .../google/inject/servlet/ScopingException.java    |   20 +-
 .../google/inject/servlet/ServletDefinition.java   |   12 +-
 .../com/google/inject/servlet/ServletScopes.java   |  192 +--
 .../com/google/inject/servlet/ServletUtils.java    |   52 +
 .../test/com/google/inject/servlet/AllTests.java   |    1 +
 .../servlet/ContinuingHttpServletRequestTest.java  |   58 +
 .../inject/servlet/FilterDefinitionTest.java       |   99 +-
 .../inject/servlet/ServletDefinitionTest.java      |   40 +-
 .../google/inject/servlet/ServletUtilsTest.java    |   58 +
 .../servlet/TransferRequestIntegrationTest.java    |  122 ++
 extensions/spring/build.xml                        |    5 +-
 extensions/spring/pom.xml                          |    6 +-
 extensions/struts2/build.xml                       |    5 +-
 extensions/struts2/pom.xml                         |    6 +-
 extensions/throwingproviders/build.xml             |    5 +-
 extensions/throwingproviders/pom.xml               |    4 +-
 .../throwingproviders/CheckedProvideUtils.java     |  106 ++
 .../throwingproviders/CheckedProviderMethod.java   |   30 +-
 .../CheckedProviderMethodsModule.java              |    3 +-
 .../CheckedProviderWithDependencies.java           |   20 +-
 .../inject/throwingproviders/ThrowingInject.java   |   26 +-
 .../throwingproviders/ThrowingProviderBinder.java  |  114 +-
 .../throwingproviders/CheckedProviderTest.java     |  778 +++++++++--
 guice.iml                                          |   18 +
 pom.xml                                            |   76 +-
 uploadApiDiffs.sh                                  |    9 +-
 189 files changed, 9150 insertions(+), 3199 deletions(-)

diff --git a/PATCHES b/PATCHES
deleted file mode 100644
index ef099d4..0000000
--- a/PATCHES
+++ /dev/null
@@ -1,1368 +0,0 @@
-diff --git a/core/pom.xml b/core/pom.xml
-index 12ed258..eccd787 100644
---- a/core/pom.xml
-+++ b/core/pom.xml
-@@ -4,18 +4,15 @@
-   <modelVersion>4.0.0</modelVersion>
- 
-   <parent>
--    <groupId>com.google.inject</groupId>
-+    <groupId>org.sonatype.sisu.inject</groupId>
-     <artifactId>guice-parent</artifactId>
--    <version>3.0-SNAPSHOT</version>
-+    <version>3.2.0-SNAPSHOT</version>
-   </parent>
- 
--  <artifactId>guice</artifactId>
-+  <groupId>org.sonatype.sisu</groupId>
-+  <artifactId>sisu-guice</artifactId>
- 
--  <name>Google Guice - Core Library</name>
--
--  <properties>
--    <cglib.version>2.2.2</cglib.version>
--  </properties>
-+  <name>Sisu Guice - Core Library</name>
- 
-   <dependencies>
-     <dependency>
-@@ -29,14 +26,21 @@
-       <version>1.0</version>
-     </dependency>
-     <dependency>
--      <groupId>com.google.guava</groupId>
--      <artifactId>guava</artifactId>
--      <version>11.0.1</version>
-+      <groupId>org.sonatype.sisu</groupId>
-+      <artifactId>sisu-guava</artifactId>
-+      <version>0.11.1</version>
-     </dependency>
-     <dependency>
-       <groupId>cglib</groupId>
-       <artifactId>cglib</artifactId>
--      <version>${cglib.version}</version>
-+      <version>2.2.2</version>
-+      <optional>true</optional>
-+    </dependency>
-+    <dependency>
-+      <groupId>org.slf4j</groupId>
-+      <artifactId>slf4j-api</artifactId>
-+      <version>1.6.3</version>
-+      <optional>true</optional>
-     </dependency>
-     <dependency>
-       <groupId>javax.inject</groupId>
-@@ -47,7 +51,7 @@
-     <dependency>
-       <groupId>org.springframework</groupId>
-       <artifactId>spring-beans</artifactId>
--      <version>3.0.5.RELEASE</version>
-+      <version>3.0.6.RELEASE</version>
-       <scope>test</scope>
-     </dependency>
-     <dependency>
-@@ -59,7 +63,7 @@
-     <dependency>
-       <groupId>org.apache.felix</groupId>
-       <artifactId>org.apache.felix.framework</artifactId>
--      <version>3.0.5</version>
-+      <version>4.0.1</version>
-       <scope>test</scope>
-     </dependency>
-   </dependencies>
-@@ -83,12 +87,16 @@
-         <artifactId>maven-surefire-plugin</artifactId>
-         <configuration>
-           <!--
--           | Temporarily excluded tests
-+           | Run core tests without SLF4J on the classpath
-+          -->
-+          <classpathDependencyExcludes>
-+            <exclude>org.slf4j:slf4j-api</exclude>
-+          </classpathDependencyExcludes>
-+          <!--
-+           | This test needs updating for use with Maven
-           -->
-           <excludes>
-             <exclude>**/OSGiContainerTest*</exclude>
--            <exclude>**/ScopesTest*</exclude>
--            <exclude>**/TypeConversionTest*</exclude>
-           </excludes>
-         </configuration>
-       </plugin>
-@@ -100,6 +108,11 @@
-         <artifactId>maven-bundle-plugin</artifactId>
-         <configuration>
-           <instructions>
-+            <Bundle-Name>
-+              ${project.artifactId}$(if;$(classes;NAMED;*.Interceptor*);; (no_aop))
-+            </Bundle-Name>
-+            <Require-Bundle>org.sonatype.sisu.guava</Require-Bundle>
-+            <DynamicImport-Package>org.slf4j</DynamicImport-Package>
-             <Eclipse-ExtensibleAPI>true</Eclipse-ExtensibleAPI>
-           </instructions>
-         </configuration>
-@@ -197,46 +210,65 @@
-           <value>!false</value>
-         </property>
-       </activation>
--      <dependencies>
--        <!--
--         | Mark as optional: embedded by JarJar
--        -->
--        <dependency>
--          <groupId>cglib</groupId>
--          <artifactId>cglib</artifactId>
--          <version>${cglib.version}</version>
--          <optional>true</optional>
--        </dependency>
--      </dependencies>
-       <build>
-         <plugins>
-           <plugin>
-             <groupId>org.sonatype.plugins</groupId>
-             <artifactId>jarjar-maven-plugin</artifactId>
-+            <version>1.5</version>
-             <configuration>
-+              <overwrite>true</overwrite>
-               <includes>
-                 <include>*:asm</include>
-                 <include>*:cglib</include>
--                <include>*:guava</include>
--                <include>*:jsr305</include>
-               </includes>
-+              <rules>
-+                <rule>
-+                  <pattern>net.sf.cglib.*</pattern>
-+                  <result>com.google.inject.internal.cglib.$@1</result>
-+                </rule>
-+                <rule>
-+                  <pattern>net.sf.cglib.**.*</pattern>
-+                  <result>com.google.inject.internal.cglib. at 1.$@2</result>
-+                </rule>
-+                <rule>
-+                  <pattern>org.objectweb.asm.*</pattern>
-+                  <result>com.google.inject.internal.asm.$@1</result>
-+                </rule>
-+                <rule>
-+                  <pattern>org.objectweb.asm.**.*</pattern>
-+                  <result>com.google.inject.internal.asm. at 1.$@2</result>
-+                </rule>
-+                <keep>
-+                  <pattern>com.google.inject.**</pattern>
-+                </keep>
-+                <keep>
-+                  <pattern>com.googlecode.**</pattern>
-+                </keep>
-+              </rules>
-             </configuration>
--          </plugin>
--          <plugin>
-             <!--
--             | Package the original non-JarJar'd classes so extensions can compile against them
-+             | JarJar all classes before running tests
-             -->
--            <artifactId>maven-jar-plugin</artifactId>
-             <executions>
-               <execution>
--                <id>no_deps</id>
--                <phase>package</phase>
-+                <id>jarjar-classes</id>
-+                <phase>process-test-classes</phase>
-                 <goals>
--                  <goal>jar</goal>
-+                  <goal>jarjar</goal>
-+                </goals>
-+                <configuration>
-+                  <input>{classes}</input>
-+                </configuration>
-+              </execution>
-+              <execution>
-+                <id>jarjar-test-classes</id>
-+                <phase>process-test-classes</phase>
-+                <goals>
-+                  <goal>jarjar</goal>
-                 </goals>
-                 <configuration>
--                  <classesDirectory>${project.build.directory}/original-classes</classesDirectory>
--                  <classifier>no_deps</classifier>
-+                  <input>{test-classes}</input>
-                 </configuration>
-               </execution>
-             </executions>
-@@ -244,6 +276,43 @@
-         </plugins>
-       </build>
-     </profile>
-+    <profile>
-+      <!--
-+       | m2e profile - force use of JarJar inside Eclipse
-+      -->
-+      <id>m2e</id>
-+      <activation>
-+        <property>
-+          <name>m2e.version</name>
-+        </property>
-+      </activation>
-+      <build>
-+        <pluginManagement>
-+          <plugins>
-+            <plugin>
-+              <groupId>org.eclipse.m2e</groupId>
-+              <artifactId>lifecycle-mapping</artifactId>
-+              <version>1.0.0</version>
-+              <configuration>
-+                <lifecycleMappingMetadata>
-+                  <pluginExecutions>
-+                    <pluginExecution>
-+                      <pluginExecutionFilter>
-+                        <groupId>org.sonatype.plugins</groupId>
-+                        <artifactId>jarjar-maven-plugin</artifactId>
-+                        <versionRange>[1.4,)</versionRange>
-+                        <goals><goal>jarjar</goal></goals>
-+                      </pluginExecutionFilter>
-+                      <action><execute /></action>
-+                    </pluginExecution>
-+                  </pluginExecutions>
-+                </lifecycleMappingMetadata>
-+              </configuration>
-+            </plugin>
-+          </plugins>
-+        </pluginManagement>
-+      </build>
-+    </profile>
-   </profiles>
- 
- </project>
-diff --git a/core/src/com/google/inject/Scopes.java b/core/src/com/google/inject/Scopes.java
-index fe44c15..035bd43 100644
---- a/core/src/com/google/inject/Scopes.java
-+++ b/core/src/com/google/inject/Scopes.java
-@@ -17,7 +17,6 @@
- package com.google.inject;
- 
- import com.google.inject.internal.CircularDependencyProxy;
--import com.google.inject.internal.InternalInjectorCreator;
- import com.google.inject.internal.LinkedBindingImpl;
- import com.google.inject.spi.BindingScopingVisitor;
- import com.google.inject.spi.ExposedBinding;
-@@ -53,14 +52,9 @@ public class Scopes {
-         public T get() {
-           if (instance == null) {
-             /*
--             * Use a pretty coarse lock. We don't want to run into deadlocks
--             * when two threads try to load circularly-dependent objects.
--             * Maybe one of these days we will identify independent graphs of
--             * objects and offer to load them in parallel.
--             *
-              * This block is re-entrant for circular dependencies.
-              */
--            synchronized (InternalInjectorCreator.class) {
-+            synchronized (this) {
-               if (instance == null) {
-                 T provided = creator.get();
- 
-diff --git a/core/src/com/google/inject/internal/AbstractBindingProcessor.java b/core/src/com/google/inject/internal/AbstractBindingProcessor.java
-index f09c40b..8fe03de 100644
---- a/core/src/com/google/inject/internal/AbstractBindingProcessor.java
-+++ b/core/src/com/google/inject/internal/AbstractBindingProcessor.java
-@@ -38,6 +38,20 @@ import java.util.Set;
-  */
- abstract class AbstractBindingProcessor extends AbstractProcessor {
- 
-+//------------------------------------------------------------------------------
-+  private static final boolean DISABLE_MISPLACED_ANNOTATION_CHECK;
-+  static {
-+    boolean disableCheck;
-+    try {
-+      disableCheck = Boolean.parseBoolean(System.getProperty(
-+          "guice.disable.misplaced.annotation.check", "false"));
-+    } catch (Throwable e) {
-+      disableCheck = false;
-+    }
-+    DISABLE_MISPLACED_ANNOTATION_CHECK = disableCheck;
-+  }
-+//------------------------------------------------------------------------------
-+
-   // 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.
-@@ -126,8 +140,14 @@ abstract class AbstractBindingProcessor extends AbstractProcessor {
-   }
-   
-   private <T> void validateKey(Object source, Key<T> key) {
-+//------------------------------------------------------------------------------
-+if (!DISABLE_MISPLACED_ANNOTATION_CHECK) {
-+//------------------------------------------------------------------------------
-     Annotations.checkForMisplacedScopeAnnotations(
-         key.getTypeLiteral().getRawType(), source, errors);
-+//------------------------------------------------------------------------------
-+}
-+//------------------------------------------------------------------------------
-   }
-   
-   /** 
-diff --git a/core/src/com/google/inject/internal/BytecodeGen.java b/core/src/com/google/inject/internal/BytecodeGen.java
-index d0a14fc..55ad161 100644
---- a/core/src/com/google/inject/internal/BytecodeGen.java
-+++ b/core/src/com/google/inject/internal/BytecodeGen.java
-@@ -118,8 +118,7 @@ public final class BytecodeGen {
-   end[NO_AOP]*/
- 
-   /** Use "-Dguice.custom.loader=false" to disable custom classloading. */
--  private static final boolean CUSTOM_LOADER_ENABLED
--      = Boolean.parseBoolean(System.getProperty("guice.custom.loader", "true"));
-+  private static final boolean CUSTOM_LOADER_ENABLED;
- 
-   /**
-    * Weak cache of bridge class loaders that make the Guice implementation
-@@ -128,6 +127,14 @@ public final class BytecodeGen {
-   private static final Map<ClassLoader, ClassLoader> CLASS_LOADER_CACHE;
- 
-   static {
-+    boolean customLoaderEnabled;
-+    try {
-+      customLoaderEnabled = Boolean.parseBoolean(System.getProperty("guice.custom.loader", "true"));
-+    } catch (Throwable e) {
-+      customLoaderEnabled = false; // unlikely we'll also have permissions for custom loading
-+    }
-+    CUSTOM_LOADER_ENABLED = customLoaderEnabled;
-+
-     if (CUSTOM_LOADER_ENABLED) {
-       CLASS_LOADER_CACHE = new MapMaker().weakKeys().weakValues().makeComputingMap(
-           new Function<ClassLoader, ClassLoader>() {
-diff --git a/core/src/com/google/inject/internal/InjectorShell.java b/core/src/com/google/inject/internal/InjectorShell.java
-index 4985f8c..6f3ade3 100644
---- a/core/src/com/google/inject/internal/InjectorShell.java
-+++ b/core/src/com/google/inject/internal/InjectorShell.java
-@@ -251,6 +251,15 @@ final class InjectorShell {
-         new ProviderInstanceBindingImpl<Logger>(injector, key,
-             SourceProvider.UNKNOWN_SOURCE, loggerFactory, Scoping.UNSCOPED,
-             loggerFactory, ImmutableSet.<InjectionPoint>of()));
-+
-+    try {
-+      Key<org.slf4j.Logger> slf4jKey = Key.get(org.slf4j.Logger.class);
-+      SLF4JLoggerFactory slf4jLoggerFactory = new SLF4JLoggerFactory(injector);
-+      injector.state.putBinding(slf4jKey,
-+          new ProviderInstanceBindingImpl<org.slf4j.Logger>(injector, slf4jKey,
-+              SourceProvider.UNKNOWN_SOURCE, slf4jLoggerFactory, Scoping.UNSCOPED,
-+              slf4jLoggerFactory, ImmutableSet.<InjectionPoint>of()));
-+    } catch (Throwable e) {}
-   }
- 
-   private static class LoggerFactory implements InternalFactory<Logger>, Provider<Logger> {
-@@ -270,6 +279,44 @@ final class InjectorShell {
-     }
-   }
- 
-+  private static class SLF4JLoggerFactory implements InternalFactory<org.slf4j.Logger>, Provider<org.slf4j.Logger> {
-+    private final Injector injector;
-+
-+    private org.slf4j.ILoggerFactory loggerFactory;
-+
-+    SLF4JLoggerFactory(Injector injector) {
-+      this.injector = injector;
-+    }
-+
-+    org.slf4j.ILoggerFactory loggerFactory() {
-+      if (loggerFactory == null) {
-+        try {
-+          loggerFactory = injector.getInstance(org.slf4j.ILoggerFactory.class);
-+        } catch (Throwable e) {}
-+        if (loggerFactory == null) {
-+          loggerFactory = org.slf4j.LoggerFactory.getILoggerFactory();
-+        }
-+      }
-+      return loggerFactory;
-+    }
-+
-+    public org.slf4j.Logger get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked) {
-+      InjectionPoint injectionPoint = dependency.getInjectionPoint();
-+      if (injectionPoint != null) {
-+        return loggerFactory().getLogger(injectionPoint.getMember().getDeclaringClass().getName());
-+      }
-+      return loggerFactory().getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME);
-+    }
-+
-+    public org.slf4j.Logger get() {
-+      return loggerFactory().getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME);
-+    }
-+
-+    public String toString() {
-+      return "Provider<org.slf4j.Logger>";
-+    }
-+  }
-+
-   private static class RootModule implements Module {
-     final Stage stage;
- 
-diff --git a/core/src/com/google/inject/internal/ProxyFactory.java b/core/src/com/google/inject/internal/ProxyFactory.java
-index 3ab04bd..f4e93d2 100644
---- a/core/src/com/google/inject/internal/ProxyFactory.java
-+++ b/core/src/com/google/inject/internal/ProxyFactory.java
-@@ -24,6 +24,7 @@ import com.google.common.collect.Lists;
- import com.google.common.collect.Maps;
- import com.google.inject.spi.InjectionPoint;
- 
-+import net.sf.cglib.core.MethodWrapper;
- import net.sf.cglib.proxy.Callback;
- import net.sf.cglib.proxy.CallbackFilter;
- import net.sf.cglib.proxy.Enhancer;
-@@ -198,35 +199,34 @@ final class ProxyFactory<T> implements ConstructionProxyFactory<T> {
-   }
- 
-   /**
--   * A callback filter that maps methods to unique IDs. We define equals and hashCode using the
--   * declaring class so that enhanced classes can be shared between injectors.
-+   * A callback filter that maps methods to unique IDs. We define equals and hashCode using
-+   * the method-wrapper:indices map so that enhanced classes can be shared between injectors.
-    */
-   private static class IndicesCallbackFilter implements CallbackFilter {
--    final Class<?> declaringClass;
--    final Map<Method, Integer> indices;
-+    final Map<Object, Integer> indices;
-+    final int hashCode;
- 
-     IndicesCallbackFilter(Class<?> declaringClass, List<Method> methods) {
--      this.declaringClass = declaringClass;
--      final Map<Method, Integer> indices = Maps.newHashMap();
-+      final Map<Object, Integer> indices = Maps.newHashMap();
-       for (int i = 0; i < methods.size(); i++) {
--        Method method = methods.get(i);
--        indices.put(method, i);
-+        indices.put(MethodWrapper.create(methods.get(i)), i);
-       }
- 
-       this.indices = indices;
-+      hashCode = indices.hashCode();
-     }
- 
-     public int accept(Method method) {
--      return indices.get(method);
-+      return indices.get(MethodWrapper.create(method));
-     }
- 
-     @Override public boolean equals(Object o) {
-       return o instanceof IndicesCallbackFilter &&
--          ((IndicesCallbackFilter) o).declaringClass == declaringClass;
-+          ((IndicesCallbackFilter) o).indices.equals(indices);
-     }
- 
-     @Override public int hashCode() {
--      return declaringClass.hashCode();
-+      return hashCode;
-     }
-   }
- 
-diff --git a/core/test/com/google/inject/ScopesTest.java b/core/test/com/google/inject/ScopesTest.java
-index 7fd328a..689aec4 100644
---- a/core/test/com/google/inject/ScopesTest.java
-+++ b/core/test/com/google/inject/ScopesTest.java
-@@ -241,7 +241,7 @@ public class ScopesTest extends TestCase {
-     Asserts.assertNotSerializable(Scopes.NO_SCOPE);
-   }
- 
--  public void testUnscopedProviderWorksOutsideOfRequestedScope() {
-+  public void ignoreTestUnscopedProviderWorksOutsideOfRequestedScope() {
-     final RememberProviderScope scope = new RememberProviderScope();
- 
-     Injector injector = Guice.createInjector(new AbstractModule() {
-diff --git a/core/test/com/google/inject/TypeConversionTest.java b/core/test/com/google/inject/TypeConversionTest.java
-index 02fb442..df3ee93 100644
---- a/core/test/com/google/inject/TypeConversionTest.java
-+++ b/core/test/com/google/inject/TypeConversionTest.java
-@@ -402,7 +402,7 @@ public class TypeConversionTest extends TestCase {
-     @Inject @NumericValue Date date;
-   }
- 
--  public void testCannotConvertUnannotatedBindings() {
-+  public void ignoreTestCannotConvertUnannotatedBindings() {
-     Injector injector = Guice.createInjector(new AbstractModule() {
-       protected void configure() {
-         bind(String.class).toInstance("55");
-diff --git a/core/test/com/googlecode/guice/OSGiContainerTest.java b/core/test/com/googlecode/guice/OSGiContainerTest.java
-index 77ab8fa..068a541 100644
---- a/core/test/com/googlecode/guice/OSGiContainerTest.java
-+++ b/core/test/com/googlecode/guice/OSGiContainerTest.java
-@@ -33,6 +33,7 @@ import java.io.FileOutputStream;
- import java.io.IOException;
- import java.io.OutputStream;
- import java.util.Iterator;
-+import java.util.Map;
- import java.util.Properties;
- 
- import javax.imageio.spi.ServiceRegistry;
-@@ -134,7 +135,7 @@ public class OSGiContainerTest
-     // test each available OSGi framework in turn
-     Iterator<FrameworkFactory> f = ServiceRegistry.lookupProviders(FrameworkFactory.class);
-     while (f.hasNext()) {
--      Framework framework = f.next().newFramework(properties);
-+      Framework framework = f.next().newFramework((Map)properties);
- 
-       framework.start();
-       BundleContext systemContext = framework.getBundleContext();
-diff --git a/extensions/assistedinject/pom.xml b/extensions/assistedinject/pom.xml
-index 745d6b4..4959704 100644
---- a/extensions/assistedinject/pom.xml
-+++ b/extensions/assistedinject/pom.xml
-@@ -4,13 +4,13 @@
-   <modelVersion>4.0.0</modelVersion>
- 
-   <parent>
--    <groupId>com.google.inject.extensions</groupId>
--    <artifactId>extensions-parent</artifactId>
--    <version>3.0-SNAPSHOT</version>
-+    <groupId>org.sonatype.sisu.inject</groupId>
-+    <artifactId>guice-extensions</artifactId>
-+    <version>3.2.0-SNAPSHOT</version>
-   </parent>
- 
-   <artifactId>guice-assistedinject</artifactId>
- 
--  <name>Google Guice - Extensions - AssistedInject</name>
-+  <name>Sisu Guice - Extensions - AssistedInject</name>
- 
- </project>
-diff --git a/extensions/grapher/pom.xml b/extensions/grapher/pom.xml
-index 015db56..3e20d17 100644
---- a/extensions/grapher/pom.xml
-+++ b/extensions/grapher/pom.xml
-@@ -4,23 +4,23 @@
-   <modelVersion>4.0.0</modelVersion>
- 
-   <parent>
--    <groupId>com.google.inject.extensions</groupId>
--    <artifactId>extensions-parent</artifactId>
--    <version>3.0-SNAPSHOT</version>
-+    <groupId>org.sonatype.sisu.inject</groupId>
-+    <artifactId>guice-extensions</artifactId>
-+    <version>3.2.0-SNAPSHOT</version>
-   </parent>
- 
-   <artifactId>guice-grapher</artifactId>
- 
--  <name>Google Guice - Extensions - Grapher</name>
-+  <name>Sisu Guice - Extensions - Grapher</name>
- 
-   <dependencies>
-     <dependency>
--      <groupId>com.google.inject.extensions</groupId>
-+      <groupId>org.sonatype.sisu.inject</groupId>
-       <artifactId>guice-assistedinject</artifactId>
-       <version>${project.version}</version>
-     </dependency>
-     <dependency>
--      <groupId>com.google.inject.extensions</groupId>
-+      <groupId>org.sonatype.sisu.inject</groupId>
-       <artifactId>guice-multibindings</artifactId>
-       <version>${project.version}</version>
-     </dependency>
-diff --git a/extensions/jmx/pom.xml b/extensions/jmx/pom.xml
-index d8188ca..1bb98a8 100644
---- a/extensions/jmx/pom.xml
-+++ b/extensions/jmx/pom.xml
-@@ -4,13 +4,13 @@
-   <modelVersion>4.0.0</modelVersion>
- 
-   <parent>
--    <groupId>com.google.inject.extensions</groupId>
--    <artifactId>extensions-parent</artifactId>
--    <version>3.0-SNAPSHOT</version>
-+    <groupId>org.sonatype.sisu.inject</groupId>
-+    <artifactId>guice-extensions</artifactId>
-+    <version>3.2.0-SNAPSHOT</version>
-   </parent>
- 
-   <artifactId>guice-jmx</artifactId>
- 
--  <name>Google Guice - Extensions - JMX</name>
-+  <name>Sisu Guice - Extensions - JMX</name>
- 
- </project>
-diff --git a/extensions/jndi/pom.xml b/extensions/jndi/pom.xml
-index ff04c5f..f186f1c 100644
---- a/extensions/jndi/pom.xml
-+++ b/extensions/jndi/pom.xml
-@@ -4,13 +4,13 @@
-   <modelVersion>4.0.0</modelVersion>
- 
-   <parent>
--    <groupId>com.google.inject.extensions</groupId>
--    <artifactId>extensions-parent</artifactId>
--    <version>3.0-SNAPSHOT</version>
-+    <groupId>org.sonatype.sisu.inject</groupId>
-+    <artifactId>guice-extensions</artifactId>
-+    <version>3.2.0-SNAPSHOT</version>
-   </parent>
- 
-   <artifactId>guice-jndi</artifactId>
- 
--  <name>Google Guice - Extensions - JNDI</name>
-+  <name>Sisu Guice - Extensions - JNDI</name>
- 
- </project>
-diff --git a/extensions/mini/pom.xml b/extensions/mini/pom.xml
-index 3fa7032..69e979b 100644
---- a/extensions/mini/pom.xml
-+++ b/extensions/mini/pom.xml
-@@ -4,13 +4,13 @@
-   <modelVersion>4.0.0</modelVersion>
- 
-   <parent>
--    <groupId>com.google.inject.extensions</groupId>
--    <artifactId>extensions-parent</artifactId>
-+    <groupId>org.sonatype.sisu.inject</groupId>
-+    <artifactId>guice-extensions</artifactId>
-     <version>3.0-SNAPSHOT</version>
-   </parent>
- 
-   <artifactId>guice-mini</artifactId>
- 
--  <name>Google Guice - Extensions - Mini</name>
-+  <name>Sisu Guice - Extensions - Mini</name>
- 
- </project>
-diff --git a/extensions/multibindings/pom.xml b/extensions/multibindings/pom.xml
-index b49283c..48cf744 100644
---- a/extensions/multibindings/pom.xml
-+++ b/extensions/multibindings/pom.xml
-@@ -4,13 +4,13 @@
-   <modelVersion>4.0.0</modelVersion>
- 
-   <parent>
--    <groupId>com.google.inject.extensions</groupId>
--    <artifactId>extensions-parent</artifactId>
--    <version>3.0-SNAPSHOT</version>
-+    <groupId>org.sonatype.sisu.inject</groupId>
-+    <artifactId>guice-extensions</artifactId>
-+    <version>3.2.0-SNAPSHOT</version>
-   </parent>
- 
-   <artifactId>guice-multibindings</artifactId>
- 
--  <name>Google Guice - Extensions - MultiBindings</name>
-+  <name>Sisu Guice - Extensions - MultiBindings</name>
- 
- </project>
-diff --git a/extensions/persist/pom.xml b/extensions/persist/pom.xml
-index 2c787b1..4fb1820 100644
---- a/extensions/persist/pom.xml
-+++ b/extensions/persist/pom.xml
-@@ -4,14 +4,14 @@
-   <modelVersion>4.0.0</modelVersion>
- 
-   <parent>
--    <groupId>com.google.inject.extensions</groupId>
--    <artifactId>extensions-parent</artifactId>
--    <version>3.0-SNAPSHOT</version>
-+    <groupId>org.sonatype.sisu.inject</groupId>
-+    <artifactId>guice-extensions</artifactId>
-+    <version>3.2.0-SNAPSHOT</version>
-   </parent>
- 
-   <artifactId>guice-persist</artifactId>
- 
--  <name>Google Guice - Extensions - Persist</name>
-+  <name>Sisu Guice - Extensions - Persist</name>
- 
-   <dependencies>
-     <dependency>
-@@ -29,7 +29,7 @@
-     <dependency>
-       <groupId>org.slf4j</groupId>
-       <artifactId>slf4j-simple</artifactId>
--      <version>1.6.1</version>
-+      <version>1.6.3</version>
-       <scope>test</scope>
-     </dependency>
-     <dependency>
-@@ -41,20 +41,9 @@
-     <dependency>
-       <groupId>org.hsqldb</groupId>
-       <artifactId>hsqldb-j5</artifactId>
--      <version>2.0.0</version>
-+      <version>2.2.4</version>
-       <scope>test</scope>
-     </dependency>
-   </dependencies>
- 
--  <build>
--    <plugins>
--      <plugin>
--        <artifactId>maven-surefire-plugin</artifactId>
--        <configuration>
--          <forkMode>never</forkMode>
--        </configuration>
--      </plugin>
--    </plugins>
--  </build>
--
- </project>
-diff --git a/extensions/persist/src/com/google/inject/persist/jpa/JpaPersistModule.java b/extensions/persist/src/com/google/inject/persist/jpa/JpaPersistModule.java
-index b318b27..5e9e364 100644
---- a/extensions/persist/src/com/google/inject/persist/jpa/JpaPersistModule.java
-+++ b/extensions/persist/src/com/google/inject/persist/jpa/JpaPersistModule.java
-@@ -37,6 +37,7 @@ import java.lang.reflect.Proxy;
- import java.util.List;
- import java.util.Properties;
- 
-+import javax.inject.Provider;
- import javax.persistence.EntityManager;
- import javax.persistence.EntityManagerFactory;
- 
-@@ -55,6 +56,7 @@ public final class JpaPersistModule extends PersistModule {
-   }
- 
-   private Properties properties;
-+  private Class<? extends Provider<? extends Properties>> propertiesProvider;
-   private MethodInterceptor transactionInterceptor;
- 
-   @Override protected void configurePersistence() {
-@@ -62,6 +64,9 @@ public final class JpaPersistModule extends PersistModule {
- 
-     if (null != properties) {
-       bind(Properties.class).annotatedWith(Jpa.class).toInstance(properties);
-+    } else if (null != propertiesProvider) {
-+      bind(Properties.class).annotatedWith(Jpa.class)
-+          .toProvider(propertiesProvider);
-     } else {
-       bind(Properties.class).annotatedWith(Jpa.class)
-           .toProvider(Providers.<Properties>of(null));
-@@ -99,6 +104,11 @@ public final class JpaPersistModule extends PersistModule {
-     return this;
-   }
- 
-+  public JpaPersistModule properties(Class<? extends Provider<? extends Properties>> provider) {
-+    this.propertiesProvider = provider;
-+    return this;
-+  }
-+
-   private final List<Class<?>> dynamicFinders = Lists.newArrayList();
- 
-   /**
-diff --git a/extensions/persist/src/com/google/inject/persist/jpa/JpaPersistService.java b/extensions/persist/src/com/google/inject/persist/jpa/JpaPersistService.java
-index b8fe35c..68d76a7 100644
---- a/extensions/persist/src/com/google/inject/persist/jpa/JpaPersistService.java
-+++ b/extensions/persist/src/com/google/inject/persist/jpa/JpaPersistService.java
-@@ -42,11 +42,11 @@ class JpaPersistService implements Provider<EntityManager>, UnitOfWork, PersistS
-   private final ThreadLocal<EntityManager> entityManager = new ThreadLocal<EntityManager>();
- 
-   private final String persistenceUnitName;
--  private final Properties persistenceProperties;
-+  private final Provider<Properties> persistenceProperties;
- 
-   @Inject
-   public JpaPersistService(@Jpa String persistenceUnitName,
--      @Nullable @Jpa Properties persistenceProperties) {
-+      @Nullable @Jpa Provider<Properties> persistenceProperties) {
-     this.persistenceUnitName = persistenceUnitName;
-     this.persistenceProperties = persistenceProperties;
-   }
-@@ -95,7 +95,7 @@ class JpaPersistService implements Provider<EntityManager>, UnitOfWork, PersistS
- 
-     if (null != persistenceProperties) {
-       this.emFactory = Persistence
--          .createEntityManagerFactory(persistenceUnitName, persistenceProperties);
-+          .createEntityManagerFactory(persistenceUnitName, persistenceProperties.get());
-     } else {
-       this.emFactory = Persistence.createEntityManagerFactory(persistenceUnitName);
-     }
-diff --git a/extensions/pom.xml b/extensions/pom.xml
-index 309e092..823c103 100644
---- a/extensions/pom.xml
-+++ b/extensions/pom.xml
-@@ -4,17 +4,16 @@
-   <modelVersion>4.0.0</modelVersion>
- 
-   <parent>
--    <groupId>com.google.inject</groupId>
-+    <groupId>org.sonatype.sisu.inject</groupId>
-     <artifactId>guice-parent</artifactId>
--    <version>3.0-SNAPSHOT</version>
-+    <version>3.2.0-SNAPSHOT</version>
-   </parent>
- 
-   <packaging>pom</packaging>
- 
--  <groupId>com.google.inject.extensions</groupId>
--  <artifactId>extensions-parent</artifactId>
-+  <artifactId>guice-extensions</artifactId>
- 
--  <name>Google Guice - Extensions</name>
-+  <name>Sisu Guice - Extensions</name>
- 
-   <modules>
-     <module>assistedinject</module>
-@@ -40,16 +39,16 @@
-      | All extensions depend on the core
-     -->
-     <dependency>
--      <groupId>com.google.inject</groupId>
--      <artifactId>guice</artifactId>
-+      <groupId>org.sonatype.sisu</groupId>
-+      <artifactId>sisu-guice</artifactId>
-       <version>${project.version}</version>
-     </dependency>
-     <!--
-      | Some extension tests depend on the core tests
-     -->
-     <dependency>
--      <groupId>com.google.inject</groupId>
--      <artifactId>guice</artifactId>
-+      <groupId>org.sonatype.sisu</groupId>
-+      <artifactId>sisu-guice</artifactId>
-       <version>${project.version}</version>
-       <classifier>tests</classifier>
-       <scope>test</scope>
-@@ -79,7 +78,7 @@
-         <artifactId>maven-bundle-plugin</artifactId>
-         <configuration>
-           <instructions>
--            <Fragment-Host>com.google.inject</Fragment-Host>
-+            <Fragment-Host>org.sonatype.sisu.guice</Fragment-Host>
-           </instructions>
-         </configuration>
-       </plugin>
-@@ -89,41 +88,22 @@
-   <profiles>
-     <profile>
-       <!--
--       | JarJar build profile: re-package ASM and CGLIB references under the Guice namespace
-+       | Non-JarJar build profile: need CGLIB during tests
-       -->
--      <id>guice.with.jarjar</id>
-       <activation>
-         <property>
-           <name>guice.with.jarjar</name>
--          <value>!false</value>
-+          <value>false</value>
-         </property>
-       </activation>
-       <dependencies>
--        <!--
--         | Extensions compile first against the non-JarJar'd core - and are then JarJar'd themselves
--         | (optional dependency so it doesn't leak to client projects that depend on Guice artifacts)
--        -->
-         <dependency>
--          <groupId>com.google.inject</groupId>
--          <artifactId>guice</artifactId>
--          <version>${project.version}</version>
--          <classifier>no_deps</classifier>
--          <optional>true</optional>
-+          <groupId>cglib</groupId>
-+          <artifactId>cglib</artifactId>
-+          <version>2.2.2</version>
-+          <scope>test</scope>
-         </dependency>
-       </dependencies>
--      <build>
--        <plugins>
--          <plugin>
--            <groupId>org.sonatype.plugins</groupId>
--            <artifactId>jarjar-maven-plugin</artifactId>
--            <configuration>
--              <excludes>
--                <exclude>*:*</exclude>
--              </excludes>
--            </configuration>
--          </plugin>
--        </plugins>
--      </build>
-     </profile>
-   </profiles>
- 
-diff --git a/extensions/service/pom.xml b/extensions/service/pom.xml
-index d4efef8..df91f36 100644
---- a/extensions/service/pom.xml
-+++ b/extensions/service/pom.xml
-@@ -4,13 +4,13 @@
-   <modelVersion>4.0.0</modelVersion>
- 
-   <parent>
--    <groupId>com.google.inject.extensions</groupId>
--    <artifactId>extensions-parent</artifactId>
-+    <groupId>org.sonatype.sisu.inject</groupId>
-+    <artifactId>guice-extensions</artifactId>
-     <version>3.0-SNAPSHOT</version>
-   </parent>
- 
-   <artifactId>guice-service</artifactId>
- 
--  <name>Google Guice - Extensions - Service</name>
-+  <name>Sisu Guice - Extensions - Service</name>
- 
- </project>
-diff --git a/extensions/servlet/pom.xml b/extensions/servlet/pom.xml
-index 4f34f2f..9017372 100644
---- a/extensions/servlet/pom.xml
-+++ b/extensions/servlet/pom.xml
-@@ -4,14 +4,14 @@
-   <modelVersion>4.0.0</modelVersion>
- 
-   <parent>
--    <groupId>com.google.inject.extensions</groupId>
--    <artifactId>extensions-parent</artifactId>
--    <version>3.0-SNAPSHOT</version>
-+    <groupId>org.sonatype.sisu.inject</groupId>
-+    <artifactId>guice-extensions</artifactId>
-+    <version>3.2.0-SNAPSHOT</version>
-   </parent>
- 
-   <artifactId>guice-servlet</artifactId>
- 
--  <name>Google Guice - Extensions - Servlet</name>
-+  <name>Sisu Guice - Extensions - Servlet</name>
- 
-   <dependencies>
-     <dependency>
-diff --git a/extensions/servlet/src/com/google/inject/servlet/FilterPipeline.java b/extensions/servlet/src/com/google/inject/servlet/FilterPipeline.java
-index 985064b..d85d97c 100755
---- a/extensions/servlet/src/com/google/inject/servlet/FilterPipeline.java
-+++ b/extensions/servlet/src/com/google/inject/servlet/FilterPipeline.java
-@@ -39,7 +39,7 @@ import javax.servlet.ServletResponse;
-  * @author dhanji at gmail.com (Dhanji R. Prasanna)
-  */
- @ImplementedBy(DefaultFilterPipeline.class)
--interface FilterPipeline {
-+public interface FilterPipeline {
-   void initPipeline(ServletContext context) throws ServletException;
-   void destroyPipeline();
- 
-diff --git a/extensions/servlet/src/com/google/inject/servlet/GuiceFilter.java b/extensions/servlet/src/com/google/inject/servlet/GuiceFilter.java
-index d069c96..1967da6 100644
---- a/extensions/servlet/src/com/google/inject/servlet/GuiceFilter.java
-+++ b/extensions/servlet/src/com/google/inject/servlet/GuiceFilter.java
-@@ -85,7 +85,7 @@ public class GuiceFilter implements Filter {
-     this(null);
-   }
- 
--  @Inject GuiceFilter(FilterPipeline filterPipeline) {
-+  @Inject protected GuiceFilter(FilterPipeline filterPipeline) {
-     injectedPipeline = filterPipeline;
-   }
- 
-diff --git a/extensions/spring/pom.xml b/extensions/spring/pom.xml
-index f81f6ad..edc79f7 100644
---- a/extensions/spring/pom.xml
-+++ b/extensions/spring/pom.xml
-@@ -4,20 +4,20 @@
-   <modelVersion>4.0.0</modelVersion>
- 
-   <parent>
--    <groupId>com.google.inject.extensions</groupId>
--    <artifactId>extensions-parent</artifactId>
--    <version>3.0-SNAPSHOT</version>
-+    <groupId>org.sonatype.sisu.inject</groupId>
-+    <artifactId>guice-extensions</artifactId>
-+    <version>3.2.0-SNAPSHOT</version>
-   </parent>
- 
-   <artifactId>guice-spring</artifactId>
- 
--  <name>Google Guice - Extensions - Spring</name>
-+  <name>Sisu Guice - Extensions - Spring</name>
- 
-   <dependencies>
-     <dependency>
-       <groupId>org.springframework</groupId>
-       <artifactId>spring-beans</artifactId>
--      <version>3.0.5.RELEASE</version>
-+      <version>3.0.6.RELEASE</version>
-       <scope>provided</scope>
-     </dependency>
-   </dependencies>
-diff --git a/extensions/struts2/pom.xml b/extensions/struts2/pom.xml
-index 38d313a..b85203c 100644
---- a/extensions/struts2/pom.xml
-+++ b/extensions/struts2/pom.xml
-@@ -4,18 +4,18 @@
-   <modelVersion>4.0.0</modelVersion>
- 
-   <parent>
--    <groupId>com.google.inject.extensions</groupId>
--    <artifactId>extensions-parent</artifactId>
--    <version>3.0-SNAPSHOT</version>
-+    <groupId>org.sonatype.sisu.inject</groupId>
-+    <artifactId>guice-extensions</artifactId>
-+    <version>3.2.0-SNAPSHOT</version>
-   </parent>
- 
-   <artifactId>guice-struts2</artifactId>
- 
--  <name>Google Guice - Extensions - Struts2</name>
-+  <name>Sisu Guice - Extensions - Struts2</name>
- 
-   <dependencies>
-     <dependency>
--      <groupId>com.google.inject.extensions</groupId>
-+      <groupId>org.sonatype.sisu.inject</groupId>
-       <artifactId>guice-servlet</artifactId>
-       <version>${project.version}</version>
-     </dependency>
-@@ -28,7 +28,7 @@
-     <dependency>
-       <groupId>org.apache.struts</groupId>
-       <artifactId>struts2-core</artifactId>
--      <version>2.2.1</version>
-+      <version>2.2.3.1</version>
-       <scope>provided</scope>
-     </dependency>
-   </dependencies>
-diff --git a/extensions/throwingproviders/pom.xml b/extensions/throwingproviders/pom.xml
-index bd67158..e89521e 100644
---- a/extensions/throwingproviders/pom.xml
-+++ b/extensions/throwingproviders/pom.xml
-@@ -4,13 +4,13 @@
-   <modelVersion>4.0.0</modelVersion>
- 
-   <parent>
--    <groupId>com.google.inject.extensions</groupId>
--    <artifactId>extensions-parent</artifactId>
--    <version>3.0-SNAPSHOT</version>
-+    <groupId>org.sonatype.sisu.inject</groupId>
-+    <artifactId>guice-extensions</artifactId>
-+    <version>3.2.0-SNAPSHOT</version>
-   </parent>
- 
-   <artifactId>guice-throwingproviders</artifactId>
- 
--  <name>Google Guice - Extensions - ThrowingProviders</name>
-+  <name>Sisu Guice - Extensions - ThrowingProviders</name>
- 
- </project>
-diff --git a/lib/build/doclava.jar b/lib/build/doclava.jar
-index 6abed4b..259cae6 100644
-Binary files a/lib/build/doclava.jar and b/lib/build/doclava.jar differ
-diff --git a/pom.xml b/pom.xml
-index 3b76484..88c0d85 100644
---- a/pom.xml
-+++ b/pom.xml
-@@ -18,21 +18,21 @@ See the Apache License Version 2.0 for the specific language governing permissio
-   <modelVersion>4.0.0</modelVersion>
- 
-   <parent>
--    <groupId>com.google</groupId>
--    <artifactId>google</artifactId>
--    <version>5</version>
-+    <groupId>org.sonatype.forge</groupId>
-+    <artifactId>forge-parent</artifactId>
-+    <version>10</version>
-   </parent>
- 
-   <packaging>pom</packaging>
- 
--  <groupId>com.google.inject</groupId>
-+  <groupId>org.sonatype.sisu.inject</groupId>
-   <artifactId>guice-parent</artifactId>
--  <version>3.0-SNAPSHOT</version>
-+  <version>3.2.0-SNAPSHOT</version>
- 
--  <name>Google Guice</name>
-+  <name>Sisu Guice</name>
- 
-   <description>
--    Guice is a lightweight dependency injection framework for Java 5 and above
-+    Patched build of Guice: a lightweight dependency injection framework for Java 5 and above
-   </description>
- 
-   <url>http://code.google.com/p/google-guice/</url>
-@@ -47,23 +47,23 @@ See the Apache License Version 2.0 for the specific language governing permissio
-     <mailingList>
-       <name>Guice Users List</name>
-       <archive>http://groups.google.com/group/google-guice/topics</archive>
--      <subscribe>http://groups.google.com/group/google-guice/subscribe</subscribe>
--      <unsubscribe>http://groups.google.com/group/google-guice/subscribe</unsubscribe>
-+      <subscribe>google-guice+subscribe at googlegroups.com</subscribe>
-+      <unsubscribe>google-guice+unsubscribe at googlegroups.com</unsubscribe>
-       <post>http://groups.google.com/group/google-guice/post</post>
-     </mailingList>
-     <mailingList>
-       <name>Guice Developers List</name>
-       <archive>http://groups.google.com/group/google-guice-dev/topics</archive>
--      <subscribe>http://groups.google.com/group/google-guice-dev/subscribe</subscribe>
--      <unsubscribe>http://groups.google.com/group/google-guice-dev/subscribe</unsubscribe>
-+      <subscribe>google-guice-dev+subscribe at googlegroups.com</subscribe>
-+      <unsubscribe>google-guice-dev+unsubscribe at googlegroups.com</unsubscribe>
-       <post>http://groups.google.com/group/google-guice-dev/post</post>
-     </mailingList>
-   </mailingLists>
- 
-   <scm>
--    <connection>scm:svn:http://google-guice.googlecode.com/svn/trunk</connection>
--    <developerConnection>scm:svn:https://google-guice.googlecode.com/svn/trunk</developerConnection>
--    <url>http://code.google.com/p/google-guice/source/browse</url>
-+    <connection>scm:git:git at github.com:sonatype/sisu-guice.git</connection>
-+    <developerConnection>scm:git:git at github.com:sonatype/sisu-guice.git</developerConnection>
-+    <url>http://github.com/sonatype/sisu-guice</url>
-   </scm>
- 
-   <issueManagement>
-@@ -73,7 +73,7 @@ See the Apache License Version 2.0 for the specific language governing permissio
- 
-   <ciManagement>
-     <system>Hudson</system>
--    <url>https://grid.sonatype.org/ci/job/Google-Guice/</url>
-+    <url>https://builds.sonatype.org/job/sisu-guice/</url>
-   </ciManagement>
- 
-   <licenses>
-@@ -97,7 +97,7 @@ See the Apache License Version 2.0 for the specific language governing permissio
-     <!--
-      | The spec version of the public Guice API
-     -->
--    <guice.api.version>1.3</guice.api.version>
-+    <guice.api.version>1.4</guice.api.version>
-     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-     <!--
-      | Use "-Dguice.with.jarjar=false" to build without jarjar
-@@ -116,8 +116,7 @@ See the Apache License Version 2.0 for the specific language governing permissio
-     <dependency>
-       <groupId>org.testng</groupId>
-       <artifactId>testng</artifactId>
--      <version>5.11</version>
--      <classifier>jdk15</classifier>
-+      <version>6.3</version>
-       <scope>test</scope>
-     </dependency>
-   </dependencies>
-@@ -153,7 +152,7 @@ See the Apache License Version 2.0 for the specific language governing permissio
-         -->
-         <plugin>
-           <artifactId>maven-remote-resources-plugin</artifactId>
--          <version>1.1</version>
-+          <version>1.2.1</version>
-           <executions>
-             <execution>
-               <goals>
-@@ -177,7 +176,7 @@ See the Apache License Version 2.0 for the specific language governing permissio
-         <plugin>
-           <groupId>org.codehaus.mojo</groupId>
-           <artifactId>animal-sniffer-maven-plugin</artifactId>
--          <version>1.6</version>
-+          <version>1.7</version>
-           <configuration>
-             <signature>
-               <groupId>org.codehaus.mojo.signature</groupId>
-@@ -195,81 +194,9 @@ See the Apache License Version 2.0 for the specific language governing permissio
-             </execution>
-           </executions>
-         </plugin>
--        <!--
--         | Shared JarJar configuration
--        -->
--        <plugin>
--          <groupId>org.sonatype.plugins</groupId>
--          <artifactId>jarjar-maven-plugin</artifactId>
--          <version>1.4</version>
--          <configuration>
--            <rules>
--              <rule>
--                <pattern>net.sf.cglib.*</pattern>
--                <result>com.google.inject.internal.cglib.$@1</result>
--              </rule>
--              <rule>
--                <pattern>net.sf.cglib.**.*</pattern>
--                <result>com.google.inject.internal.cglib. at 1.$@2</result>
--              </rule>
--              <rule>
--                <pattern>org.objectweb.asm.*</pattern>
--                <result>com.google.inject.internal.asm.$@1</result>
--              </rule>
--              <rule>
--                <pattern>org.objectweb.asm.**.*</pattern>
--                <result>com.google.inject.internal.asm. at 1.$@2</result>
--              </rule>
--              <rule>
--                <pattern>com.google.common.*</pattern>
--                <result>com.google.inject.internal.guava.$@1</result>
--              </rule>
--              <rule>
--                <pattern>com.google.common.**.*</pattern>
--                <result>com.google.inject.internal.guava. at 1.$@2</result>
--              </rule>
--              <keep>
--                <pattern>com.google.inject.**</pattern>
--              </keep>
--              <keep>
--                <pattern>com.googlecode.**</pattern>
--              </keep>
--              <keep>
--                <!-- the servlet extension uses this but core doesn't,
--                     so we explicitly instruct the build to keep it. -->
--                <pattern>com.google.common.base.Throwables</pattern>
--              </keep>
--            </rules>
--          </configuration>
--          <!--
--           | JarJar all classes before running tests
--          -->
--          <executions>
--            <execution>
--              <id>jarjar-classes</id>
--              <phase>process-test-classes</phase>
--              <goals>
--                <goal>jarjar</goal>
--              </goals>
--              <configuration>
--                <input>{classes}</input>
--              </configuration>
--            </execution>
--            <execution>
--              <id>jarjar-test-classes</id>
--              <phase>process-test-classes</phase>
--              <goals>
--                <goal>jarjar</goal>
--              </goals>
--              <configuration>
--                <input>{test-classes}</input>
--              </configuration>
--            </execution>
--          </executions>
--        </plugin>
-         <plugin>
-           <artifactId>maven-surefire-plugin</artifactId>
--          <version>2.5</version>
-+          <version>2.10</version>
-           <configuration>
-             <redirectTestOutputToFile>true</redirectTestOutputToFile>
-           </configuration>
-@@ -280,21 +207,19 @@ See the Apache License Version 2.0 for the specific language governing permissio
-         <plugin>
-           <groupId>org.apache.felix</groupId>
-           <artifactId>maven-bundle-plugin</artifactId>
--          <version>2.1.0</version>
-+          <version>2.3.6</version>
-           <configuration>
-             <instructions>
--              <module>com.google.inject</module>
--              <_include>-${project.basedir}/build.properties</_include>
-+              <Bundle-Name>${project.artifactId}</Bundle-Name>
-+              <Bundle-SymbolicName>$(maven-symbolicname);singleton:=true</Bundle-SymbolicName>
-               <Bundle-Copyright>Copyright (C) 2006 Google Inc.</Bundle-Copyright>
-               <Bundle-DocURL>http://code.google.com/p/google-guice/</Bundle-DocURL>
--              <Bundle-Name>${project.artifactId}</Bundle-Name>
--              <Bundle-SymbolicName>$(module)</Bundle-SymbolicName>
-+              <Bundle-Vendor>Sonatype, Inc.</Bundle-Vendor>
-               <Bundle-RequiredExecutionEnvironment>
-                 J2SE-1.5,JavaSE-1.6
-               </Bundle-RequiredExecutionEnvironment>
--              <Import-Package>!com.google.inject.*,*</Import-Package>
--              <_exportcontents>!*.internal.*,$(module).*;version=${guice.api.version}</_exportcontents>
--              <_versionpolicy>[$(version;==;$(@)),$(version;+;$(@)))</_versionpolicy>
-+              <_exportcontents>!*.internal.*,*;version=${guice.api.version}</_exportcontents>
-+              <Import-Package>!com.google.*,*</Import-Package>
-               <_nouses>true</_nouses>
-               <_removeheaders>
-                 Embed-Dependency,Embed-Transitive,
-@@ -304,6 +229,9 @@ See the Apache License Version 2.0 for the specific language governing permissio
-                 Ignore-Package,Bnd-LastModified
-               </_removeheaders>
-             </instructions>
-+            <archive>
-+              <forced>true</forced><!-- dummy entry to stop bundleplugin from picking up jar config -->
-+            </archive>
-           </configuration>
-           <executions>
-             <execution>
-@@ -319,12 +247,11 @@ See the Apache License Version 2.0 for the specific language governing permissio
-         -->
-         <plugin>
-           <artifactId>maven-jar-plugin</artifactId>
--          <version>2.3.1</version>
-+          <version>2.3.2</version>
-           <configuration>
-             <archive>
-               <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
--              <!--  Exclude to mirror ant build -->
--          <addMavenDescriptor>false</addMavenDescriptor>
-+              <addMavenDescriptor>false</addMavenDescriptor><!--  Exclude to mirror ant build -->
-             </archive>
-           </configuration>
-           <executions>
-@@ -338,7 +265,35 @@ See the Apache License Version 2.0 for the specific language governing permissio
-         </plugin>
-         <plugin>
-           <artifactId>maven-javadoc-plugin</artifactId>
--          <version>2.7</version>
-+          <version>2.8</version>
-+          <configuration>
-+            <doclet>com.google.doclava.Doclava</doclet>
-+            <docletPath>
-+              ${project.basedir}/../lib/build/doclava.jar:
-+              ${project.basedir}/../../lib/build/doclava.jar
-+            </docletPath>
-+            <!--
-+             | bootclasspath required by Sun's JVM 
-+            -->
-+            <bootclasspath>${sun.boot.class.path}</bootclasspath>
-+            <excludePackageNames>*.internal</excludePackageNames>
-+            <additionalparam>
-+              -quiet
-+              -federate JDK http://download.oracle.com/javase/6/docs/api/index.html?
-+              -federationxml JDK http://doclava.googlecode.com/svn/static/api/openjdk-6.xml
-+              -hdf project.name "${project.name} (patched build of Google Guice)"
-+              -d ${project.build.directory}/apidocs
-+            </additionalparam>
-+            <useStandardDocletOptions>false</useStandardDocletOptions>
-+            <!--
-+             | Apple's JVM sometimes requires more memory
-+            -->
-+            <additionalJOption>-J-Xmx1024m</additionalJOption>
-+          </configuration>
-+        </plugin>
-+        <plugin>
-+          <artifactId>maven-site-plugin</artifactId>
-+          <version>3.0</version>
-         </plugin>
-         <plugin>
-           <artifactId>maven-source-plugin</artifactId>
-@@ -346,46 +301,21 @@ See the Apache License Version 2.0 for the specific language governing permissio
-         </plugin>
-         <plugin>
-           <artifactId>maven-gpg-plugin</artifactId>
--          <version>1.1</version>
-+          <version>1.4</version>
-         </plugin>
-         <plugin>
-           <artifactId>maven-release-plugin</artifactId>
--          <version>2.1</version>
-+          <version>2.2.1</version>
-           <configuration>
-             <autoVersionSubmodules>true</autoVersionSubmodules>
-           </configuration>
-         </plugin>
-         <plugin>
-           <artifactId>maven-deploy-plugin</artifactId>
--          <version>2.5</version>
-+          <version>2.7</version>
-         </plugin>
-       </plugins>
-     </pluginManagement>
-   </build>
- 
--  <profiles>
--    <profile>
--      <!--
--       | Deployment profile for the Sonatype Grid
--      -->
--      <id>sonatype-grid</id>
--      <properties>
--        <forgeReleaseId>forge-releases</forgeReleaseId>
--        <forgeReleaseUrl>http://repository.sonatype.org:8081/service/local/staging/deploy/maven2</forgeReleaseUrl>
--        <forgeSnapshotId>forge-snapshots</forgeSnapshotId>
--        <forgeSnapshotUrl>http://repository.sonatype.org/content/repositories/snapshots</forgeSnapshotUrl>
--      </properties>
--      <distributionManagement>
--        <repository>
--          <id>${forgeReleaseId}</id>
--          <url>${forgeReleaseUrl}</url>
--        </repository>
--        <snapshotRepository>
--          <id>${forgeSnapshotId}</id>
--          <url>${forgeSnapshotUrl}</url>
--        </snapshotRepository>
--      </distributionManagement>
--    </profile>
--  </profiles>
--
- </project>
diff --git a/PATCHES/GUICE_183_fine_grained_singleton_lock.patch b/PATCHES/GUICE_183_fine_grained_singleton_lock.patch
new file mode 100644
index 0000000..efb70d8
--- /dev/null
+++ b/PATCHES/GUICE_183_fine_grained_singleton_lock.patch
@@ -0,0 +1,33 @@
+Description: Replace class-level global SINGLETON lock with a finer-grained lock
+Author: Stuart McCulloch <mcculls at gmail.com>
+Bug-Google: http://code.google.com/p/google-guice/issues/detail?id=183
+Last-Update: 2012-07-29
+
+diff --git a/core/src/com/google/inject/Scopes.java b/core/src/com/google/inject/Scopes.java
+index fe44c15..035bd43 100644
+--- a/core/src/com/google/inject/Scopes.java
++++ b/core/src/com/google/inject/Scopes.java
+@@ -17,7 +17,6 @@
+ package com.google.inject;
+ 
+ import com.google.inject.internal.CircularDependencyProxy;
+-import com.google.inject.internal.InternalInjectorCreator;
+ import com.google.inject.internal.LinkedBindingImpl;
+ import com.google.inject.spi.BindingScopingVisitor;
+ import com.google.inject.spi.ExposedBinding;
+@@ -53,14 +52,9 @@ public class Scopes {
+         public T get() {
+           if (instance == null) {
+             /*
+-             * Use a pretty coarse lock. We don't want to run into deadlocks
+-             * when two threads try to load circularly-dependent objects.
+-             * Maybe one of these days we will identify independent graphs of
+-             * objects and offer to load them in parallel.
+-             *
+              * This block is re-entrant for circular dependencies.
+              */
+-            synchronized (InternalInjectorCreator.class) {
++            synchronized (this) {
+               if (instance == null) {
+                 T provided = creator.get();
+ 
diff --git a/PATCHES/GUICE_288_decouple_thread_local.patch b/PATCHES/GUICE_288_decouple_thread_local.patch
new file mode 100644
index 0000000..3049be8
--- /dev/null
+++ b/PATCHES/GUICE_288_decouple_thread_local.patch
@@ -0,0 +1,46 @@
+Description: Decouple the context ThreadLocal from the containing Injector/ClassLoader
+Author: Stuart McCulloch <mcculls at gmail.com>
+Bug-Google: http://code.google.com/p/google-guice/issues/detail?id=288
+Last-Update: 2013-09-19
+
+diff --git a/core/src/com/google/inject/internal/InjectorImpl.java b/core/src/com/google/inject/internal/InjectorImpl.java
+index 6faab97..3c25b40 100644
+--- a/core/src/com/google/inject/internal/InjectorImpl.java
++++ b/core/src/com/google/inject/internal/InjectorImpl.java
+@@ -128,7 +128,7 @@ final class InjectorImpl implements Injector, Lookups {
+     if (parent != null) {
+       localContext = parent.localContext;
+     } else {
+-      localContext = new LocalContextThreadLocal();
++      localContext = new ThreadLocal<Object[]>();
+     }
+   }
+ 
+@@ -1035,11 +1035,15 @@ final class InjectorImpl implements Injector, Lookups {
+     return getProvider(type).get();
+   }
+ 
+-  final ThreadLocal<Object[]> localContext;
++  private final ThreadLocal<Object[]> localContext;
+ 
+   /** Looks up thread local context. Creates (and removes) a new context if necessary. */
+   <T> T callInContext(ContextualCallable<T> callable) throws ErrorsException {
+     Object[] reference = localContext.get();
++    if (reference == null) {
++      reference = new Object[1];
++      localContext.set(reference);
++    }
+     if (reference[0] == null) {
+       reference[0] = new InternalContext();
+       try {
+@@ -1061,10 +1065,4 @@ final class InjectorImpl implements Injector, Lookups {
+         .toString();
+   }
+ 
+-  private static final class LocalContextThreadLocal extends ThreadLocal<Object[]> {
+-    @Override
+-    protected Object[] initialValue() {
+-      return new Object[1];
+-    }
+-  }
+ }
diff --git a/PATCHES/GUICE_350_robust_system_get_property.patch b/PATCHES/GUICE_350_robust_system_get_property.patch
new file mode 100644
index 0000000..15c6cb5
--- /dev/null
+++ b/PATCHES/GUICE_350_robust_system_get_property.patch
@@ -0,0 +1,68 @@
+Description: Wrap System.getProperty call in case security is enabled
+Author: Stuart McCulloch <mcculls at gmail.com>
+Bug-Google: http://code.google.com/p/google-guice/issues/detail?id=350
+Last-Update: 2013-11-13
+
+diff --git a/core/src/com/google/inject/internal/BytecodeGen.java b/core/src/com/google/inject/internal/BytecodeGen.java
+index ad7764b..f0ebc0a 100644
+--- a/core/src/com/google/inject/internal/BytecodeGen.java
++++ b/core/src/com/google/inject/internal/BytecodeGen.java
+@@ -117,8 +117,7 @@ public final class BytecodeGen {
+   end[NO_AOP]*/
+ 
+   /** Use "-Dguice.custom.loader=false" to disable custom classloading. */
+-  private static final boolean CUSTOM_LOADER_ENABLED
+-      = Boolean.parseBoolean(System.getProperty("guice.custom.loader", "true"));
++  private static final boolean CUSTOM_LOADER_ENABLED;
+ 
+   /**
+    * Weak cache of bridge class loaders that make the Guice implementation
+@@ -127,6 +126,14 @@ public final class BytecodeGen {
+   private static final LoadingCache<ClassLoader, ClassLoader> CLASS_LOADER_CACHE;
+ 
+   static {
++    boolean customLoaderEnabled;
++    try {
++      customLoaderEnabled = Boolean.parseBoolean(System.getProperty("guice.custom.loader", "true"));
++    } catch (Throwable e) {
++      customLoaderEnabled = false; // unlikely we'll also have permissions for custom loading
++    }
++    CUSTOM_LOADER_ENABLED = customLoaderEnabled;
++
+     CacheBuilder<Object, Object> builder = CacheBuilder.newBuilder().weakKeys().weakValues();
+     if (!CUSTOM_LOADER_ENABLED) {
+       builder.maximumSize(0);
+diff --git a/core/src/com/google/inject/internal/InternalFlags.java b/core/src/com/google/inject/internal/InternalFlags.java
+index 2c4ed75..31d412d 100644
+--- a/core/src/com/google/inject/internal/InternalFlags.java
++++ b/core/src/com/google/inject/internal/InternalFlags.java
+@@ -7,6 +7,18 @@ import java.util.logging.Logger;
+  */
+ public class InternalFlags {
+   private final static Logger logger = Logger.getLogger(InternalFlags.class.getName());
++
++  private static final IncludeStackTraceOption INCLUDE_STACK_TRACES;
++  static {
++    IncludeStackTraceOption includeStackTraces;
++    try {
++      includeStackTraces = parseIncludeStackTraceOption();
++    } catch (Throwable e) {
++      includeStackTraces = IncludeStackTraceOption.ONLY_FOR_DECLARING_SOURCE;
++    }
++    INCLUDE_STACK_TRACES = includeStackTraces;
++  }
++
+   /**
+    * The options for Guice stack trace collection.
+    */
+@@ -21,6 +33,10 @@ public class InternalFlags {
+ 
+ 
+   public static IncludeStackTraceOption getIncludeStackTraceOption() {
++    return INCLUDE_STACK_TRACES;
++  }
++
++  private static IncludeStackTraceOption parseIncludeStackTraceOption() {
+     String flag = System.getProperty("guice_include_stack_traces");
+     try {
+       return (flag == null || flag.length() == 0)
diff --git a/PATCHES/GUICE_492_slf4j_logger_injection.patch b/PATCHES/GUICE_492_slf4j_logger_injection.patch
new file mode 100644
index 0000000..0f875a5
--- /dev/null
+++ b/PATCHES/GUICE_492_slf4j_logger_injection.patch
@@ -0,0 +1,173 @@
+Description: Provide built-in injection of SLF4J loggers
+Author: Stuart McCulloch <mcculls at gmail.com>
+Bug-Google: http://code.google.com/p/google-guice/issues/detail?id=492
+Last-Update: 2012-07-29
+
+diff --git a/common.xml b/common.xml
+index 1102481..b0cae0c 100644
+--- a/common.xml
++++ b/common.xml
+@@ -44,6 +44,10 @@
+ 
+     <property name="Export-Package" value="!${module}.internal.*,${module}.*;version=${api.version}"/>
+ 
++    <condition property="DynamicImport-Package" value="org.slf4j">
++      <equals arg1="${module}" arg2="com.google.inject"/>
++    </condition>
++
+     <condition property="Eclipse-ExtensibleAPI" value="true">
+       <equals arg1="${module}" arg2="com.google.inject"/>
+     </condition>
+diff --git a/core/pom.xml b/core/pom.xml
+index 43570f2..5a04460 100644
+--- a/core/pom.xml
++++ b/core/pom.xml
+@@ -25,6 +25,12 @@
+       <version>1</version>
+     </dependency>
+     <dependency>
++      <groupId>org.slf4j</groupId>
++      <artifactId>slf4j-api</artifactId>
++      <version>1.6.4</version>
++      <optional>true</optional>
++    </dependency>
++    <dependency>
+       <groupId>aopalliance</groupId>
+       <artifactId>aopalliance</artifactId>
+       <version>1.0</version>
+@@ -89,6 +95,12 @@
+         <artifactId>maven-surefire-plugin</artifactId>
+         <configuration>
+           <!--
++           | Run core tests without SLF4J on the classpath
++          -->
++          <classpathDependencyExcludes>
++            <exclude>org.slf4j:slf4j-api</exclude>
++          </classpathDependencyExcludes>
++          <!--
+            | Temporarily excluded tests
+           -->
+           <excludes>
+@@ -107,6 +119,7 @@
+         <configuration>
+           <instructions>
+             <Eclipse-ExtensibleAPI>true</Eclipse-ExtensibleAPI>
++            <DynamicImport-Package>org.slf4j</DynamicImport-Package>
+           </instructions>
+         </configuration>
+       </plugin>
+diff --git a/core/src/com/google/inject/internal/InjectorShell.java b/core/src/com/google/inject/internal/InjectorShell.java
+index 043fdcf..f727a5e 100644
+--- a/core/src/com/google/inject/internal/InjectorShell.java
++++ b/core/src/com/google/inject/internal/InjectorShell.java
+@@ -252,6 +252,15 @@ final class InjectorShell {
+         new ProviderInstanceBindingImpl<Logger>(injector, key,
+             SourceProvider.UNKNOWN_SOURCE, loggerFactory, Scoping.UNSCOPED,
+             loggerFactory, ImmutableSet.<InjectionPoint>of()));
++
++    try {
++      Key<org.slf4j.Logger> slf4jKey = Key.get(org.slf4j.Logger.class);
++      SLF4JLoggerFactory slf4jLoggerFactory = new SLF4JLoggerFactory(injector);
++      injector.state.putBinding(slf4jKey,
++          new ProviderInstanceBindingImpl<org.slf4j.Logger>(injector, slf4jKey,
++              SourceProvider.UNKNOWN_SOURCE, slf4jLoggerFactory, Scoping.UNSCOPED,
++              slf4jLoggerFactory, ImmutableSet.<InjectionPoint>of()));
++    } catch (Throwable e) {}
+   }
+ 
+   private static class LoggerFactory implements InternalFactory<Logger>, Provider<Logger> {
+@@ -271,6 +280,44 @@ final class InjectorShell {
+     }
+   }
+   
++  private static class SLF4JLoggerFactory implements InternalFactory<org.slf4j.Logger>, Provider<org.slf4j.Logger> {
++    private final Injector injector;
++
++    private org.slf4j.ILoggerFactory loggerFactory;
++
++    SLF4JLoggerFactory(Injector injector) {
++      this.injector = injector;
++    }
++
++    org.slf4j.ILoggerFactory loggerFactory() {
++      if (loggerFactory == null) {
++        try {
++          loggerFactory = injector.getInstance(org.slf4j.ILoggerFactory.class);
++        } catch (Throwable e) {}
++        if (loggerFactory == null) {
++          loggerFactory = org.slf4j.LoggerFactory.getILoggerFactory();
++        }
++      }
++      return loggerFactory;
++    }
++
++    public org.slf4j.Logger get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked) {
++      InjectionPoint injectionPoint = dependency.getInjectionPoint();
++      if (injectionPoint != null) {
++        return loggerFactory().getLogger(injectionPoint.getMember().getDeclaringClass().getName());
++      }
++      return loggerFactory().getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME);
++    }
++
++    public org.slf4j.Logger get() {
++      return loggerFactory().getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME);
++    }
++
++    public String toString() {
++      return "Provider<org.slf4j.Logger>";
++    }
++  }
++
+   private static void bindStage(InjectorImpl injector, Stage stage) {
+     Key<Stage> key = Key.get(Stage.class);
+     InstanceBindingImpl<Stage> stageBinding = new InstanceBindingImpl<Stage>(
+diff --git a/extensions/persist/pom.xml b/extensions/persist/pom.xml
+index 60e197d..f4fd4c5 100644
+--- a/extensions/persist/pom.xml
++++ b/extensions/persist/pom.xml
+@@ -29,7 +29,7 @@
+     <dependency>
+       <groupId>org.slf4j</groupId>
+       <artifactId>slf4j-simple</artifactId>
+-      <version>1.6.1</version>
++      <version>1.6.4</version>
+       <scope>test</scope>
+     </dependency>
+     <dependency>
+diff --git a/guice.iml b/guice.iml
+index 53b2d05..2abdea0 100644
+--- a/guice.iml
++++ b/guice.iml
+@@ -87,6 +87,15 @@
+         <SOURCES />
+       </library>
+     </orderEntry>
++    <orderEntry type="module-library" exported="">
++      <library>
++        <CLASSES>
++          <root url="jar://$MODULE_DIR$/lib/build/slf4j-api-1.6.4.jar!/" />
++        </CLASSES>
++        <JAVADOC />
++        <SOURCES />
++      </library>
++    </orderEntry>
+   </component>
+ </module>
+ 
+diff --git a/lib/build/slf4j-api-1.6.4.jar b/lib/build/slf4j-api-1.6.4.jar
+new file mode 100644
+index 0000000..76ef305
+Binary files /dev/null and b/lib/build/slf4j-api-1.6.4.jar differ
+diff --git a/pom.xml b/pom.xml
+index 91e2a83..9bfe995 100644
+--- a/pom.xml
++++ b/pom.xml
+@@ -270,7 +270,7 @@ See the Apache License Version 2.0 for the specific language governing permissio
+         </plugin>
+         <plugin>
+           <artifactId>maven-surefire-plugin</artifactId>
+-          <version>2.5</version>
++          <version>2.6</version>
+           <configuration>
+             <redirectTestOutputToFile>true</redirectTestOutputToFile>
+           </configuration>
diff --git a/PATCHES/GUICE_618_extensible_filter_pipeline.patch b/PATCHES/GUICE_618_extensible_filter_pipeline.patch
new file mode 100644
index 0000000..4cbb400
--- /dev/null
+++ b/PATCHES/GUICE_618_extensible_filter_pipeline.patch
@@ -0,0 +1,40 @@
+Description: Let sub-classes of GuiceFilter customize the servlet filter pipeline
+Author: Stuart McCulloch <mcculls at gmail.com>
+Bug-Google: http://code.google.com/p/google-guice/issues/detail?id=618
+Last-Update: 2012-07-29
+
+diff --git a/extensions/servlet/src/com/google/inject/servlet/FilterPipeline.java b/extensions/servlet/src/com/google/inject/servlet/FilterPipeline.java
+index 985064b..d85d97c 100755
+--- a/extensions/servlet/src/com/google/inject/servlet/FilterPipeline.java
++++ b/extensions/servlet/src/com/google/inject/servlet/FilterPipeline.java
+@@ -39,7 +39,7 @@ import javax.servlet.ServletResponse;
+  * @author dhanji at gmail.com (Dhanji R. Prasanna)
+  */
+ @ImplementedBy(DefaultFilterPipeline.class)
+-interface FilterPipeline {
++public interface FilterPipeline {
+   void initPipeline(ServletContext context) throws ServletException;
+   void destroyPipeline();
+ 
+diff --git a/extensions/servlet/src/com/google/inject/servlet/GuiceFilter.java b/extensions/servlet/src/com/google/inject/servlet/GuiceFilter.java
+index a439dba..2913e55 100644
+--- a/extensions/servlet/src/com/google/inject/servlet/GuiceFilter.java
++++ b/extensions/servlet/src/com/google/inject/servlet/GuiceFilter.java
+@@ -88,7 +88,7 @@ public class GuiceFilter implements Filter {
+     this(null);
+   }
+ 
+-  @Inject GuiceFilter(FilterPipeline filterPipeline) {
++  @Inject protected GuiceFilter(FilterPipeline filterPipeline) {
+     injectedPipeline = filterPipeline;
+   }
+ 
+@@ -99,7 +99,7 @@ public class GuiceFilter implements Filter {
+     // This can happen if you create many injectors and they all have their own
+     // servlet module. This is legal, caveat a small warning.
+     if (GuiceFilter.pipeline instanceof ManagedFilterPipeline) {
+-      LOGGER.warning(MULTIPLE_INJECTORS_WARNING);
++      LOGGER.config(MULTIPLE_INJECTORS_WARNING);
+     }
+ 
+     // We overwrite the default pipeline
diff --git a/PATCHES/GUICE_695_persistence_properties_provider.patch b/PATCHES/GUICE_695_persistence_properties_provider.patch
new file mode 100644
index 0000000..6cecacb
--- /dev/null
+++ b/PATCHES/GUICE_695_persistence_properties_provider.patch
@@ -0,0 +1,73 @@
+Description: Let clients bind lazy (provided) persistence properties
+Author: Stuart McCulloch <mcculls at gmail.com>
+Bug-Google: http://code.google.com/p/google-guice/issues/detail?id=695
+Last-Update: 2012-07-29
+
+diff --git a/extensions/persist/src/com/google/inject/persist/jpa/JpaPersistModule.java b/extensions/persist/src/com/google/inject/persist/jpa/JpaPersistModule.java
+index b318b27..9bbcbfb 100644
+--- a/extensions/persist/src/com/google/inject/persist/jpa/JpaPersistModule.java
++++ b/extensions/persist/src/com/google/inject/persist/jpa/JpaPersistModule.java
+@@ -37,6 +37,7 @@ import java.lang.reflect.Proxy;
+ import java.util.List;
+ import java.util.Properties;
+ 
++import javax.inject.Provider;
+ import javax.persistence.EntityManager;
+ import javax.persistence.EntityManagerFactory;
+ 
+@@ -55,6 +56,7 @@ public final class JpaPersistModule extends PersistModule {
+   }
+ 
+   private Properties properties;
++  private Class<? extends Provider<? extends Properties>> propertiesProvider;
+   private MethodInterceptor transactionInterceptor;
+ 
+   @Override protected void configurePersistence() {
+@@ -62,6 +64,8 @@ public final class JpaPersistModule extends PersistModule {
+ 
+     if (null != properties) {
+       bind(Properties.class).annotatedWith(Jpa.class).toInstance(properties);
++    } else if (null != propertiesProvider) {
++      bind(Properties.class).annotatedWith(Jpa.class).toProvider(propertiesProvider);
+     } else {
+       bind(Properties.class).annotatedWith(Jpa.class)
+           .toProvider(Providers.<Properties>of(null));
+@@ -99,6 +103,11 @@ public final class JpaPersistModule extends PersistModule {
+     return this;
+   }
+ 
++  public JpaPersistModule properties(Class<? extends Provider<? extends Properties>> provider) {
++    this.propertiesProvider = provider;
++    return this;
++  }
++
+   private final List<Class<?>> dynamicFinders = Lists.newArrayList();
+ 
+   /**
+diff --git a/extensions/persist/src/com/google/inject/persist/jpa/JpaPersistService.java b/extensions/persist/src/com/google/inject/persist/jpa/JpaPersistService.java
+index b8fe35c..68d76a7 100644
+--- a/extensions/persist/src/com/google/inject/persist/jpa/JpaPersistService.java
++++ b/extensions/persist/src/com/google/inject/persist/jpa/JpaPersistService.java
+@@ -42,11 +42,11 @@ class JpaPersistService implements Provider<EntityManager>, UnitOfWork, PersistS
+   private final ThreadLocal<EntityManager> entityManager = new ThreadLocal<EntityManager>();
+ 
+   private final String persistenceUnitName;
+-  private final Properties persistenceProperties;
++  private final Provider<Properties> persistenceProperties;
+ 
+   @Inject
+   public JpaPersistService(@Jpa String persistenceUnitName,
+-      @Nullable @Jpa Properties persistenceProperties) {
++      @Nullable @Jpa Provider<Properties> persistenceProperties) {
+     this.persistenceUnitName = persistenceUnitName;
+     this.persistenceProperties = persistenceProperties;
+   }
+@@ -95,7 +95,7 @@ class JpaPersistService implements Provider<EntityManager>, UnitOfWork, PersistS
+ 
+     if (null != persistenceProperties) {
+       this.emFactory = Persistence
+-          .createEntityManagerFactory(persistenceUnitName, persistenceProperties);
++          .createEntityManagerFactory(persistenceUnitName, persistenceProperties.get());
+     } else {
+       this.emFactory = Persistence.createEntityManagerFactory(persistenceUnitName);
+     }
diff --git a/PATCHES/GUICE_697_add_missing_headers.patch b/PATCHES/GUICE_697_add_missing_headers.patch
new file mode 100644
index 0000000..59ab455
--- /dev/null
+++ b/PATCHES/GUICE_697_add_missing_headers.patch
@@ -0,0 +1,199 @@
+Description: Add missing headers (codebase is licensed under the Apache License, Version 2.0)
+Author: Stuart McCulloch <mcculls at gmail.com>
+Bug-Google: http://code.google.com/p/google-guice/issues/detail?id=697
+Last-Update: 2013-08-08
+
+diff --git a/core/src/com/google/inject/internal/DelayedInitialize.java b/core/src/com/google/inject/internal/DelayedInitialize.java
+index 80e52d1..82a8463 100644
+--- a/core/src/com/google/inject/internal/DelayedInitialize.java
++++ b/core/src/com/google/inject/internal/DelayedInitialize.java
+@@ -1,4 +1,18 @@
+-// Copyright 2011 Google Inc. All Rights Reserved.
++/**
++ * Copyright (C) 2011 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;
+ 
+diff --git a/core/src/com/google/inject/internal/Nullability.java b/core/src/com/google/inject/internal/Nullability.java
+index f90342e..dcec9c9 100644
+--- a/core/src/com/google/inject/internal/Nullability.java
++++ b/core/src/com/google/inject/internal/Nullability.java
+@@ -1,3 +1,19 @@
++/**
++ * 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.internal;
+ 
+ import java.lang.annotation.Annotation;
+diff --git a/core/src/com/google/inject/spi/Toolable.java b/core/src/com/google/inject/spi/Toolable.java
+index 37ed41f..ffabd9d 100644
+--- a/core/src/com/google/inject/spi/Toolable.java
++++ b/core/src/com/google/inject/spi/Toolable.java
+@@ -1,3 +1,19 @@
++/**
++ * Copyright (C) 2010 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 java.lang.annotation.ElementType.METHOD;
+diff --git a/extensions/persist/src/com/google/inject/persist/finder/package-info.java b/extensions/persist/src/com/google/inject/persist/finder/package-info.java
+index 514a54f..279929c 100644
+--- a/extensions/persist/src/com/google/inject/persist/finder/package-info.java
++++ b/extensions/persist/src/com/google/inject/persist/finder/package-info.java
+@@ -1,4 +1,20 @@
+ /**
++ * Copyright (C) 2010 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.
++ */
++
++/**
+  * Dynamic Finder API for Guice Persist.
+  */
+ package com.google.inject.persist.finder;
+diff --git a/extensions/persist/src/com/google/inject/persist/jpa/package-info.java b/extensions/persist/src/com/google/inject/persist/jpa/package-info.java
+index 7a55893..f9e46da 100644
+--- a/extensions/persist/src/com/google/inject/persist/jpa/package-info.java
++++ b/extensions/persist/src/com/google/inject/persist/jpa/package-info.java
+@@ -1,4 +1,20 @@
+ /**
++ * Copyright (C) 2010 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.
++ */
++
++/**
+  * guice-persist's Java Persistence API (JPA) support.
+  */
+ package com.google.inject.persist.jpa;
+diff --git a/extensions/servlet/src/com/google/inject/servlet/ServletUtils.java b/extensions/servlet/src/com/google/inject/servlet/ServletUtils.java
+index 6a29425..88ecd31 100644
+--- a/extensions/servlet/src/com/google/inject/servlet/ServletUtils.java
++++ b/extensions/servlet/src/com/google/inject/servlet/ServletUtils.java
+@@ -1,4 +1,18 @@
+-// Copyright 2012 Google Inc. All Rights Reserved.
++/**
++ * Copyright (C) 2012 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.servlet;
+ 
+diff --git a/extensions/throwingproviders/src/com/google/inject/throwingproviders/CheckedProvideUtils.java b/extensions/throwingproviders/src/com/google/inject/throwingproviders/CheckedProvideUtils.java
+index 3d8a001..ca5ab77 100644
+--- a/extensions/throwingproviders/src/com/google/inject/throwingproviders/CheckedProvideUtils.java
++++ b/extensions/throwingproviders/src/com/google/inject/throwingproviders/CheckedProvideUtils.java
+@@ -1,4 +1,18 @@
+-// Copyright 2012 Google Inc. All Rights Reserved.
++/**
++ * Copyright (C) 2012 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.throwingproviders;
+ 
+diff --git a/extensions/throwingproviders/src/com/google/inject/throwingproviders/CheckedProviderWithDependencies.java b/extensions/throwingproviders/src/com/google/inject/throwingproviders/CheckedProviderWithDependencies.java
+index 4b87f40..79df84d 100644
+--- a/extensions/throwingproviders/src/com/google/inject/throwingproviders/CheckedProviderWithDependencies.java
++++ b/extensions/throwingproviders/src/com/google/inject/throwingproviders/CheckedProviderWithDependencies.java
+@@ -1,4 +1,18 @@
+-// Copyright 2012 Google Inc. All Rights Reserved.
++/**
++ * Copyright (C) 2012 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.throwingproviders;
+ 
diff --git a/PATCHES/GUICE_709_externalize_guava_dependency.patch b/PATCHES/GUICE_709_externalize_guava_dependency.patch
new file mode 100644
index 0000000..6681216
--- /dev/null
+++ b/PATCHES/GUICE_709_externalize_guava_dependency.patch
@@ -0,0 +1,843 @@
+Description: Pull out embedded Guava and expose it as an external dependency
+Author: Stuart McCulloch <mcculls at gmail.com>
+Bug-Google: http://code.google.com/p/google-guice/issues/detail?id=709
+Last-Update: 2014-01-17
+
+diff --git a/build.properties b/build.properties
+index a76a5ab..d8bf543 100644
+--- a/build.properties
++++ b/build.properties
+@@ -32,4 +32,4 @@ javadoc.packagenames=com.google.inject,com.google.inject.spi,\
+   com.google.inject.persist.jpa
+ test.class=com.google.inject.AllTests
+ module=com.google.inject
+-imports=!net.sf.cglib.*,!org.objectweb.asm.*,!com.google.common.*
++imports=!net.sf.cglib.*,!org.objectweb.asm.*
+diff --git a/build.xml b/build.xml
+index 51d2cb1..d9802ae 100644
+--- a/build.xml
++++ b/build.xml
+@@ -31,34 +31,34 @@
+         <ant antfile="extensions/grapher/build.xml" target="distjars" inheritAll="false"/>
+ 
+     <copy toDir="${build.dir}/dist"> 
+-      <fileset dir="extensions/servlet/build" includes="*.jar" excludes="*-with-deps.jar"/>
++      <fileset dir="extensions/servlet/build" includes="*.jar"/>
+     </copy>
+     <copy toDir="${build.dir}/dist"> 
+-      <fileset dir="extensions/spring/build" includes="*.jar" excludes="*-with-deps.jar"/>
++      <fileset dir="extensions/spring/build" includes="*.jar"/>
+     </copy>
+     <copy toDir="${build.dir}/dist">
+-      <fileset dir="extensions/struts2/build" includes="*.jar" excludes="*-with-deps.jar"/>
++      <fileset dir="extensions/struts2/build" includes="*.jar"/>
+     </copy>
+     <copy toDir="${build.dir}/dist">
+-      <fileset dir="extensions/assistedinject/build" includes="*.jar" excludes="*-with-deps.jar"/>
++      <fileset dir="extensions/assistedinject/build" includes="*.jar"/>
+     </copy>
+     <copy toDir="${build.dir}/dist">
+-      <fileset dir="extensions/jmx/build" includes="*.jar" excludes="*-with-deps.jar"/>
++      <fileset dir="extensions/jmx/build" includes="*.jar"/>
+     </copy>
+     <copy toDir="${build.dir}/dist">
+-      <fileset dir="extensions/jndi/build" includes="*.jar" excludes="*-with-deps.jar"/>
++      <fileset dir="extensions/jndi/build" includes="*.jar"/>
+     </copy>
+     <copy toDir="${build.dir}/dist">
+-      <fileset dir="extensions/throwingproviders/build" includes="*.jar" excludes="*-with-deps.jar"/>
++      <fileset dir="extensions/throwingproviders/build" includes="*.jar"/>
+     </copy>
+     <copy toDir="${build.dir}/dist">
+-      <fileset dir="extensions/multibindings/build" includes="*.jar" excludes="*-with-deps.jar"/>
++      <fileset dir="extensions/multibindings/build" includes="*.jar"/>
+     </copy>
+     <copy toDir="${build.dir}/dist">
+-      <fileset dir="extensions/persist/build" includes="*.jar" excludes="*-with-deps.jar"/>
++      <fileset dir="extensions/persist/build" includes="*.jar"/>
+     </copy>
+         <copy toDir="${build.dir}/dist">
+-          <fileset dir="extensions/grapher/build" includes="*.jar" excludes="*-with-deps.jar"/>
++          <fileset dir="extensions/grapher/build" includes="*.jar"/>
+         </copy>
+ 
+     <copy toDir="${build.dir}/dist" file="COPYING"/> 
+@@ -93,7 +93,7 @@
+   </target>
+ 
+   <target name="test.dist.run"
+-    depends="jar, test.compile-with-deps"
++    depends="jar, test.withdeps"
+     description="Execute JUnit tests against distribution jar with the given jvmarg.">
+     <java fork="true"
+         classname="junit.textui.TestRunner"
+@@ -102,11 +102,12 @@
+       <classpath>
+         <pathelement location="${build.dir}/guice-${version}-tests.jar"/>
+         <pathelement location="${build.dir}/dist/guice-${version}.jar"/>
++        <pathelement location="lib/javax.inject.jar"/>
+         <pathelement location="lib/aopalliance.jar"/>
++        <pathelement location="lib/guava-11.0.2.jar"/>
+         <pathelement location="lib/build/junit.jar"/>
+         <pathelement location="lib/build/servlet-api-2.5.jar"/>
+         <pathelement location="lib/build/easymock.jar"/>
+-        <pathelement location="lib/javax.inject.jar"/>
+         <pathelement location="lib/build/javax.inject-tck.jar"/>
+         <pathelement location="lib/build/bnd-0.0.384.jar"/>
+         <pathelement location="lib/build/felix-2.0.5.jar"/>
+@@ -134,7 +135,7 @@
+     <javadoc packagenames="com.google.*"
+                  docletpath="${jdiff.home}/jdiff.jar${path.separator}${jdiff.home}/xerces.jar"
+              maxmemory="512M"
+-             classpath="lib/javax.inject.jar${path.separator}lib/aopalliance.jar">
++             classpath="lib/javax.inject.jar${path.separator}lib/aopalliance.jar${path.separator}guava-11.0.2.jar">
+       <fileset dir="${src.dir}" defaultexcludes="yes">
+         <include name="com/google/**"/>
+         <exclude name="com/google/inject/internal/**"/>
+@@ -163,7 +164,7 @@
+                      docletpath="${jdiff.home}/jdiff.jar${path.separator}${jdiff.home}/xerces.jar"
+                  maxmemory="512M"
+                      sourcefiles="${jdiff.home}/Null.java"
+-                 classpath="lib/javax.inject.jar${path.separator}lib/aopalliance.jar">
++                 classpath="lib/javax.inject.jar${path.separator}lib/aopalliance.jar${path.separator}guava-11.0.2.jar">
+           <doclet name="jdiff.JDiff">
+                 <param name="-oldapi" value="${old.api}"/>
+                 <param name="-oldapidir" value="latest-api-diffs"/>             
+@@ -183,7 +184,7 @@
+              docletpath="lib/build/doclava.jar"
+              bootclasspath="${java.home}/lib/rt.jar"
+              maxmemory="512M"
+-             classpath="lib/javax.inject.jar${path.separator}lib/aopalliance.jar">
++             classpath="lib/javax.inject.jar${path.separator}lib/aopalliance.jar${path.separator}guava-11.0.2.jar">
+       <fileset dir="${src.dir}" defaultexcludes="yes">
+         <include name="com/google/**"/>
+         <exclude name="com/google/inject/internal/**"/>
+diff --git a/common.xml b/common.xml
+index 1a7add9..e54d19d 100644
+--- a/common.xml
++++ b/common.xml
+@@ -119,7 +119,7 @@
+       depends="source.jar, jar"
+       description="Build jar files"/>
+ 
+-  <target name="test.compile-with-deps" depends="test.compile"
++  <target name="test.withdeps" depends="test.compile"
+       description="Build a jar of tests with internal.util refocused.">
+     <mkdir dir="${build.dir}/dist"/>
+     <dirname property="common.basedir" file="${ant.file.common}"/>
+@@ -131,10 +131,6 @@
+       <rule pattern="net.sf.cglib.**.*" result="com.google.inject.internal.cglib. at 1.$@2"/>
+       <rule pattern="org.objectweb.asm.*" result="com.google.inject.internal.asm.$@1"/>
+       <rule pattern="org.objectweb.asm.**.*" result="com.google.inject.internal.asm. at 1.$@2"/>
+-      <rule pattern="com.google.common.*" result="com.google.inject.internal.guava.$@1"/>
+-      <rule pattern="com.google.common.**.*" result="com.google.inject.internal.guava. at 1.$@2"/>
+-      <rule pattern="javax.annotation.*.class" result="com.google.inject.internal.jsr305.$@1"/>
+-      <rule pattern="javax.annotation.**.*.class" result="com.google.inject.internal.jsr305. at 1.$@2"/>
+       <keep pattern="com.google.inject.**"/>
+       <keep pattern="com.googlecode.**"/>
+     </jarjar>
+@@ -150,35 +146,10 @@
+       <fileset dir="${build.dir}/classes"/>
+       <zipfileset src="${common.basedir}/lib/build/cglib-3.1.jar"/>
+       <zipfileset src="${common.basedir}/lib/build/asm-4.2.jar"/>
+-      <zipfileset src="${common.basedir}/lib/build/guava-11.0.1.jar"/>
+       <rule pattern="net.sf.cglib.*" result="com.google.inject.internal.cglib.$@1"/>
+       <rule pattern="net.sf.cglib.**.*" result="com.google.inject.internal.cglib. at 1.$@2"/>
+       <rule pattern="org.objectweb.asm.*" result="com.google.inject.internal.asm.$@1"/>
+       <rule pattern="org.objectweb.asm.**.*" result="com.google.inject.internal.asm. at 1.$@2"/>
+-      <rule pattern="com.google.common.*" result="com.google.inject.internal.guava.$@1"/>
+-      <rule pattern="com.google.common.**.*" result="com.google.inject.internal.guava. at 1.$@2"/>
+-      <keep pattern="com.google.inject.**"/>
+-      <!-- the servlet extension uses this but core doesn't,
+-           so we explicitly instruct the build to keep it. -->
+-      <keep pattern="com.google.common.base.Throwables"/>
+-    </jarjar>
+-  </target>
+-
+-  <target name="jar.withrenameddeps" depends="compile"
+-      description="Build jar with dependencies embedded.">
+-    <mkdir dir="${build.dir}/dist"/>
+-    <dirname property="common.basedir" file="${ant.file.common}"/>
+-    <taskdef name="jarjar" classname="com.tonicsystems.jarjar.JarJarTask"
+-        classpath="${common.basedir}/lib/build/jarjar-1.1.jar"/>
+-    <jarjar jarfile="${build.dir}/${ant.project.name}-with-deps.jar">
+-      <zipfileset src="${common.basedir}/lib/build/cglib-3.1.jar"><include name="LICENSE"/><include name="NOTICE"/></zipfileset>
+-      <fileset dir="${build.dir}/classes"/>
+-      <rule pattern="net.sf.cglib.*" result="com.google.inject.internal.cglib.$@1"/>
+-      <rule pattern="net.sf.cglib.**.*" result="com.google.inject.internal.cglib. at 1.$@2"/>
+-      <rule pattern="org.objectweb.asm.*" result="com.google.inject.internal.asm.$@1"/>
+-      <rule pattern="org.objectweb.asm.**.*" result="com.google.inject.internal.asm. at 1.$@2"/>
+-      <rule pattern="com.google.common.*" result="com.google.inject.internal.guava.$@1"/>
+-      <rule pattern="com.google.common.**.*" result="com.google.inject.internal.guava. at 1.$@2"/>
+       <keep pattern="com.google.inject.**"/>
+     </jarjar>
+   </target>
+diff --git a/copy.sh b/copy.sh
+deleted file mode 100755
+index a3e2202..0000000
+--- a/copy.sh
++++ /dev/null
+@@ -1,54 +0,0 @@
+-#!/bin/sh
+-# Copies classes into Guice's internal package.
+-
+-client=/usr/local/google/clients/collect/google3
+-
+-srcdir=core/src/com/google/inject/internal
+-testdir=core/test/com/google/inject/internal
+-
+-filter() {
+-  sed 's/com.google.common.base.internal/com.google.inject.internal/' | \
+-  sed 's/com.google.common.base/com.google.inject.internal/' | \
+-  sed 's/com.google.common.collect/com.google.inject.internal/'
+-}
+-
+-copy() {
+-  inFile=$1;
+-  fileName=`basename $inFile`
+-  dest=$2
+-  destpath=$dest/$fileName
+-  filter < $client/${inFile} > $destpath
+-}
+-
+-commonpath=java/com/google/common
+-
+-copy $commonpath/collect/ComputationException.java $srcdir
+-copy $commonpath/collect/AsynchronousComputationException.java $srcdir
+-copy $commonpath/collect/CustomConcurrentHashMap.java $srcdir
+-copy $commonpath/collect/ExpirationTimer.java $srcdir
+-copy $commonpath/collect/MapMaker.java $srcdir
+-copy $commonpath/collect/NullOutputException.java $srcdir
+-copy $commonpath/base/Function.java $srcdir
+-copy $commonpath/base/Nullable.java $srcdir
+-copy $commonpath/base/FinalizableReference.java $srcdir
+-copy $commonpath/base/FinalizableReferenceQueue.java $srcdir
+-copy $commonpath/base/internal/Finalizer.java $srcdir
+-copy $commonpath/base/FinalizableWeakReference.java $srcdir
+-copy $commonpath/base/FinalizableSoftReference.java $srcdir
+-copy $commonpath/base/FinalizablePhantomReference.java $srcdir
+-
+-commontestspath=javatests/com/google/common
+-
+-copy $commontestspath/base/FinalizableReferenceQueueTest.java $testdir
+-copy $commontestspath/collect/MapMakerTestSuite.java $testdir
+-copy $commontestspath/collect/Jsr166HashMap.java $testdir
+-copy $commontestspath/collect/Jsr166HashMapTest.java $testdir
+-copy $commonpath/collect/ForwardingConcurrentMap.java $testdir
+-copy $commonpath/collect/ForwardingMap.java $testdir
+-copy $commonpath/collect/ForwardingCollection.java $testdir
+-copy $commonpath/collect/ForwardingObject.java $testdir
+-copy $commonpath/collect/ForwardingSet.java $testdir
+-copy $commonpath/collect/ForwardingMap.java $testdir
+-copy $commonpath/base/Preconditions.java $testdir
+-
+-chmod +w -R $srcdir $testdir
+diff --git a/core/pom.xml b/core/pom.xml
+index 97c8e60..1ea34a2 100644
+--- a/core/pom.xml
++++ b/core/pom.xml
+@@ -13,11 +13,6 @@
+ 
+   <name>Google Guice - Core Library</name>
+ 
+-  <properties>
+-    <cglib.version>3.0</cglib.version>
+-    <asm.version>4.0</asm.version>
+-  </properties>
+-
+   <dependencies>
+     <dependency>
+       <groupId>javax.inject</groupId>
+@@ -32,17 +27,17 @@
+     <dependency>
+       <groupId>com.google.guava</groupId>
+       <artifactId>guava</artifactId>
+-      <version>11.0.1</version>
++      <version>11.0.2</version>
+     </dependency>
++    <!--
++     | CGLIB is embedded by default by the JarJar build profile
++    -->
+     <dependency>
+       <groupId>cglib</groupId>
+       <artifactId>cglib</artifactId>
+-      <version>${cglib.version}</version>
+-    </dependency>
+-    <dependency>
+-      <groupId>org.ow2.asm</groupId>
+-      <artifactId>asm-util</artifactId>
+-      <version>${asm.version}</version>
++      <version>3.1</version>
++      <optional>true</optional>
++      <scope>provided</scope>
+     </dependency>
+     <dependency>
+       <groupId>javax.inject</groupId>
+@@ -207,7 +202,7 @@
+     </profile>
+     <profile>
+       <!--
+-       | JarJar build profile: re-package ASM and CGLIB classes under the Guice namespace
++       | JarJar build profile: embed CGLIB (and ASM) classes under Guice namespace
+       -->
+       <id>guice.with.jarjar</id>
+       <activation>
+@@ -216,52 +211,87 @@
+           <value>!false</value>
+         </property>
+       </activation>
+-      <dependencies>
+-        <!--
+-         | Mark as optional: embedded by JarJar
+-        -->
+-        <dependency>
+-          <groupId>cglib</groupId>
+-          <artifactId>cglib</artifactId>
+-          <version>${cglib.version}</version>
+-          <optional>true</optional>
+-        </dependency>
+-        <dependency>
+-          <groupId>org.ow2.asm</groupId>
+-          <artifactId>asm-util</artifactId>
+-          <version>${asm.version}</version>
+-          <optional>true</optional>
+-        </dependency>
+-      </dependencies>
+       <build>
+         <plugins>
+           <plugin>
+             <groupId>org.sonatype.plugins</groupId>
+             <artifactId>jarjar-maven-plugin</artifactId>
++            <version>1.8</version>
+             <configuration>
++              <overwrite>true</overwrite>
+               <includes>
+                 <include>*:asm*</include>
+                 <include>*:cglib</include>
+-                <include>*:guava</include>
+-                <include>*:jsr305</include>
+               </includes>
++              <rules>
++                <rule>
++                  <pattern>net.sf.cglib.*</pattern>
++                  <result>com.google.inject.internal.cglib.$@1</result>
++                </rule>
++                <rule>
++                  <pattern>net.sf.cglib.**.*</pattern>
++                  <result>com.google.inject.internal.cglib. at 1.$@2</result>
++                </rule>
++                <rule>
++                  <pattern>org.objectweb.asm.*</pattern>
++                  <result>com.google.inject.internal.asm.$@1</result>
++                </rule>
++                <rule>
++                  <pattern>org.objectweb.asm.**.*</pattern>
++                  <result>com.google.inject.internal.asm. at 1.$@2</result>
++                </rule>
++                <keep>
++                  <pattern>com.google.inject.**</pattern>
++                </keep>
++                <keep>
++                  <pattern>com.googlecode.**</pattern>
++                </keep>
++              </rules>
+             </configuration>
+-          </plugin>
+-          <plugin>
+             <!--
+-             | Package the original non-JarJar'd classes so extensions can compile against them
++             | JarJar all classes before running tests
+             -->
++            <executions>
++              <execution>
++                <id>jarjar-classes</id>
++                <phase>process-test-classes</phase>
++                <goals>
++                  <goal>jarjar</goal>
++                </goals>
++                <configuration>
++                  <input>{classes}</input>
++                </configuration>
++              </execution>
++              <execution>
++                <id>jarjar-test-classes</id>
++                <phase>process-test-classes</phase>
++                <goals>
++                  <goal>jarjar</goal>
++                </goals>
++                <configuration>
++                  <input>{test-classes}</input>
++                </configuration>
++              </execution>
++            </executions>
++          </plugin>
++          <!--
++           | Attach original (non-JarJar'd) classes
++          -->
++          <plugin>
+             <artifactId>maven-jar-plugin</artifactId>
+             <executions>
+               <execution>
+-                <id>no_deps</id>
++                <id>classes</id>
+                 <phase>package</phase>
+                 <goals>
+                   <goal>jar</goal>
+                 </goals>
+                 <configuration>
+                   <classesDirectory>${project.build.directory}/original-classes</classesDirectory>
+-                  <classifier>no_deps</classifier>
++                  <classifier>classes</classifier>
++                  <archive>
++                    <manifestFile combine.self="override" />
++                  </archive>
+                 </configuration>
+               </execution>
+             </executions>
+@@ -269,6 +299,43 @@
+         </plugins>
+       </build>
+     </profile>
++    <profile>
++      <!--
++       | m2e profile - enable use of JarJar inside Eclipse
++      -->
++      <id>m2e</id>
++      <activation>
++        <property>
++          <name>m2e.version</name>
++        </property>
++      </activation>
++      <build>
++        <pluginManagement>
++          <plugins>
++            <plugin>
++              <groupId>org.eclipse.m2e</groupId>
++              <artifactId>lifecycle-mapping</artifactId>
++              <version>1.0.0</version>
++              <configuration>
++                <lifecycleMappingMetadata>
++                  <pluginExecutions>
++                    <pluginExecution>
++                      <pluginExecutionFilter>
++                        <groupId>org.sonatype.plugins</groupId>
++                        <artifactId>jarjar-maven-plugin</artifactId>
++                        <versionRange>[1.4,)</versionRange>
++                        <goals><goal>jarjar</goal></goals>
++                      </pluginExecutionFilter>
++                      <action><execute /></action>
++                    </pluginExecution>
++                  </pluginExecutions>
++                </lifecycleMappingMetadata>
++              </configuration>
++            </plugin>
++          </plugins>
++        </pluginManagement>
++      </build>
++    </profile>
+   </profiles>
+ 
+ </project>
+diff --git a/core/test/com/googlecode/guice/OSGiContainerTest.java b/core/test/com/googlecode/guice/OSGiContainerTest.java
+index 77ab8fa..6cb2b1b 100644
+--- a/core/test/com/googlecode/guice/OSGiContainerTest.java
++++ b/core/test/com/googlecode/guice/OSGiContainerTest.java
+@@ -58,6 +58,7 @@ public class OSGiContainerTest
+   static final String AOPALLIANCE_JAR = System.getProperty("aopalliance.jar", "lib/aopalliance.jar");
+ /*end[AOP]*/
+   static final String JAVAX_INJECT_JAR = System.getProperty("javax.inject.jar", "lib/javax.inject.jar");
++  static final String GUAVA_JAR = System.getProperty("guava.jar", "lib/guava-11.0.2.jar");
+ 
+   // dynamically build test bundles
+   @Override protected void setUp()
+@@ -71,6 +72,7 @@ public class OSGiContainerTest
+     assertTrue(failMsg(), new File(AOPALLIANCE_JAR).isFile());
+ /*end[AOP]*/
+     assertTrue(failMsg(), new File(JAVAX_INJECT_JAR).isFile());
++    assertTrue(failMsg(), new File(GUAVA_JAR).isFile());
+ 
+     Properties instructions = new Properties();
+ 
+@@ -86,6 +88,12 @@ public class OSGiContainerTest
+     buildBundle("javax.inject", instructions, JAVAX_INJECT_JAR);
+     instructions.clear();
+ 
++    // early versions of guava did not ship with OSGi metadata
++    instructions.setProperty("Export-Package", "com.google.common.*");
++    instructions.setProperty("Import-Package", "*;resolution:=optional");
++    buildBundle("guava", instructions, GUAVA_JAR);
++    instructions.clear();
++
+     // strict imports to make sure test bundle only has access to these packages
+     instructions.setProperty("Import-Package", "org.osgi.framework,"
+ /*if[AOP]*/
+@@ -144,6 +152,7 @@ public class OSGiContainerTest
+       systemContext.installBundle("reference:file:" + BUILD_TEST_DIR + "/aopalliance.jar");
+ /*end[AOP]*/
+       systemContext.installBundle("reference:file:" + BUILD_TEST_DIR + "/javax.inject.jar");
++      systemContext.installBundle("reference:file:" + BUILD_TEST_DIR + "/guava.jar");
+       systemContext.installBundle("reference:file:" + GUICE_JAR);
+       systemContext.installBundle("reference:file:" + BUILD_TEST_DIR + "/osgitests.jar").start();
+ 
+diff --git a/extensions/assistedinject/build.xml b/extensions/assistedinject/build.xml
+index 16557de..459ff9e 100644
+--- a/extensions/assistedinject/build.xml
++++ b/extensions/assistedinject/build.xml
+@@ -10,11 +10,10 @@
+     <pathelement path="../../build/classes"/>
+   </path>
+ 
+-  <target name="jar" depends="jar.withrenameddeps, manifest" description="Build jar.">
++  <target name="jar" depends="compile, manifest" description="Build jar.">
+     <jar destfile="${build.dir}/${ant.project.name}-${version}.jar"
+         manifest="${build.dir}/META-INF/MANIFEST.MF">
+-      <zipfileset src="${build.dir}/${ant.project.name}-with-deps.jar"
+-          excludes="com/google/inject/internal/**"/>
++      <fileset dir="${build.dir}/classes"/>
+     </jar>
+   </target>
+ 
+diff --git a/extensions/grapher/build.xml b/extensions/grapher/build.xml
+index d36fdcd..d5524dd 100644
+--- a/extensions/grapher/build.xml
++++ b/extensions/grapher/build.xml
+@@ -10,11 +10,10 @@
+     <pathelement path="../../build/classes"/>
+   </path>
+ 
+-  <target name="jar" depends="jar.withrenameddeps, manifest" description="Build jar.">
++  <target name="jar" depends="compile, manifest" description="Build jar.">
+     <jar destfile="${build.dir}/${ant.project.name}-${version}.jar"
+         manifest="${build.dir}/META-INF/MANIFEST.MF">
+-      <zipfileset src="${build.dir}/${ant.project.name}-with-deps.jar"
+-          excludes="com/google/inject/internal/**"/>
++      <fileset dir="${build.dir}/classes"/>
+     </jar>
+   </target>
+ 
+diff --git a/extensions/jmx/build.xml b/extensions/jmx/build.xml
+index 1bdcb97..8a99b77 100644
+--- a/extensions/jmx/build.xml
++++ b/extensions/jmx/build.xml
+@@ -10,11 +10,10 @@
+     <pathelement path="../../build/classes"/>
+   </path>
+ 
+-  <target name="jar" depends="jar.withrenameddeps, manifest" description="Build jar.">
++  <target name="jar" depends="compile, manifest" description="Build jar.">
+     <jar destfile="${build.dir}/${ant.project.name}-${version}.jar"
+         manifest="${build.dir}/META-INF/MANIFEST.MF">
+-      <zipfileset src="${build.dir}/${ant.project.name}-with-deps.jar"
+-          excludes="com/google/inject/internal/**"/>
++      <fileset dir="${build.dir}/classes"/>
+     </jar>
+   </target>
+ 
+diff --git a/extensions/jndi/build.xml b/extensions/jndi/build.xml
+index 14eaa8b..5c366a2 100644
+--- a/extensions/jndi/build.xml
++++ b/extensions/jndi/build.xml
+@@ -10,11 +10,10 @@
+     <pathelement path="../../build/classes"/>
+   </path>
+ 
+-  <target name="jar" depends="jar.withrenameddeps, manifest" description="Build jar.">
++  <target name="jar" depends="compile, manifest" description="Build jar.">
+     <jar destfile="${build.dir}/${ant.project.name}-${version}.jar"
+         manifest="${build.dir}/META-INF/MANIFEST.MF">
+-      <zipfileset src="${build.dir}/${ant.project.name}-with-deps.jar"
+-          excludes="com/google/inject/internal/**"/>
++      <fileset dir="${build.dir}/classes"/>
+     </jar>
+   </target>
+ 
+diff --git a/extensions/multibindings/build.xml b/extensions/multibindings/build.xml
+index 7a23462..2f72159 100644
+--- a/extensions/multibindings/build.xml
++++ b/extensions/multibindings/build.xml
+@@ -10,11 +10,10 @@
+     <pathelement path="../../build/classes"/>
+   </path>
+ 
+-  <target name="jar" depends="jar.withrenameddeps, manifest" description="Build jar.">
++  <target name="jar" depends="compile, manifest" description="Build jar.">
+     <jar destfile="${build.dir}/${ant.project.name}-${version}.jar"
+         manifest="${build.dir}/META-INF/MANIFEST.MF">
+-      <zipfileset src="${build.dir}/${ant.project.name}-with-deps.jar"
+-          excludes="com/google/inject/internal/**"/>
++      <fileset dir="${build.dir}/classes"/>
+     </jar>
+   </target>
+ 
+diff --git a/extensions/persist/build.xml b/extensions/persist/build.xml
+index 9be40c8..764c726 100644
+--- a/extensions/persist/build.xml
++++ b/extensions/persist/build.xml
+@@ -11,11 +11,10 @@
+     <pathelement path="../../build/classes"/>
+   </path>
+ 
+-  <target name="jar" depends="jar.withrenameddeps, manifest" description="Build jar.">
++  <target name="jar" depends="compile, manifest" description="Build jar.">
+     <jar destfile="${build.dir}/${ant.project.name}-${version}.jar"
+         manifest="${build.dir}/META-INF/MANIFEST.MF">
+-      <zipfileset src="${build.dir}/${ant.project.name}-with-deps.jar"
+-          excludes="com/google/inject/internal/**"/>
++      <fileset dir="${build.dir}/classes"/>
+     </jar>
+   </target>
+ 
+diff --git a/extensions/pom.xml b/extensions/pom.xml
+index bd706c7..c1fb034 100644
+--- a/extensions/pom.xml
++++ b/extensions/pom.xml
+@@ -43,6 +43,7 @@
+       <groupId>com.google.inject</groupId>
+       <artifactId>guice</artifactId>
+       <version>${project.version}</version>
++      <scope>provided</scope>
+     </dependency>
+     <!--
+      | Some extension tests depend on the core tests
+@@ -101,41 +102,22 @@
+   <profiles>
+     <profile>
+       <!--
+-       | JarJar build profile: re-package ASM and CGLIB references under the Guice namespace
++       | If JarJar build profile is disabled we need CGLIB during tests
+       -->
+-      <id>guice.with.jarjar</id>
+       <activation>
+         <property>
+           <name>guice.with.jarjar</name>
+-          <value>!false</value>
++          <value>false</value>
+         </property>
+       </activation>
+       <dependencies>
+-        <!--
+-         | Extensions compile first against the non-JarJar'd core - and are then JarJar'd themselves
+-         | (optional dependency so it doesn't leak to client projects that depend on Guice artifacts)
+-        -->
+         <dependency>
+-          <groupId>com.google.inject</groupId>
+-          <artifactId>guice</artifactId>
+-          <version>${project.version}</version>
+-          <classifier>no_deps</classifier>
+-          <optional>true</optional>
++          <groupId>cglib</groupId>
++          <artifactId>cglib</artifactId>
++          <version>3.1</version>
++          <scope>test</scope>
+         </dependency>
+       </dependencies>
+-      <build>
+-        <plugins>
+-          <plugin>
+-            <groupId>org.sonatype.plugins</groupId>
+-            <artifactId>jarjar-maven-plugin</artifactId>
+-            <configuration>
+-              <excludes>
+-                <exclude>*:*</exclude>
+-              </excludes>
+-            </configuration>
+-          </plugin>
+-        </plugins>
+-      </build>
+     </profile>
+   </profiles>
+ 
+diff --git a/extensions/servlet/build.xml b/extensions/servlet/build.xml
+index d7a10e1..5bee7e5 100644
+--- a/extensions/servlet/build.xml
++++ b/extensions/servlet/build.xml
+@@ -12,11 +12,10 @@
+     <pathelement path="../../build/classes"/>
+   </path>
+ 
+-  <target name="jar" depends="jar.withrenameddeps, manifest" description="Build jar.">
++  <target name="jar" depends="compile, manifest" description="Build jar.">
+     <jar destfile="${build.dir}/${ant.project.name}-${version}.jar"
+         manifest="${build.dir}/META-INF/MANIFEST.MF">
+-      <zipfileset src="${build.dir}/${ant.project.name}-with-deps.jar"
+-          excludes="com/google/inject/internal/**"/>
++      <fileset dir="${build.dir}/classes"/>
+     </jar>
+   </target>
+ 
+diff --git a/extensions/spring/build.xml b/extensions/spring/build.xml
+index 16c8299..3051929 100644
+--- a/extensions/spring/build.xml
++++ b/extensions/spring/build.xml
+@@ -10,11 +10,10 @@
+     <pathelement path="../../build/classes"/>
+   </path>
+ 
+-  <target name="jar" depends="jar.withrenameddeps, manifest" description="Build jar.">
++  <target name="jar" depends="compile, manifest" description="Build jar.">
+     <jar destfile="${build.dir}/${ant.project.name}-${version}.jar"
+         manifest="${build.dir}/META-INF/MANIFEST.MF">
+-      <zipfileset src="${build.dir}/${ant.project.name}-with-deps.jar"
+-          excludes="com/google/inject/internal/**"/>
++      <fileset dir="${build.dir}/classes"/>
+     </jar>
+   </target>
+ 
+diff --git a/extensions/struts2/build.xml b/extensions/struts2/build.xml
+index 820dfa2..6463274 100644
+--- a/extensions/struts2/build.xml
++++ b/extensions/struts2/build.xml
+@@ -11,11 +11,10 @@
+     <fileset dir="../servlet/build" includes="*.jar"/>
+   </path>
+ 
+-  <target name="jar" depends="jar.withrenameddeps, manifest" description="Build jar.">
++  <target name="jar" depends="compile, manifest" description="Build jar.">
+     <jar destfile="${build.dir}/${ant.project.name}-${version}.jar"
+         manifest="${build.dir}/META-INF/MANIFEST.MF">
+-      <zipfileset src="${build.dir}/${ant.project.name}-with-deps.jar"
+-          excludes="com/google/inject/internal/**"/>
++      <fileset dir="${build.dir}/classes"/>
+     </jar>
+   </target>
+ 
+diff --git a/extensions/throwingproviders/build.xml b/extensions/throwingproviders/build.xml
+index 9d36431..27c20e0 100644
+--- a/extensions/throwingproviders/build.xml
++++ b/extensions/throwingproviders/build.xml
+@@ -10,11 +10,10 @@
+     <pathelement path="../../build/classes"/>
+   </path>
+ 
+-  <target name="jar" depends="jar.withrenameddeps, manifest" description="Build jar.">
++  <target name="jar" depends="compile, manifest" description="Build jar.">
+     <jar destfile="${build.dir}/${ant.project.name}-${version}.jar"
+         manifest="${build.dir}/META-INF/MANIFEST.MF">
+-      <zipfileset src="${build.dir}/${ant.project.name}-with-deps.jar"
+-          excludes="com/google/inject/internal/**"/>
++      <fileset dir="${build.dir}/classes"/>
+     </jar>
+   </target>
+ 
+diff --git a/guice.iml b/guice.iml
+index 53b2d05..8dd0ae2 100644
+--- a/guice.iml
++++ b/guice.iml
+@@ -87,6 +87,15 @@
+         <SOURCES />
+       </library>
+     </orderEntry>
++    <orderEntry type="module-library" exported="">
++      <library>
++        <CLASSES>
++          <root url="jar://$MODULE_DIR$/lib/guava-11.0.2.jar!/" />
++        </CLASSES>
++        <JAVADOC />
++        <SOURCES />
++      </library>
++    </orderEntry>
+   </component>
+ </module>
+ 
+diff --git a/lib/build/guava-11.0.1.jar b/lib/build/guava-11.0.1.jar
+deleted file mode 100644
+index af4a383..0000000
+Binary files a/lib/build/guava-11.0.1.jar and /dev/null differ
+diff --git a/lib/guava-11.0.2.jar b/lib/guava-11.0.2.jar
+new file mode 100644
+index 0000000..c8c8d5d
+Binary files /dev/null and b/lib/guava-11.0.2.jar differ
+diff --git a/pom.xml b/pom.xml
+index 0805839..21ea053 100644
+--- a/pom.xml
++++ b/pom.xml
+@@ -196,78 +196,6 @@ See the Apache License Version 2.0 for the specific language governing permissio
+             </execution>
+           </executions>
+         </plugin>
+-        <!--
+-         | Shared JarJar configuration
+-        -->
+-        <plugin>
+-          <groupId>org.sonatype.plugins</groupId>
+-          <artifactId>jarjar-maven-plugin</artifactId>
+-          <version>1.4</version>
+-          <configuration>
+-            <rules>
+-              <rule>
+-                <pattern>net.sf.cglib.*</pattern>
+-                <result>com.google.inject.internal.cglib.$@1</result>
+-              </rule>
+-              <rule>
+-                <pattern>net.sf.cglib.**.*</pattern>
+-                <result>com.google.inject.internal.cglib. at 1.$@2</result>
+-              </rule>
+-              <rule>
+-                <pattern>org.objectweb.asm.*</pattern>
+-                <result>com.google.inject.internal.asm.$@1</result>
+-              </rule>
+-              <rule>
+-                <pattern>org.objectweb.asm.**.*</pattern>
+-                <result>com.google.inject.internal.asm. at 1.$@2</result>
+-              </rule>
+-              <rule>
+-                <pattern>com.google.common.*</pattern>
+-                <result>com.google.inject.internal.guava.$@1</result>
+-              </rule>
+-              <rule>
+-                <pattern>com.google.common.**.*</pattern>
+-                <result>com.google.inject.internal.guava. at 1.$@2</result>
+-              </rule>
+-              <keep>
+-                <pattern>com.google.inject.**</pattern>
+-              </keep>
+-              <keep>
+-                <pattern>com.googlecode.**</pattern>
+-              </keep>
+-              <keep>
+-                <!-- the servlet extension uses this but core doesn't,
+-                     so we explicitly instruct the build to keep it. -->
+-                <pattern>com.google.common.base.Throwables</pattern>
+-              </keep>
+-            </rules>
+-          </configuration>
+-          <!--
+-           | JarJar all classes before running tests
+-          -->
+-          <executions>
+-            <execution>
+-              <id>jarjar-classes</id>
+-              <phase>process-test-classes</phase>
+-              <goals>
+-                <goal>jarjar</goal>
+-              </goals>
+-              <configuration>
+-                <input>{classes}</input>
+-              </configuration>
+-            </execution>
+-            <execution>
+-              <id>jarjar-test-classes</id>
+-              <phase>process-test-classes</phase>
+-              <goals>
+-                <goal>jarjar</goal>
+-              </goals>
+-              <configuration>
+-                <input>{test-classes}</input>
+-              </configuration>
+-            </execution>
+-          </executions>
+-        </plugin>
+         <plugin>
+           <artifactId>maven-surefire-plugin</artifactId>
+           <version>2.5</version>
+@@ -332,6 +260,10 @@ See the Apache License Version 2.0 for the specific language governing permissio
+                 Ignore-Package,Bnd-LastModified
+               </_removeheaders>
+             </instructions>
++            <!--
++             | Exclude from version calculations, as it doesn't use semantic versioning
++            -->
++            <excludeDependencies>guava</excludeDependencies>
+           </configuration>
+           <executions>
+             <execution>
diff --git a/PATCHES/GUICE_718_configurable_annotation_check.patch b/PATCHES/GUICE_718_configurable_annotation_check.patch
new file mode 100644
index 0000000..58a20f1
--- /dev/null
+++ b/PATCHES/GUICE_718_configurable_annotation_check.patch
@@ -0,0 +1,45 @@
+Description: Support disabling of misplaced annotation check for legacy components
+Author: Stuart McCulloch <mcculls at gmail.com>
+Bug-Google: http://code.google.com/p/google-guice/issues/detail?id=718
+Last-Update: 2012-07-29
+
+diff --git a/core/src/com/google/inject/internal/AbstractBindingProcessor.java b/core/src/com/google/inject/internal/AbstractBindingProcessor.java
+index 72dae2b..bbb9930 100644
+--- a/core/src/com/google/inject/internal/AbstractBindingProcessor.java
++++ b/core/src/com/google/inject/internal/AbstractBindingProcessor.java
+@@ -39,6 +39,20 @@ import java.util.Set;
+  */
+ abstract class AbstractBindingProcessor extends AbstractProcessor {
+ 
++//------------------------------------------------------------------------------
++  private static final boolean DISABLE_MISPLACED_ANNOTATION_CHECK;
++  static {
++    boolean disableCheck;
++    try {
++      disableCheck = Boolean.parseBoolean(System.getProperty(
++          "guice.disable.misplaced.annotation.check", "false"));
++    } catch (Throwable e) {
++      disableCheck = false;
++    }
++    DISABLE_MISPLACED_ANNOTATION_CHECK = disableCheck;
++  }
++//------------------------------------------------------------------------------
++
+   // 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.
+@@ -127,8 +141,14 @@ abstract class AbstractBindingProcessor extends AbstractProcessor {
+   }
+   
+   private <T> void validateKey(Object source, Key<T> key) {
++//------------------------------------------------------------------------------
++if (!DISABLE_MISPLACED_ANNOTATION_CHECK) {
++//------------------------------------------------------------------------------
+     Annotations.checkForMisplacedScopeAnnotations(
+         key.getTypeLiteral().getRawType(), source, errors);
++//------------------------------------------------------------------------------
++}
++//------------------------------------------------------------------------------
+   }
+   
+   /** 
diff --git a/PATCHES/GUICE_759_upgrade_to_asm5.patch b/PATCHES/GUICE_759_upgrade_to_asm5.patch
new file mode 100644
index 0000000..37e70b7
--- /dev/null
+++ b/PATCHES/GUICE_759_upgrade_to_asm5.patch
@@ -0,0 +1,74 @@
+Description: Upgrade ASM to 5.0_BETA (via patched cglib)
+Author: Stuart McCulloch <mcculls at gmail.com>
+Bug-Google: http://code.google.com/p/google-guice/issues/detail?id=759
+Last-Update: 2013-12-10
+
+diff --git a/build.xml b/build.xml
+index d9802ae..ac19447 100644
+--- a/build.xml
++++ b/build.xml
+@@ -252,7 +252,7 @@
+       <arg value="-DNO_AOP" />
+     </munge>
+     <replace file="build/no_aop/common.xml" value="">
+-      <replacetoken><![CDATA[<zipfileset src="${common.basedir}/lib/build/asm-4.2.jar"/>]]></replacetoken>
++      <replacetoken><![CDATA[<zipfileset src="${common.basedir}/lib/build/asm-5.0_BETA.jar"/>]]></replacetoken>
+     </replace>
+     <replace file="build/no_aop/common.xml" value="">
+       <replacetoken><![CDATA[<zipfileset src="${common.basedir}/lib/build/cglib-3.1.jar"/>]]></replacetoken>
+diff --git a/common.xml b/common.xml
+index e54d19d..d23fbe9 100644
+--- a/common.xml
++++ b/common.xml
+@@ -145,7 +145,7 @@
+     <jarjar jarfile="${build.dir}/${ant.project.name}-with-deps.jar">
+       <fileset dir="${build.dir}/classes"/>
+       <zipfileset src="${common.basedir}/lib/build/cglib-3.1.jar"/>
+-      <zipfileset src="${common.basedir}/lib/build/asm-4.2.jar"/>
++      <zipfileset src="${common.basedir}/lib/build/asm-5.0_BETA.jar"/>
+       <rule pattern="net.sf.cglib.*" result="com.google.inject.internal.cglib.$@1"/>
+       <rule pattern="net.sf.cglib.**.*" result="com.google.inject.internal.cglib. at 1.$@2"/>
+       <rule pattern="org.objectweb.asm.*" result="com.google.inject.internal.asm.$@1"/>
+diff --git a/core/pom.xml b/core/pom.xml
+index 7606276..5346795 100644
+--- a/core/pom.xml
++++ b/core/pom.xml
+@@ -33,6 +33,13 @@
+      | CGLIB is embedded by default by the JarJar build profile
+     -->
+     <dependency>
++      <groupId>org.ow2.asm</groupId>
++      <artifactId>asm</artifactId>
++      <version>5.0_BETA</version>
++      <optional>true</optional>
++      <scope>provided</scope>
++    </dependency>
++    <dependency>
+       <groupId>cglib</groupId>
+       <artifactId>cglib</artifactId>
+       <version>3.1</version>
+diff --git a/extensions/pom.xml b/extensions/pom.xml
+index f50bf3c..150a5d6 100644
+--- a/extensions/pom.xml
++++ b/extensions/pom.xml
+@@ -111,6 +111,12 @@
+       </activation>
+       <dependencies>
+         <dependency>
++          <groupId>org.ow2.asm</groupId>
++          <artifactId>asm</artifactId>
++          <version>5.0_BETA</version>
++          <scope>test</scope>
++        </dependency>
++        <dependency>
+           <groupId>cglib</groupId>
+           <artifactId>cglib</artifactId>
+           <version>3.1</version>
+diff --git a/lib/build/asm-4.2.jar b/lib/build/asm-4.2.jar
+deleted file mode 100644
+index 693913d..0000000
+Binary files a/lib/build/asm-4.2.jar and /dev/null differ
+diff --git a/lib/build/asm-5.0_BETA.jar b/lib/build/asm-5.0_BETA.jar
+new file mode 100644
+index 0000000..2e26615
+Binary files /dev/null and b/lib/build/asm-5.0_BETA.jar differ
diff --git a/PATCHES/SISUFY.patch b/PATCHES/SISUFY.patch
new file mode 100644
index 0000000..2d09b88
--- /dev/null
+++ b/PATCHES/SISUFY.patch
@@ -0,0 +1,590 @@
+Description: Rename and relabel Google-Guice build to make it clear this is a vendor branch
+Author: Stuart McCulloch <mcculls at gmail.com>
+Last-Update: 2012-12-09
+
+diff --git a/README.md b/README.md
+new file mode 100644
+index 0000000..7597582
+--- /dev/null
++++ b/README.md
+@@ -0,0 +1,10 @@
++Patched build of http://code.google.com/p/google-guice/ - see PATCHES for the exact differences.
++
++Compatibility with Google-Guice
++-------------------------------
++
++The main difference between Sisu-Guice and Google-Guice is that Guava is now exposed as a direct Maven dependency. If you are assembling your application outside of Maven you therefore need to add Guava to the runtime JARs. The build uses Guava 11.0.2 but you can use Maven's `<dependencyManagement>` to select a different version of Guava. (The current source code is compatible up to and including Guava 15.0)
++
++Because of this dependency difference you should avoid mixing the official Google-Guice library with internal extensions provided by Sisu-Guice and vice-versa. Third-party Guice extensions should be compatible with either library.
++
++Sisu-Guice retains the same public API as Google-Guice and is binary compatible from a client perspective.
+diff --git a/core/pom.xml b/core/pom.xml
+index 97c8e60..27f5ea5 100644
+--- a/core/pom.xml
++++ b/core/pom.xml
+@@ -4,14 +4,15 @@
+   <modelVersion>4.0.0</modelVersion>
+ 
+   <parent>
+-    <groupId>com.google.inject</groupId>
++    <groupId>org.sonatype.sisu.inject</groupId>
+     <artifactId>guice-parent</artifactId>
+     <version>4.0-SNAPSHOT</version>
+   </parent>
+ 
+-  <artifactId>guice</artifactId>
++  <groupId>org.sonatype.sisu</groupId>
++  <artifactId>sisu-guice</artifactId>
+ 
+-  <name>Google Guice - Core Library</name>
++  <name>Sisu Guice - Core Library</name>
+ 
+   <properties>
+     <cglib.version>3.0</cglib.version>
+@@ -123,18 +124,6 @@
+           </excludes>
+         </configuration>
+       </plugin>
+-      <!--
+-       | Generate sources jar
+-      -->
+-      <plugin>
+-        <artifactId>maven-source-plugin</artifactId>
+-      </plugin>
+-      <!--
+-       | Generate javadoc jar
+-      -->
+-      <plugin>
+-        <artifactId>maven-javadoc-plugin</artifactId>
+-      </plugin>
+     </plugins>
+   </build>
+ 
+diff --git a/extensions/assistedinject/pom.xml b/extensions/assistedinject/pom.xml
+index 601f24e..38f360e 100644
+--- a/extensions/assistedinject/pom.xml
++++ b/extensions/assistedinject/pom.xml
+@@ -4,13 +4,13 @@
+   <modelVersion>4.0.0</modelVersion>
+ 
+   <parent>
+-    <groupId>com.google.inject.extensions</groupId>
++    <groupId>org.sonatype.sisu.inject</groupId>
+     <artifactId>extensions-parent</artifactId>
+     <version>4.0-SNAPSHOT</version>
+   </parent>
+ 
+   <artifactId>guice-assistedinject</artifactId>
+ 
+-  <name>Google Guice - Extensions - AssistedInject</name>
++  <name>Sisu Guice - Extensions - AssistedInject</name>
+ 
+ </project>
+diff --git a/extensions/grapher/pom.xml b/extensions/grapher/pom.xml
+index 6f63452..920f7ba 100644
+--- a/extensions/grapher/pom.xml
++++ b/extensions/grapher/pom.xml
+@@ -4,23 +4,23 @@
+   <modelVersion>4.0.0</modelVersion>
+ 
+   <parent>
+-    <groupId>com.google.inject.extensions</groupId>
++    <groupId>org.sonatype.sisu.inject</groupId>
+     <artifactId>extensions-parent</artifactId>
+     <version>4.0-SNAPSHOT</version>
+   </parent>
+ 
+   <artifactId>guice-grapher</artifactId>
+ 
+-  <name>Google Guice - Extensions - Grapher</name>
++  <name>Sisu Guice - Extensions - Grapher</name>
+ 
+   <dependencies>
+     <dependency>
+-      <groupId>com.google.inject.extensions</groupId>
++      <groupId>org.sonatype.sisu.inject</groupId>
+       <artifactId>guice-assistedinject</artifactId>
+       <version>${project.version}</version>
+     </dependency>
+     <dependency>
+-      <groupId>com.google.inject.extensions</groupId>
++      <groupId>org.sonatype.sisu.inject</groupId>
+       <artifactId>guice-multibindings</artifactId>
+       <version>${project.version}</version>
+     </dependency>
+diff --git a/extensions/jmx/pom.xml b/extensions/jmx/pom.xml
+index 62c7d17..b6d53bf 100644
+--- a/extensions/jmx/pom.xml
++++ b/extensions/jmx/pom.xml
+@@ -4,13 +4,13 @@
+   <modelVersion>4.0.0</modelVersion>
+ 
+   <parent>
+-    <groupId>com.google.inject.extensions</groupId>
++    <groupId>org.sonatype.sisu.inject</groupId>
+     <artifactId>extensions-parent</artifactId>
+     <version>4.0-SNAPSHOT</version>
+   </parent>
+ 
+   <artifactId>guice-jmx</artifactId>
+ 
+-  <name>Google Guice - Extensions - JMX</name>
++  <name>Sisu Guice - Extensions - JMX</name>
+ 
+ </project>
+diff --git a/extensions/jndi/pom.xml b/extensions/jndi/pom.xml
+index fb9bc3c..f78153a 100644
+--- a/extensions/jndi/pom.xml
++++ b/extensions/jndi/pom.xml
+@@ -4,13 +4,13 @@
+   <modelVersion>4.0.0</modelVersion>
+ 
+   <parent>
+-    <groupId>com.google.inject.extensions</groupId>
++    <groupId>org.sonatype.sisu.inject</groupId>
+     <artifactId>extensions-parent</artifactId>
+     <version>4.0-SNAPSHOT</version>
+   </parent>
+ 
+   <artifactId>guice-jndi</artifactId>
+ 
+-  <name>Google Guice - Extensions - JNDI</name>
++  <name>Sisu Guice - Extensions - JNDI</name>
+ 
+ </project>
+diff --git a/extensions/jndi/test/placeholder.txt b/extensions/jndi/test/placeholder.txt
+new file mode 100644
+index 0000000..8d1c8b6
+--- /dev/null
++++ b/extensions/jndi/test/placeholder.txt
+@@ -0,0 +1 @@
++ 
+diff --git a/extensions/mini/pom.xml b/extensions/mini/pom.xml
+index 03f5c01..213a9ee 100644
+--- a/extensions/mini/pom.xml
++++ b/extensions/mini/pom.xml
+@@ -4,13 +4,13 @@
+   <modelVersion>4.0.0</modelVersion>
+ 
+   <parent>
+-    <groupId>com.google.inject.extensions</groupId>
++    <groupId>org.sonatype.sisu.inject</groupId>
+     <artifactId>extensions-parent</artifactId>
+-    <version>3.1.0-SNAPSHOT</version>
++    <version>3.2.0-SNAPSHOT</version>
+   </parent>
+ 
+   <artifactId>guice-mini</artifactId>
+ 
+-  <name>Google Guice - Extensions - Mini</name>
++  <name>Sisu Guice - Extensions - Mini</name>
+ 
+ </project>
+diff --git a/extensions/multibindings/pom.xml b/extensions/multibindings/pom.xml
+index 8875e39..9eb7ee9 100644
+--- a/extensions/multibindings/pom.xml
++++ b/extensions/multibindings/pom.xml
+@@ -4,13 +4,13 @@
+   <modelVersion>4.0.0</modelVersion>
+ 
+   <parent>
+-    <groupId>com.google.inject.extensions</groupId>
++    <groupId>org.sonatype.sisu.inject</groupId>
+     <artifactId>extensions-parent</artifactId>
+     <version>4.0-SNAPSHOT</version>
+   </parent>
+ 
+   <artifactId>guice-multibindings</artifactId>
+ 
+-  <name>Google Guice - Extensions - MultiBindings</name>
++  <name>Sisu Guice - Extensions - MultiBindings</name>
+ 
+ </project>
+diff --git a/extensions/persist/pom.xml b/extensions/persist/pom.xml
+index 18b15a3..2edfc04 100644
+--- a/extensions/persist/pom.xml
++++ b/extensions/persist/pom.xml
+@@ -4,14 +4,14 @@
+   <modelVersion>4.0.0</modelVersion>
+ 
+   <parent>
+-    <groupId>com.google.inject.extensions</groupId>
++    <groupId>org.sonatype.sisu.inject</groupId>
+     <artifactId>extensions-parent</artifactId>
+     <version>4.0-SNAPSHOT</version>
+   </parent>
+ 
+   <artifactId>guice-persist</artifactId>
+ 
+-  <name>Google Guice - Extensions - Persist</name>
++  <name>Sisu Guice - Extensions - Persist</name>
+ 
+   <dependencies>
+     <dependency>
+diff --git a/extensions/pom.xml b/extensions/pom.xml
+index bd706c7..5b6574b 100644
+--- a/extensions/pom.xml
++++ b/extensions/pom.xml
+@@ -4,17 +4,16 @@
+   <modelVersion>4.0.0</modelVersion>
+ 
+   <parent>
+-    <groupId>com.google.inject</groupId>
++    <groupId>org.sonatype.sisu.inject</groupId>
+     <artifactId>guice-parent</artifactId>
+     <version>4.0-SNAPSHOT</version>
+   </parent>
+ 
+   <packaging>pom</packaging>
+ 
+-  <groupId>com.google.inject.extensions</groupId>
+   <artifactId>extensions-parent</artifactId>
+ 
+-  <name>Google Guice - Extensions</name>
++  <name>Sisu Guice - Extensions</name>
+ 
+   <modules>
+     <module>assistedinject</module>
+@@ -40,17 +39,17 @@
+      | All extensions depend on the core
+     -->
+     <dependency>
+-      <groupId>com.google.inject</groupId>
+-      <artifactId>guice</artifactId>
++      <groupId>org.sonatype.sisu</groupId>
++      <artifactId>sisu-guice</artifactId>
+       <version>${project.version}</version>
+       <scope>provided</scope>
+     </dependency>
+     <!--
+      | Some extension tests depend on the core tests
+     -->
+     <dependency>
+-      <groupId>com.google.inject</groupId>
+-      <artifactId>guice</artifactId>
++      <groupId>org.sonatype.sisu</groupId>
++      <artifactId>sisu-guice</artifactId>
+       <version>${project.version}</version>
+       <classifier>tests</classifier>
+       <scope>test</scope>
+@@ -79,22 +78,10 @@
+         <artifactId>maven-bundle-plugin</artifactId>
+         <configuration>
+           <instructions>
+-            <Fragment-Host>com.google.inject</Fragment-Host>
++            <Fragment-Host>org.sonatype.sisu.guice</Fragment-Host>
+           </instructions>
+         </configuration>
+       </plugin>
+-      <!--
+-       | Generate sources jar
+-      -->
+-      <plugin>
+-        <artifactId>maven-source-plugin</artifactId>
+-      </plugin>
+-      <!--
+-       | Generate javadoc jar
+-      -->
+-      <plugin>
+-        <artifactId>maven-javadoc-plugin</artifactId>
+-      </plugin>
+     </plugins>
+   </build>
+ 
+diff --git a/extensions/service/pom.xml b/extensions/service/pom.xml
+index 8dd4001..9eede3e 100644
+--- a/extensions/service/pom.xml
++++ b/extensions/service/pom.xml
+@@ -4,13 +4,13 @@
+   <modelVersion>4.0.0</modelVersion>
+ 
+   <parent>
+-    <groupId>com.google.inject.extensions</groupId>
++    <groupId>org.sonatype.sisu.inject</groupId>
+     <artifactId>extensions-parent</artifactId>
+-    <version>3.1.0-SNAPSHOT</version>
++    <version>3.2.0-SNAPSHOT</version>
+   </parent>
+ 
+   <artifactId>guice-service</artifactId>
+ 
+-  <name>Google Guice - Extensions - Service</name>
++  <name>Sisu Guice - Extensions - Service</name>
+ 
+ </project>
+diff --git a/extensions/servlet/pom.xml b/extensions/servlet/pom.xml
+index b4117ab..abd6e44 100644
+--- a/extensions/servlet/pom.xml
++++ b/extensions/servlet/pom.xml
+@@ -4,14 +4,14 @@
+   <modelVersion>4.0.0</modelVersion>
+ 
+   <parent>
+-    <groupId>com.google.inject.extensions</groupId>
++    <groupId>org.sonatype.sisu.inject</groupId>
+     <artifactId>extensions-parent</artifactId>
+     <version>4.0-SNAPSHOT</version>
+   </parent>
+ 
+   <artifactId>guice-servlet</artifactId>
+ 
+-  <name>Google Guice - Extensions - Servlet</name>
++  <name>Sisu Guice - Extensions - Servlet</name>
+ 
+   <dependencies>
+     <dependency>
+diff --git a/extensions/spring/pom.xml b/extensions/spring/pom.xml
+index e1bf819..391fd83 100644
+--- a/extensions/spring/pom.xml
++++ b/extensions/spring/pom.xml
+@@ -4,14 +4,14 @@
+   <modelVersion>4.0.0</modelVersion>
+ 
+   <parent>
+-    <groupId>com.google.inject.extensions</groupId>
++    <groupId>org.sonatype.sisu.inject</groupId>
+     <artifactId>extensions-parent</artifactId>
+     <version>4.0-SNAPSHOT</version>
+   </parent>
+ 
+   <artifactId>guice-spring</artifactId>
+ 
+-  <name>Google Guice - Extensions - Spring</name>
++  <name>Sisu Guice - Extensions - Spring</name>
+ 
+   <dependencies>
+     <dependency>
+diff --git a/extensions/struts2/pom.xml b/extensions/struts2/pom.xml
+index cf987cf..2a60ebd 100644
+--- a/extensions/struts2/pom.xml
++++ b/extensions/struts2/pom.xml
+@@ -4,18 +4,18 @@
+   <modelVersion>4.0.0</modelVersion>
+ 
+   <parent>
+-    <groupId>com.google.inject.extensions</groupId>
++    <groupId>org.sonatype.sisu.inject</groupId>
+     <artifactId>extensions-parent</artifactId>
+     <version>4.0-SNAPSHOT</version>
+   </parent>
+ 
+   <artifactId>guice-struts2</artifactId>
+ 
+-  <name>Google Guice - Extensions - Struts2</name>
++  <name>Sisu Guice - Extensions - Struts2</name>
+ 
+   <dependencies>
+     <dependency>
+-      <groupId>com.google.inject.extensions</groupId>
++      <groupId>org.sonatype.sisu.inject</groupId>
+       <artifactId>guice-servlet</artifactId>
+       <version>${project.version}</version>
+     </dependency>
+diff --git a/extensions/throwingproviders/pom.xml b/extensions/throwingproviders/pom.xml
+index cd866cc..d086054 100644
+--- a/extensions/throwingproviders/pom.xml
++++ b/extensions/throwingproviders/pom.xml
+@@ -4,13 +4,13 @@
+   <modelVersion>4.0.0</modelVersion>
+ 
+   <parent>
+-    <groupId>com.google.inject.extensions</groupId>
++    <groupId>org.sonatype.sisu.inject</groupId>
+     <artifactId>extensions-parent</artifactId>
+     <version>4.0-SNAPSHOT</version>
+   </parent>
+ 
+   <artifactId>guice-throwingproviders</artifactId>
+ 
+-  <name>Google Guice - Extensions - ThrowingProviders</name>
++  <name>Sisu Guice - Extensions - ThrowingProviders</name>
+ 
+ </project>
+diff --git a/pom.xml b/pom.xml
+index 0805839..83477bc 100644
+--- a/pom.xml
++++ b/pom.xml
+@@ -18,21 +18,21 @@ See the Apache License Version 2.0 for the specific language governing permissio
+   <modelVersion>4.0.0</modelVersion>
+ 
+   <parent>
+-    <groupId>com.google</groupId>
+-    <artifactId>google</artifactId>
+-    <version>5</version>
++    <groupId>org.sonatype.forge</groupId>
++    <artifactId>forge-parent</artifactId>
++    <version>35</version>
+   </parent>
+ 
+   <packaging>pom</packaging>
+ 
+-  <groupId>com.google.inject</groupId>
++  <groupId>org.sonatype.sisu.inject</groupId>
+   <artifactId>guice-parent</artifactId>
+   <version>4.0-SNAPSHOT</version>
+ 
+-  <name>Google Guice</name>
++  <name>Sisu Guice</name>
+ 
+   <description>
+-    Guice is a lightweight dependency injection framework for Java 5 and above
++    Patched build of Guice: a lightweight dependency injection framework for Java 5 and above
+   </description>
+ 
+   <url>http://code.google.com/p/google-guice/</url>
+@@ -61,9 +61,9 @@ See the Apache License Version 2.0 for the specific language governing permissio
+   </mailingLists>
+ 
+   <scm>
+-    <connection>scm:git:https://code.google.com/p/google-guice/</connection>
+-    <developerConnection>scm:git:https://code.google.com/p/google-guice/</developerConnection>
+-    <url>https://code.google.com/p/google-guice/</url>
++    <connection>scm:git:git at github.com:sonatype/sisu-guice.git</connection>
++    <developerConnection>scm:git:git at github.com:sonatype/sisu-guice.git</developerConnection>
++    <url>http://github.com/sonatype/sisu-guice</url>
+   </scm>
+ 
+   <issueManagement>
+@@ -73,7 +73,7 @@ See the Apache License Version 2.0 for the specific language governing permissio
+ 
+   <ciManagement>
+     <system>Hudson</system>
+-    <url>https://grid.sonatype.org/ci/job/google-guice/</url>
++    <url>https://builds.sonatype.org/job/sisu-guice/</url>
+   </ciManagement>
+ 
+   <licenses>
+@@ -107,7 +107,6 @@ See the Apache License Version 2.0 for the specific language governing permissio
+      | Use "-Dguice.with.no_aop=false" to skip the no-AOP variant
+     -->
+     <guice.with.no_aop>true</guice.with.no_aop>
+-    <gpg.skip>true</gpg.skip>
+   </properties>
+ 
+   <dependencies>
+@@ -316,7 +315,7 @@ See the Apache License Version 2.0 for the specific language governing permissio
+               <Bundle-Copyright>Copyright (C) 2006 Google Inc.</Bundle-Copyright>
+               <Bundle-DocURL>http://code.google.com/p/google-guice/</Bundle-DocURL>
+               <Bundle-Name>${project.artifactId}</Bundle-Name>
+-              <Bundle-SymbolicName>$(module)</Bundle-SymbolicName>
++              <Bundle-Vendor>Sonatype, Inc.</Bundle-Vendor>
+               <Bundle-RequiredExecutionEnvironment>
+                 J2SE-1.5,JavaSE-1.6
+               </Bundle-RequiredExecutionEnvironment>
+@@ -367,82 +366,56 @@ See the Apache License Version 2.0 for the specific language governing permissio
+         <plugin>
+           <artifactId>maven-javadoc-plugin</artifactId>
+           <version>2.7</version>
+-          <executions>
+-            <execution>
+-              <phase>package</phase>
+-              <goals>
+-                <goal>jar</goal>
+-              </goals>
+-            </execution>
+-          </executions>
++          <configuration>
++            <doclet>com.google.doclava.Doclava</doclet>
++            <docletPath>
++              ${project.basedir}/../lib/build/doclava.jar:
++              ${project.basedir}/../../lib/build/doclava.jar
++            </docletPath>
++            <!--
++             | bootclasspath required by Sun's JVM 
++            -->
++            <bootclasspath>${sun.boot.class.path}</bootclasspath>
++            <excludePackageNames>*.internal</excludePackageNames>
++            <additionalparam>
++              -quiet
++              -federate JDK http://download.oracle.com/javase/6/docs/api/index.html?
++              -federationxml JDK http://doclava.googlecode.com/svn/static/api/openjdk-6.xml
++              -hdf project.name "${project.name} (patched build of Google Guice)"
++              -d ${project.build.directory}/apidocs
++            </additionalparam>
++            <useStandardDocletOptions>false</useStandardDocletOptions>
++            <!--
++             | Apple's JVM sometimes requires more memory
++            -->
++            <additionalJOption>-J-Xmx1024m</additionalJOption>
++          </configuration>
++        </plugin>
++        <plugin>
++          <artifactId>maven-site-plugin</artifactId>
++          <version>3.2</version>
+         </plugin>
+         <plugin>
+           <artifactId>maven-source-plugin</artifactId>
+           <version>2.1.2</version>
+-          <executions>
+-            <execution>
+-              <phase>package</phase>
+-              <goals>
+-                <goal>jar</goal>
+-                <goal>test-jar</goal>
+-              </goals>
+-            </execution>
+-          </executions>
++        </plugin>
++        <plugin>
++          <artifactId>maven-gpg-plugin</artifactId>
++          <version>1.4</version>
+         </plugin>
+         <plugin>
+           <artifactId>maven-release-plugin</artifactId>
+-          <version>2.1</version>
++          <version>2.2.1</version>
+           <configuration>
+             <autoVersionSubmodules>true</autoVersionSubmodules>
+           </configuration>
+         </plugin>
+         <plugin>
+           <artifactId>maven-deploy-plugin</artifactId>
+-          <version>2.5</version>
++          <version>2.7</version>
+         </plugin>
+       </plugins>
+     </pluginManagement>
+-    <plugins>
+-      <!--
+-       | Sign artifacts.
+-      -->
+-      <plugin>
+-        <artifactId>maven-gpg-plugin</artifactId>
+-        <version>1.4</version>
+-        <executions>
+-          <execution>
+-            <id>sign-artifacts</id>
+-            <phase>verify</phase>
+-            <goals><goal>sign</goal></goals>
+-          </execution>
+-        </executions>
+-      </plugin>
+-    </plugins>
+   </build>
+ 
+-  <profiles>
+-    <profile>
+-      <!--
+-       | Deployment profile for the Sonatype Grid
+-      -->
+-      <id>sonatype-grid</id>
+-      <properties>
+-        <forgeReleaseId>forge-releases</forgeReleaseId>
+-        <forgeReleaseUrl>http://repository.sonatype.org:8081/service/local/staging/deploy/maven2</forgeReleaseUrl>
+-        <forgeSnapshotId>forge-snapshots</forgeSnapshotId>
+-        <forgeSnapshotUrl>http://repository.sonatype.org/content/repositories/snapshots</forgeSnapshotUrl>
+-      </properties>
+-      <distributionManagement>
+-        <repository>
+-          <id>${forgeReleaseId}</id>
+-          <url>${forgeReleaseUrl}</url>
+-        </repository>
+-        <snapshotRepository>
+-          <id>${forgeSnapshotId}</id>
+-          <url>${forgeSnapshotUrl}</url>
+-        </snapshotRepository>
+-      </distributionManagement>
+-    </profile>
+-  </profiles>
+-
+ </project>
diff --git a/README b/README
deleted file mode 100644
index ad2843d..0000000
--- a/README
+++ /dev/null
@@ -1 +0,0 @@
-Patched build of http://code.google.com/p/google-guice/ - see PATCHES for the exact differences.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..7597582
--- /dev/null
+++ b/README.md
@@ -0,0 +1,10 @@
+Patched build of http://code.google.com/p/google-guice/ - see PATCHES for the exact differences.
+
+Compatibility with Google-Guice
+-------------------------------
+
+The main difference between Sisu-Guice and Google-Guice is that Guava is now exposed as a direct Maven dependency. If you are assembling your application outside of Maven you therefore need to add Guava to the runtime JARs. The build uses Guava 11.0.2 but you can use Maven's `<dependencyManagement>` to select a different version of Guava. (The current source code is compatible up to and including Guava 15.0)
+
+Because of this dependency difference you should avoid mixing the official Google-Guice library with internal extensions provided by Sisu-Guice and vice-versa. Third-party Guice extensions should be compatible with either library.
+
+Sisu-Guice retains the same public API as Google-Guice and is binary compatible from a client perspective.
diff --git a/build.properties b/build.properties
index a76a5ab..d8bf543 100644
--- a/build.properties
+++ b/build.properties
@@ -32,4 +32,4 @@ javadoc.packagenames=com.google.inject,com.google.inject.spi,\
   com.google.inject.persist.jpa
 test.class=com.google.inject.AllTests
 module=com.google.inject
-imports=!net.sf.cglib.*,!org.objectweb.asm.*,!com.google.common.*
+imports=!net.sf.cglib.*,!org.objectweb.asm.*
diff --git a/build.xml b/build.xml
index b499839..ac19447 100644
--- a/build.xml
+++ b/build.xml
@@ -28,38 +28,38 @@
     <ant antfile="extensions/throwingproviders/build.xml" target="distjars" inheritAll="false"/>
     <ant antfile="extensions/multibindings/build.xml" target="distjars" inheritAll="false"/>
     <ant antfile="extensions/persist/build.xml" target="distjars" inheritAll="false"/>
-  	<ant antfile="extensions/grapher/build.xml" target="distjars" inheritAll="false"/>
+        <ant antfile="extensions/grapher/build.xml" target="distjars" inheritAll="false"/>
 
     <copy toDir="${build.dir}/dist"> 
-      <fileset dir="extensions/servlet/build" includes="*.jar" excludes="*-with-deps.jar"/>
+      <fileset dir="extensions/servlet/build" includes="*.jar"/>
     </copy>
     <copy toDir="${build.dir}/dist"> 
-      <fileset dir="extensions/spring/build" includes="*.jar" excludes="*-with-deps.jar"/>
+      <fileset dir="extensions/spring/build" includes="*.jar"/>
     </copy>
     <copy toDir="${build.dir}/dist">
-      <fileset dir="extensions/struts2/build" includes="*.jar" excludes="*-with-deps.jar"/>
+      <fileset dir="extensions/struts2/build" includes="*.jar"/>
     </copy>
     <copy toDir="${build.dir}/dist">
-      <fileset dir="extensions/assistedinject/build" includes="*.jar" excludes="*-with-deps.jar"/>
+      <fileset dir="extensions/assistedinject/build" includes="*.jar"/>
     </copy>
     <copy toDir="${build.dir}/dist">
-      <fileset dir="extensions/jmx/build" includes="*.jar" excludes="*-with-deps.jar"/>
+      <fileset dir="extensions/jmx/build" includes="*.jar"/>
     </copy>
     <copy toDir="${build.dir}/dist">
-      <fileset dir="extensions/jndi/build" includes="*.jar" excludes="*-with-deps.jar"/>
+      <fileset dir="extensions/jndi/build" includes="*.jar"/>
     </copy>
     <copy toDir="${build.dir}/dist">
-      <fileset dir="extensions/throwingproviders/build" includes="*.jar" excludes="*-with-deps.jar"/>
+      <fileset dir="extensions/throwingproviders/build" includes="*.jar"/>
     </copy>
     <copy toDir="${build.dir}/dist">
-      <fileset dir="extensions/multibindings/build" includes="*.jar" excludes="*-with-deps.jar"/>
+      <fileset dir="extensions/multibindings/build" includes="*.jar"/>
     </copy>
     <copy toDir="${build.dir}/dist">
-      <fileset dir="extensions/persist/build" includes="*.jar" excludes="*-with-deps.jar"/>
+      <fileset dir="extensions/persist/build" includes="*.jar"/>
     </copy>
-  	<copy toDir="${build.dir}/dist">
-  	  <fileset dir="extensions/grapher/build" includes="*.jar" excludes="*-with-deps.jar"/>
-  	</copy>    	
+        <copy toDir="${build.dir}/dist">
+          <fileset dir="extensions/grapher/build" includes="*.jar"/>
+        </copy>
 
     <copy toDir="${build.dir}/dist" file="COPYING"/> 
     <copy toDir="${build.dir}/dist"> 
@@ -78,27 +78,42 @@
           excludes="build/**,**/.svn/**,classes/**,.settings/**,bin/**,latest-api-diffs/**,latest-javadoc/**,.classpath,.project"/>
     </zip>
   </target>
-  
+
   <target name="test.dist"
-      depends="jar, test.compile-with-deps"
-      description="Execute JUnit tests against distribution jar.">
-    <java fork="true" 
+    description="Execute JUnit tests against distribution jar.">
+    <antcall target="test.dist.run">
+      <param name="jvmarg-value" value="-Dguice_include_stack_traces="/>
+    </antcall>
+    <antcall target="test.dist.run">
+      <param name="jvmarg-value" value="-Dguice_include_stack_traces=OFF"/>
+    </antcall>
+    <antcall target="test.dist.run">
+      <param name="jvmarg-value" value="-Dguice_include_stack_traces=COMPLETE"/>
+    </antcall>
+  </target>
+
+  <target name="test.dist.run"
+    depends="jar, test.withdeps"
+    description="Execute JUnit tests against distribution jar with the given jvmarg.">
+    <java fork="true"
         classname="junit.textui.TestRunner"
         failonerror="true"
         taskname="junit">
       <classpath>
         <pathelement location="${build.dir}/guice-${version}-tests.jar"/>
         <pathelement location="${build.dir}/dist/guice-${version}.jar"/>
+        <pathelement location="lib/javax.inject.jar"/>
         <pathelement location="lib/aopalliance.jar"/>
+        <pathelement location="lib/guava-11.0.2.jar"/>
         <pathelement location="lib/build/junit.jar"/>
         <pathelement location="lib/build/servlet-api-2.5.jar"/>
         <pathelement location="lib/build/easymock.jar"/>
-        <pathelement location="lib/javax.inject.jar"/>
         <pathelement location="lib/build/javax.inject-tck.jar"/>
         <pathelement location="lib/build/bnd-0.0.384.jar"/>
         <pathelement location="lib/build/felix-2.0.5.jar"/>
       </classpath>
-      <arg value="com.google.inject.AllTests"/>    
+      <arg value="com.google.inject.AllTests"/>
+      <jvmarg value="${jvmarg-value}"/>
       <syspropertyset>
         <propertyref name="guice.custom.loader"/>
         <propertyref name="version"/>
@@ -107,20 +122,20 @@
       </syspropertyset>
     </java>
   </target>
-	
-  <property name="old.api" value="2.0"/>
-  <property name="new.api" value="3.0"/>
+
+  <property name="old.api" value="3.0"/>
+  <property name="new.api" value="4.0"/>
   <target name="jdiff">
-  	<property name="jdiff.home" value="lib/build/jdiff"/>
-  	<property name="jdiff.tmp" value="build/docs/latest-api-diffs"/>
-  	<delete dir="${jdiff.tmp}"/>
-  	<mkdir dir="${jdiff.tmp}"/>
-  	
-  	<!-- Generate API for current version. -->
+        <property name="jdiff.home" value="lib/build/jdiff"/>
+        <property name="jdiff.tmp" value="build/docs/latest-api-diffs"/>
+        <delete dir="${jdiff.tmp}"/>
+        <mkdir dir="${jdiff.tmp}"/>
+  
+        <!-- Generate API for current version. -->
     <javadoc packagenames="com.google.*"
-	         docletpath="${jdiff.home}/jdiff.jar${path.separator}${jdiff.home}/xerces.jar"
+                 docletpath="${jdiff.home}/jdiff.jar${path.separator}${jdiff.home}/xerces.jar"
              maxmemory="512M"
-    	     classpath="lib/javax.inject.jar${path.separator}lib/aopalliance.jar">
+             classpath="lib/javax.inject.jar${path.separator}lib/aopalliance.jar${path.separator}guava-11.0.2.jar">
       <fileset dir="${src.dir}" defaultexcludes="yes">
         <include name="com/google/**"/>
         <exclude name="com/google/inject/internal/**"/>
@@ -134,7 +149,7 @@
       <fileset dir="${multibindings.src.dir}"/>
       <fileset dir="${persist.src.dir}"/>
       <fileset dir="${struts2.src.dir}"/>
-    	<fileset dir="${grapher.src.dir}"/>
+        <fileset dir="${grapher.src.dir}"/>
 
       <doclet name="jdiff.JDiff"
               path="${jdiff.home}/jdiff.jar:${jdiff.home}/xerces.jar">
@@ -143,33 +158,33 @@
       </doclet>
     </javadoc>
 
-  	<!-- Do a diff against the previous version. -->
-	<javadoc packagenames="com.google.*"
-	         destdir="${jdiff.tmp}"
-		     docletpath="${jdiff.home}/jdiff.jar${path.separator}${jdiff.home}/xerces.jar"
-	         maxmemory="512M"
-		     sourcefiles="${jdiff.home}/Null.java"
-  	         classpath="lib/javax.inject.jar${path.separator}lib/aopalliance.jar">
-	  <doclet name="jdiff.JDiff">
-	  	<param name="-oldapi" value="${old.api}"/>
-	  	<param name="-oldapidir" value="latest-api-diffs"/>	  	
-	  	<param name="-javadocold" value="http://google-guice.googlecode.com/svn/trunk/latest-api-diffs/${old.api}/javadoc/"/>
-	  	<param name="-newapi" value="${new.api}"/>
-	    <param name="-newapidir" value="${jdiff.tmp}"/>
-	  	<param name="-javadocnew" value="http://google-guice.googlecode.com/svn/trunk/latest-api-diffs/${new.api}/javadoc/"/>
-	  	<param name="-stats"/>
-	  	<param name="-docchanges"/>
-	  </doclet>
-	</javadoc>
+        <!-- Do a diff against the previous version. -->
+        <javadoc packagenames="com.google.*"
+                 destdir="${jdiff.tmp}"
+                     docletpath="${jdiff.home}/jdiff.jar${path.separator}${jdiff.home}/xerces.jar"
+                 maxmemory="512M"
+                     sourcefiles="${jdiff.home}/Null.java"
+                 classpath="lib/javax.inject.jar${path.separator}lib/aopalliance.jar${path.separator}guava-11.0.2.jar">
+          <doclet name="jdiff.JDiff">
+                <param name="-oldapi" value="${old.api}"/>
+                <param name="-oldapidir" value="latest-api-diffs"/>             
+                <param name="-javadocold" value="http://google-guice.googlecode.com/git/latest-api-diffs/${old.api}/javadoc/"/>
+                <param name="-newapi" value="${new.api}"/>
+            <param name="-newapidir" value="${jdiff.tmp}"/>
+                <param name="-javadocnew" value="http://google-guice.googlecode.com/git/latest-api-diffs/${new.api}/javadoc/"/>
+                <param name="-stats"/>
+                <param name="-docchanges"/>
+          </doclet>
+        </javadoc>
   </target>
   
   <target name="javadoc">
     <javadoc packagenames="com.google.*"
              destdir="build/docs"
-    	     docletpath="lib/build/doclava.jar"
+             docletpath="lib/build/doclava.jar"
              bootclasspath="${java.home}/lib/rt.jar"
              maxmemory="512M"
-    	     classpath="lib/javax.inject.jar${path.separator}lib/aopalliance.jar">
+             classpath="lib/javax.inject.jar${path.separator}lib/aopalliance.jar${path.separator}guava-11.0.2.jar">
       <fileset dir="${src.dir}" defaultexcludes="yes">
         <include name="com/google/**"/>
         <exclude name="com/google/inject/internal/**"/>
@@ -182,17 +197,18 @@
       <fileset dir="${throwingproviders.src.dir}"/>
       <fileset dir="${multibindings.src.dir}"/>
       <fileset dir="${persist.src.dir}"/>
-    	<fileset dir="${grapher.src.dir}"/>
+        <fileset dir="${grapher.src.dir}"/>
       <!-- TODO: this breaks Doclava for some reason
       <fileset dir="${struts2.src.dir}"/> -->
 
       <doclet name="com.google.doclava.Doclava">
         <param name="-hdf"/> <param name="project.name"/> <param name="Guice"/>
         <param name="-since"/> <param name="lib/build/guice-1.0.xml"/> <param name="Guice_1.0" />
-        <param name="-since"/> <param name="lib/build/guice-2.0.xml"/> <param name="Guice_2.0" /> 
+        <param name="-since"/> <param name="lib/build/guice-2.0.xml"/> <param name="Guice_2.0" />
+      	<param name="-since"/> <param name="lib/build/guice-3.0.xml"/> <param name="Guice_3.0" />
         <param name="-apiversion" value="Guice_${new.api}"/>
         <param name="-assetsdir" value="javadoc/assets"/>
-      	<param name="-apixml" value="build/docs/guice-${new.api}.xml"/>
+        <param name="-apixml" value="build/docs/guice-${new.api}.xml"/>
         <!-- TODO: fix doclava federation
           http://aopalliance.sourceforge.net/doc
           http://www.springframework.org/docs/api/
@@ -236,16 +252,16 @@
       <arg value="-DNO_AOP" />
     </munge>
     <replace file="build/no_aop/common.xml" value="">
-      <replacetoken><![CDATA[<zipfileset src="${common.basedir}/lib/build/asm-3.3.1.jar"/>]]></replacetoken>
+      <replacetoken><![CDATA[<zipfileset src="${common.basedir}/lib/build/asm-5.0_BETA.jar"/>]]></replacetoken>
     </replace>
     <replace file="build/no_aop/common.xml" value="">
-      <replacetoken><![CDATA[<zipfileset src="${common.basedir}/lib/build/cglib-2.2.2.jar"/>]]></replacetoken>
+      <replacetoken><![CDATA[<zipfileset src="${common.basedir}/lib/build/cglib-3.1.jar"/>]]></replacetoken>
     </replace>
     <replace file="build/no_aop/common.xml" value="">
-      <replacetoken><![CDATA[<zipfileset src="${common.basedir}/lib/build/cglib-2.2.2.jar">
-        <include name="LICENSE"/>
-        <include name="NOTICE"/>
-      </zipfileset>]]></replacetoken>
+      <replacetoken><![CDATA[<zipfileset src="${common.basedir}/lib/build/cglib-3.1.jar"><include name="LICENSE"/><include name="NOTICE"/></zipfileset>]]></replacetoken>
+    </replace>
+    <replace file="build/no_aop/common.xml" value='Bundle-Name" value="$${ant.project.name} (no_aop)'>
+      <replacetoken><![CDATA[Bundle-Name" value="${ant.project.name}]]></replacetoken>
     </replace>
   </target>
 
diff --git a/common.xml b/common.xml
index 943d385..73ac4c3 100644
--- a/common.xml
+++ b/common.xml
@@ -7,7 +7,7 @@
   <!-- can be overridden at the command line with -Dversion=
        or in IDEA, in the ant properties dialog -->
   <property name="version" value="snapshot"/>
-  <property name="api.version" value="1.3"/>
+  <property name="api.version" value="1.4"/>
 
   <target name="compile" description="Compile Java source.">
     <mkdir dir="${build.dir}/classes"/>
@@ -44,6 +44,10 @@
 
     <property name="Export-Package" value="!${module}.internal.*,${module}.*;version=${api.version}"/>
 
+    <condition property="DynamicImport-Package" value="org.slf4j">
+      <equals arg1="${module}" arg2="com.google.inject"/>
+    </condition>
+
     <condition property="Eclipse-ExtensibleAPI" value="true">
       <equals arg1="${module}" arg2="com.google.inject"/>
     </condition>
@@ -119,7 +123,7 @@
       depends="source.jar, jar"
       description="Build jar files"/>
 
-  <target name="test.compile-with-deps" depends="test.compile"
+  <target name="test.withdeps" depends="test.compile"
       description="Build a jar of tests with internal.util refocused.">
     <mkdir dir="${build.dir}/dist"/>
     <dirname property="common.basedir" file="${ant.file.common}"/>
@@ -131,10 +135,6 @@
       <rule pattern="net.sf.cglib.**.*" result="com.google.inject.internal.cglib. at 1.$@2"/>
       <rule pattern="org.objectweb.asm.*" result="com.google.inject.internal.asm.$@1"/>
       <rule pattern="org.objectweb.asm.**.*" result="com.google.inject.internal.asm. at 1.$@2"/>
-      <rule pattern="com.google.common.*" result="com.google.inject.internal.guava.$@1"/>
-      <rule pattern="com.google.common.**.*" result="com.google.inject.internal.guava. at 1.$@2"/>
-      <rule pattern="javax.annotation.*.class" result="com.google.inject.internal.jsr305.$@1"/>
-      <rule pattern="javax.annotation.**.*.class" result="com.google.inject.internal.jsr305. at 1.$@2"/>
       <keep pattern="com.google.inject.**"/>
       <keep pattern="com.googlecode.**"/>
     </jarjar>
@@ -148,40 +148,12 @@
         classpath="${common.basedir}/lib/build/jarjar-1.1.jar"/>
     <jarjar jarfile="${build.dir}/${ant.project.name}-with-deps.jar">
       <fileset dir="${build.dir}/classes"/>
-      <zipfileset src="${common.basedir}/lib/build/cglib-2.2.2.jar"/>
-      <zipfileset src="${common.basedir}/lib/build/asm-3.3.1.jar"/>
-      <zipfileset src="${common.basedir}/lib/build/guava-11.0.1.jar"/>
-      <rule pattern="net.sf.cglib.*" result="com.google.inject.internal.cglib.$@1"/>
-      <rule pattern="net.sf.cglib.**.*" result="com.google.inject.internal.cglib. at 1.$@2"/>
-      <rule pattern="org.objectweb.asm.*" result="com.google.inject.internal.asm.$@1"/>
-      <rule pattern="org.objectweb.asm.**.*" result="com.google.inject.internal.asm. at 1.$@2"/>
-      <rule pattern="com.google.common.*" result="com.google.inject.internal.guava.$@1"/>
-      <rule pattern="com.google.common.**.*" result="com.google.inject.internal.guava. at 1.$@2"/>
-      <keep pattern="com.google.inject.**"/>
-      <!-- the servlet extension uses this but core doesn't,
-           so we explicitly instruct the build to keep it. -->
-      <keep pattern="com.google.common.base.Throwables"/>
-    </jarjar>
-  </target>
-
-  <target name="jar.withrenameddeps" depends="compile"
-      description="Build jar with dependencies embedded.">
-    <mkdir dir="${build.dir}/dist"/>
-    <dirname property="common.basedir" file="${ant.file.common}"/>
-    <taskdef name="jarjar" classname="com.tonicsystems.jarjar.JarJarTask"
-        classpath="${common.basedir}/lib/build/jarjar-1.1.jar"/>
-    <jarjar jarfile="${build.dir}/${ant.project.name}-with-deps.jar">
-      <zipfileset src="${common.basedir}/lib/build/cglib-2.2.2.jar">
-        <include name="LICENSE"/>
-        <include name="NOTICE"/>
-      </zipfileset>
-      <fileset dir="${build.dir}/classes"/>
+      <zipfileset src="${common.basedir}/lib/build/cglib-3.1.jar"/>
+      <zipfileset src="${common.basedir}/lib/build/asm-5.0_BETA.jar"/>
       <rule pattern="net.sf.cglib.*" result="com.google.inject.internal.cglib.$@1"/>
       <rule pattern="net.sf.cglib.**.*" result="com.google.inject.internal.cglib. at 1.$@2"/>
       <rule pattern="org.objectweb.asm.*" result="com.google.inject.internal.asm.$@1"/>
       <rule pattern="org.objectweb.asm.**.*" result="com.google.inject.internal.asm. at 1.$@2"/>
-      <rule pattern="com.google.common.*" result="com.google.inject.internal.guava.$@1"/>
-      <rule pattern="com.google.common.**.*" result="com.google.inject.internal.guava. at 1.$@2"/>
       <keep pattern="com.google.inject.**"/>
     </jarjar>
   </target>
diff --git a/copy.sh b/copy.sh
deleted file mode 100755
index a3e2202..0000000
--- a/copy.sh
+++ /dev/null
@@ -1,54 +0,0 @@
-#!/bin/sh
-# Copies classes into Guice's internal package.
-
-client=/usr/local/google/clients/collect/google3
-
-srcdir=core/src/com/google/inject/internal
-testdir=core/test/com/google/inject/internal
-
-filter() {
-  sed 's/com.google.common.base.internal/com.google.inject.internal/' | \
-  sed 's/com.google.common.base/com.google.inject.internal/' | \
-  sed 's/com.google.common.collect/com.google.inject.internal/'
-}
-
-copy() {
-  inFile=$1;
-  fileName=`basename $inFile`
-  dest=$2
-  destpath=$dest/$fileName
-  filter < $client/${inFile} > $destpath
-}
-
-commonpath=java/com/google/common
-
-copy $commonpath/collect/ComputationException.java $srcdir
-copy $commonpath/collect/AsynchronousComputationException.java $srcdir
-copy $commonpath/collect/CustomConcurrentHashMap.java $srcdir
-copy $commonpath/collect/ExpirationTimer.java $srcdir
-copy $commonpath/collect/MapMaker.java $srcdir
-copy $commonpath/collect/NullOutputException.java $srcdir
-copy $commonpath/base/Function.java $srcdir
-copy $commonpath/base/Nullable.java $srcdir
-copy $commonpath/base/FinalizableReference.java $srcdir
-copy $commonpath/base/FinalizableReferenceQueue.java $srcdir
-copy $commonpath/base/internal/Finalizer.java $srcdir
-copy $commonpath/base/FinalizableWeakReference.java $srcdir
-copy $commonpath/base/FinalizableSoftReference.java $srcdir
-copy $commonpath/base/FinalizablePhantomReference.java $srcdir
-
-commontestspath=javatests/com/google/common
-
-copy $commontestspath/base/FinalizableReferenceQueueTest.java $testdir
-copy $commontestspath/collect/MapMakerTestSuite.java $testdir
-copy $commontestspath/collect/Jsr166HashMap.java $testdir
-copy $commontestspath/collect/Jsr166HashMapTest.java $testdir
-copy $commonpath/collect/ForwardingConcurrentMap.java $testdir
-copy $commonpath/collect/ForwardingMap.java $testdir
-copy $commonpath/collect/ForwardingCollection.java $testdir
-copy $commonpath/collect/ForwardingObject.java $testdir
-copy $commonpath/collect/ForwardingSet.java $testdir
-copy $commonpath/collect/ForwardingMap.java $testdir
-copy $commonpath/base/Preconditions.java $testdir
-
-chmod +w -R $srcdir $testdir
diff --git a/core/pom.xml b/core/pom.xml
index 7fa754d..28050e9 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -6,7 +6,7 @@
   <parent>
     <groupId>org.sonatype.sisu.inject</groupId>
     <artifactId>guice-parent</artifactId>
-    <version>3.1.1</version>
+    <version>3.1.9</version>
   </parent>
 
   <groupId>org.sonatype.sisu</groupId>
@@ -21,26 +21,37 @@
       <version>1</version>
     </dependency>
     <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+      <version>1.6.4</version>
+      <optional>true</optional>
+    </dependency>
+    <dependency>
       <groupId>aopalliance</groupId>
       <artifactId>aopalliance</artifactId>
       <version>1.0</version>
     </dependency>
     <dependency>
-      <groupId>org.sonatype.sisu</groupId>
-      <artifactId>sisu-guava</artifactId>
-      <version>0.11.1</version>
+      <groupId>com.google.guava</groupId>
+      <artifactId>guava</artifactId>
+      <version>11.0.2</version>
     </dependency>
+    <!--
+     | CGLIB is embedded by default by the JarJar build profile
+    -->
     <dependency>
-      <groupId>cglib</groupId>
-      <artifactId>cglib</artifactId>
-      <version>2.2.2</version>
+      <groupId>org.ow2.asm</groupId>
+      <artifactId>asm</artifactId>
+      <version>5.0_BETA</version>
       <optional>true</optional>
+      <scope>provided</scope>
     </dependency>
     <dependency>
-      <groupId>org.slf4j</groupId>
-      <artifactId>slf4j-api</artifactId>
-      <version>1.6.3</version>
+      <groupId>cglib</groupId>
+      <artifactId>cglib</artifactId>
+      <version>3.1</version>
       <optional>true</optional>
+      <scope>provided</scope>
     </dependency>
     <dependency>
       <groupId>javax.inject</groupId>
@@ -51,7 +62,7 @@
     <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-beans</artifactId>
-      <version>3.0.6.RELEASE</version>
+      <version>3.0.5.RELEASE</version>
       <scope>test</scope>
     </dependency>
     <dependency>
@@ -63,7 +74,7 @@
     <dependency>
       <groupId>org.apache.felix</groupId>
       <artifactId>org.apache.felix.framework</artifactId>
-      <version>4.0.1</version>
+      <version>3.0.5</version>
       <scope>test</scope>
     </dependency>
   </dependencies>
@@ -93,10 +104,12 @@
             <exclude>org.slf4j:slf4j-api</exclude>
           </classpathDependencyExcludes>
           <!--
-           | This test needs updating for use with Maven
+           | Temporarily excluded tests
           -->
           <excludes>
             <exclude>**/OSGiContainerTest*</exclude>
+            <exclude>**/ScopesTest*</exclude>
+            <exclude>**/TypeConversionTest*</exclude>
           </excludes>
         </configuration>
       </plugin>
@@ -108,12 +121,9 @@
         <artifactId>maven-bundle-plugin</artifactId>
         <configuration>
           <instructions>
-            <Bundle-Name>
-              ${project.artifactId}$(if;$(classes;NAMED;*.Interceptor*);; (no_aop))
-            </Bundle-Name>
-            <Require-Bundle>org.sonatype.sisu.guava</Require-Bundle>
-            <DynamicImport-Package>org.slf4j</DynamicImport-Package>
+            <Bundle-Name>${project.artifactId}$(if;$(classes;NAMED;*.MethodAspect);; (no_aop))</Bundle-Name>
             <Eclipse-ExtensibleAPI>true</Eclipse-ExtensibleAPI>
+            <DynamicImport-Package>org.slf4j</DynamicImport-Package>
           </instructions>
         </configuration>
       </plugin>
@@ -201,7 +211,7 @@
     </profile>
     <profile>
       <!--
-       | JarJar build profile: re-package ASM and CGLIB classes under the Guice namespace
+       | JarJar build profile: embed CGLIB (and ASM) classes under Guice namespace
       -->
       <id>guice.with.jarjar</id>
       <activation>
@@ -215,11 +225,11 @@
           <plugin>
             <groupId>org.sonatype.plugins</groupId>
             <artifactId>jarjar-maven-plugin</artifactId>
-            <version>1.5</version>
+            <version>1.8</version>
             <configuration>
               <overwrite>true</overwrite>
               <includes>
-                <include>*:asm</include>
+                <include>*:asm*</include>
                 <include>*:cglib</include>
               </includes>
               <rules>
@@ -273,12 +283,34 @@
               </execution>
             </executions>
           </plugin>
+          <!--
+           | Attach original (non-JarJar'd) classes
+          -->
+          <plugin>
+            <artifactId>maven-jar-plugin</artifactId>
+            <executions>
+              <execution>
+                <id>classes</id>
+                <phase>package</phase>
+                <goals>
+                  <goal>jar</goal>
+                </goals>
+                <configuration>
+                  <classesDirectory>${project.build.directory}/original-classes</classesDirectory>
+                  <classifier>classes</classifier>
+                  <archive>
+                    <manifestFile combine.self="override" />
+                  </archive>
+                </configuration>
+              </execution>
+            </executions>
+          </plugin>
         </plugins>
       </build>
     </profile>
     <profile>
       <!--
-       | m2e profile - force use of JarJar inside Eclipse
+       | m2e profile - enable use of JarJar inside Eclipse
       -->
       <id>m2e</id>
       <activation>
diff --git a/core/src/com/google/inject/AbstractModule.java b/core/src/com/google/inject/AbstractModule.java
index 4b102ad..fafbd12 100644
--- a/core/src/com/google/inject/AbstractModule.java
+++ b/core/src/com/google/inject/AbstractModule.java
@@ -256,11 +256,11 @@ public abstract class AbstractModule implements Module {
   }
   
   /**
-   * @see Binder#bindListener(Matcher, ProvisionListener)
+   * @see Binder#bindListener(Matcher, ProvisionListener...)
    * @since 4.0
    */
-  protected void bindListener(Matcher<? super Key<?>> keyMatcher,
+  protected void bindListener(Matcher<? super Binding<?>> bindingMatcher,
       ProvisionListener... listener) {
-    binder().bindListener(keyMatcher, listener);
+    binder().bindListener(bindingMatcher, listener);
   }
 }
diff --git a/core/src/com/google/inject/Binder.java b/core/src/com/google/inject/Binder.java
index cb58ca6..dae3812 100644
--- a/core/src/com/google/inject/Binder.java
+++ b/core/src/com/google/inject/Binder.java
@@ -383,12 +383,12 @@ public interface Binder {
    * through Providers) can also be notified through TypeListeners registered in
    * {@link #bindListener}.
    * 
-   * @param keyMatcher that matches keys of provisioned objects the listener
+   * @param bindingMatcher that matches bindings of provisioned objects the listener
    *          should be notified of
-   * @param listeners for provisioned objects matched by keyMatcher   * 
+   * @param listeners for provisioned objects matched by bindingMatcher 
    * @since 4.0
    */
-  void bindListener(Matcher<? super Key<?>> keyMatcher, ProvisionListener... listeners);
+  void bindListener(Matcher<? super Binding<?>> bindingMatcher, ProvisionListener... listeners);
 
   /**
    * Returns a binder that uses {@code source} as the reference location for
@@ -470,4 +470,27 @@ public interface Binder {
    * @since 3.0
    */
   void disableCircularProxies();
+  
+  /**
+   * Requires that a {@literal @}{@link Inject} annotation exists on a constructor in order for
+   * Guice to consider it an eligible injectable class. By default, Guice will inject classes that
+   * have a no-args constructor if no {@literal @}{@link Inject} annotation exists on any
+   * constructor.
+   * <p>
+   * If the class is bound using {@link LinkedBindingBuilder#toConstructor}, Guice will still inject
+   * that constructor regardless of annotations.
+   *
+   * @since 4.0
+   */
+  void requireAtInjectOnConstructors();
+
+  /**
+   * Requires that Guice finds an exactly matching binding annotation.  This disables the
+   * error-prone feature in Guice where it can substitute a binding for
+   * <code>{@literal @}Named Foo</code> when attempting to inject
+   * <code>{@literal @}Named("foo") Foo</code>.
+   *
+   * @since 4.0
+   */
+  void requireExactBindingAnnotations();
 }
diff --git a/core/src/com/google/inject/Inject.java b/core/src/com/google/inject/Inject.java
index e96dfa9..e3bbd27 100644
--- a/core/src/com/google/inject/Inject.java
+++ b/core/src/com/google/inject/Inject.java
@@ -34,7 +34,7 @@ import java.lang.annotation.Target;
  * <li>Every instance it constructs. The class being constructed must have
  * exactly one of its constructors marked with {@code @Inject} or must have a
  * constructor taking no parameters. The Injector then proceeds to perform
- * method and field injections.
+ * field and method injections.
  * 
  * <li>Pre-constructed instances passed to {@link Injector#injectMembers},
  * {@link com.google.inject.binder.LinkedBindingBuilder#toInstance(Object)} and
diff --git a/core/src/com/google/inject/Key.java b/core/src/com/google/inject/Key.java
index 8984120..7ed5e20 100644
--- a/core/src/com/google/inject/Key.java
+++ b/core/src/com/google/inject/Key.java
@@ -18,6 +18,8 @@ package com.google.inject;
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.inject.internal.Annotations.generateAnnotation;
+import static com.google.inject.internal.Annotations.isAllDefaultMethods;
 
 import com.google.inject.internal.Annotations;
 import com.google.inject.internal.MoreTypes;
@@ -70,7 +72,8 @@ public class Key<T> {
   @SuppressWarnings("unchecked")
   protected Key(Class<? extends Annotation> annotationType) {
     this.annotationStrategy = strategyFor(annotationType);
-    this.typeLiteral = (TypeLiteral<T>) TypeLiteral.fromSuperclassTypeParameter(getClass());
+    this.typeLiteral = MoreTypes.canonicalizeForKey(
+        (TypeLiteral<T>) TypeLiteral.fromSuperclassTypeParameter(getClass()));
     this.hashCode = computeHashCode();
   }
 
@@ -90,7 +93,8 @@ public class Key<T> {
   protected Key(Annotation annotation) {
     // no usages, not test-covered
     this.annotationStrategy = strategyFor(annotation);
-    this.typeLiteral = (TypeLiteral<T>) TypeLiteral.fromSuperclassTypeParameter(getClass());
+    this.typeLiteral = MoreTypes.canonicalizeForKey(
+        (TypeLiteral<T>) TypeLiteral.fromSuperclassTypeParameter(getClass()));
     this.hashCode = computeHashCode();
   }
 
@@ -108,7 +112,8 @@ public class Key<T> {
   @SuppressWarnings("unchecked")
   protected Key() {
     this.annotationStrategy = NullAnnotationStrategy.INSTANCE;
-    this.typeLiteral = (TypeLiteral<T>) TypeLiteral.fromSuperclassTypeParameter(getClass());
+    this.typeLiteral = MoreTypes.canonicalizeForKey(
+        (TypeLiteral<T>) TypeLiteral.fromSuperclassTypeParameter(getClass()));
     this.hashCode = computeHashCode();
   }
 
@@ -129,6 +134,11 @@ public class Key<T> {
     this.hashCode = computeHashCode();
   }
 
+  /**
+   * Computes the hash code for this key. This logic is duplicated in {@link
+   * com.google.inject.internal.RehashableKeys.Keys#needsRehashing}; if it is
+   * changed here, be sure to change it there also.
+   */
   private int computeHashCode() {
     return typeLiteral.hashCode() * 31 + annotationStrategy.hashCode();
   }
@@ -277,7 +287,7 @@ public class Key<T> {
   /**
    * Returns a new key of the specified type with the same annotation as this
    * key.
-   * 
+   *
    * @since 3.0
    */
   public <T> Key<T> ofType(Class<T> type) {
@@ -287,7 +297,7 @@ public class Key<T> {
   /**
    * Returns a new key of the specified type with the same annotation as this
    * key.
-   * 
+   *
    * @since 3.0
    */
   public Key<?> ofType(Type type) {
@@ -297,7 +307,7 @@ public class Key<T> {
   /**
    * Returns a new key of the specified type with the same annotation as this
    * key.
-   * 
+   *
    * @since 3.0
    */
   public <T> Key<T> ofType(TypeLiteral<T> type) {
@@ -306,7 +316,7 @@ public class Key<T> {
 
   /**
    * Returns true if this key has annotation attributes.
-   * 
+   *
    * @since 3.0
    */
   public boolean hasAttributes() {
@@ -316,7 +326,7 @@ public class Key<T> {
   /**
    * Returns this key without annotation attributes, i.e. with only the
    * annotation type.
-   * 
+   *
    * @since 3.0
    */
   public Key<T> withoutAttributes() {
@@ -350,10 +360,15 @@ public class Key<T> {
    * Gets the strategy for an annotation type.
    */
   static AnnotationStrategy strategyFor(Class<? extends Annotation> annotationType) {
+    annotationType = Annotations.canonicalizeIfNamed(annotationType);
+    if (isAllDefaultMethods(annotationType)) {
+      return strategyFor(generateAnnotation(annotationType));
+    }
+
     checkNotNull(annotationType, "annotation type");
     ensureRetainedAtRuntime(annotationType);
     ensureIsBindingAnnotation(annotationType);
-    return new AnnotationTypeStrategy(Annotations.canonicalizeIfNamed(annotationType), null);
+    return new AnnotationTypeStrategy(annotationType, null);
 
   }
 
diff --git a/core/src/com/google/inject/PrivateModule.java b/core/src/com/google/inject/PrivateModule.java
index fcc7933..cf93f1c 100644
--- a/core/src/com/google/inject/PrivateModule.java
+++ b/core/src/com/google/inject/PrivateModule.java
@@ -24,6 +24,7 @@ import com.google.inject.binder.AnnotatedElementBuilder;
 import com.google.inject.binder.LinkedBindingBuilder;
 import com.google.inject.matcher.Matcher;
 import com.google.inject.spi.Message;
+import com.google.inject.spi.ProvisionListener;
 import com.google.inject.spi.TypeConverter;
 import com.google.inject.spi.TypeListener;
 
@@ -293,4 +294,12 @@ public abstract class PrivateModule implements Module {
       TypeListener listener) {
     binder().bindListener(typeMatcher, listener);
   }
+  
+  /**
+   * @see Binder#bindListener(Matcher, ProvisionListener...)
+   */
+  protected void bindListener(Matcher<? super Binding<?>> bindingMatcher,
+      ProvisionListener... listeners) {
+    binder().bindListener(bindingMatcher, listeners);
+  }
 }
diff --git a/core/src/com/google/inject/Provides.java b/core/src/com/google/inject/Provides.java
index ca7aa65..c66a428 100644
--- a/core/src/com/google/inject/Provides.java
+++ b/core/src/com/google/inject/Provides.java
@@ -25,7 +25,7 @@ import java.lang.annotation.Target;
 
 /**
  * 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.
+ * type is bound to its returned value. Guice will pass dependencies to the method as parameters.
  *
  * @author crazybob at google.com (Bob Lee)
  * @since 2.0
diff --git a/core/src/com/google/inject/ProvisionException.java b/core/src/com/google/inject/ProvisionException.java
index 4d6afcd..e239446 100644
--- a/core/src/com/google/inject/ProvisionException.java
+++ b/core/src/com/google/inject/ProvisionException.java
@@ -18,7 +18,6 @@ package com.google.inject;
 
 import static com.google.common.base.Preconditions.checkArgument;
 
-import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.inject.internal.Errors;
 import com.google.inject.spi.Message;
@@ -45,7 +44,7 @@ public final class ProvisionException extends RuntimeException {
 
   public ProvisionException(String message, Throwable cause) {
     super(cause);
-    this.messages = ImmutableSet.of(new Message(ImmutableList.of(), message, cause));
+    this.messages = ImmutableSet.of(new Message(message, cause));
   }
 
   public ProvisionException(String message) {
diff --git a/core/src/com/google/inject/internal/AbstractBindingProcessor.java b/core/src/com/google/inject/internal/AbstractBindingProcessor.java
index 8fe03de..bbb9930 100644
--- a/core/src/com/google/inject/internal/AbstractBindingProcessor.java
+++ b/core/src/com/google/inject/internal/AbstractBindingProcessor.java
@@ -26,6 +26,7 @@ import com.google.inject.MembersInjector;
 import com.google.inject.Module;
 import com.google.inject.Provider;
 import com.google.inject.Scope;
+import com.google.inject.Stage;
 import com.google.inject.TypeLiteral;
 import com.google.inject.spi.DefaultBindingTargetVisitor;
 
@@ -65,8 +66,8 @@ abstract class AbstractBindingProcessor extends AbstractProcessor {
       Module.class,
       Provider.class,
       Scope.class,
+      Stage.class,
       TypeLiteral.class);
-  // TODO(jessewilson): fix BuiltInModule, then add Stage
   
   protected final ProcessedBindingData bindingData;
   
diff --git a/core/src/com/google/inject/internal/Annotations.java b/core/src/com/google/inject/internal/Annotations.java
index 982bc0b..dfc6936 100644
--- a/core/src/com/google/inject/internal/Annotations.java
+++ b/core/src/com/google/inject/internal/Annotations.java
@@ -17,7 +17,14 @@
 package com.google.inject.internal;
 
 import com.google.common.base.Function;
-import com.google.common.collect.MapMaker;
+import com.google.common.base.Joiner;
+import com.google.common.base.Joiner.MapJoiner;
+import com.google.common.base.Preconditions;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
 import com.google.inject.BindingAnnotation;
 import com.google.inject.Key;
 import com.google.inject.ScopeAnnotation;
@@ -29,7 +36,10 @@ import com.google.inject.name.Names;
 import java.lang.annotation.Annotation;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Map;
@@ -50,6 +60,115 @@ public class Annotations {
     return annotationType.getDeclaredMethods().length == 0;
   }
 
+  public static boolean isAllDefaultMethods(Class<? extends Annotation> annotationType) {
+    boolean hasMethods = false;
+    for (Method m : annotationType.getDeclaredMethods()) {
+      hasMethods = true;
+      if (m.getDefaultValue() == null) {
+        return false;
+      }
+    }
+    return hasMethods;
+  }
+
+  private static final LoadingCache<Class<? extends Annotation>, Annotation> cache =
+      CacheBuilder.newBuilder().weakKeys().build(
+          new CacheLoader<Class<? extends Annotation>, Annotation>() {
+            @Override
+            public Annotation load(Class<? extends Annotation> input) {
+              return generateAnnotationImpl(input);
+            }
+          });
+
+  /**
+   * Generates an Annotation for the annotation class. Requires that the annotation is all
+   * optionals.
+   */
+  public static <T extends Annotation> T generateAnnotation(Class<T> annotationType) {
+    Preconditions.checkState(
+        isAllDefaultMethods(annotationType), "%s is not all default methods", annotationType);
+    return (T)cache.getUnchecked(annotationType);
+  }
+
+  private static <T extends Annotation> T generateAnnotationImpl(final Class<T> annotationType) {
+    final Map<String, Object> members = resolveMembers(annotationType);
+    return annotationType.cast(Proxy.newProxyInstance(
+        annotationType.getClassLoader(),
+        new Class<?>[] { annotationType },
+        new InvocationHandler() {
+          @Override
+          public Object invoke(Object proxy, Method method, Object[] args) throws Exception {
+            String name = method.getName();
+            if (name.equals("annotationType")) {
+              return annotationType;
+            } else if (name.equals("toString")) {
+              return annotationToString(annotationType, members);
+            } else if (name.equals("hashCode")) {
+              return annotationHashCode(annotationType, members);
+            } else if (name.equals("equals")) {
+              return annotationEquals(annotationType, members, args[0]);
+            } else {
+              return members.get(name);
+            }
+          }
+        }));
+  }
+
+  private static ImmutableMap<String, Object> resolveMembers(
+      Class<? extends Annotation> annotationType) {
+    ImmutableMap.Builder<String, Object> result = ImmutableMap.builder();
+    for (Method method : annotationType.getDeclaredMethods()) {
+      result.put(method.getName(), method.getDefaultValue());
+    }
+    return result.build();
+  }
+
+  /** Implements {@link Annotation#equals}. */
+  private static boolean annotationEquals(Class<? extends Annotation> type,
+      Map<String, Object> members, Object other) throws Exception {
+    if (!type.isInstance(other)) {
+      return false;
+    }
+    for (Method method : type.getDeclaredMethods()) {
+      String name = method.getName();
+      if (!Arrays.deepEquals(
+          new Object[] {method.invoke(other)}, new Object[] {members.get(name)})) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  /** Implements {@link Annotation#hashCode}. */
+  private static int annotationHashCode(Class<? extends Annotation> type,
+      Map<String, Object> members) throws Exception {
+    int result = 0;
+    for (Method method : type.getDeclaredMethods()) {
+      String name = method.getName();
+      Object value = members.get(name);
+      result += (127 * name.hashCode()) ^ (Arrays.deepHashCode(new Object[] {value}) - 31);
+    }
+    return result;
+  }
+
+  private static final MapJoiner JOINER = Joiner.on(", ").withKeyValueSeparator("=");
+
+  private static final Function<Object, String> DEEP_TO_STRING_FN = new Function<Object, String>() {
+    @Override
+    public String apply(Object arg) {
+      String s = Arrays.deepToString(new Object[] {arg});
+      return s.substring(1, s.length() - 1); // cut off brackets
+    }
+  };
+
+  /** Implements {@link Annotation#toString}. */
+  private static String annotationToString(Class<? extends Annotation> type,
+      Map<String, Object> members) throws Exception {
+    StringBuilder sb = new StringBuilder().append("@").append(type.getName()).append("(");
+    JOINER.appendTo(sb, Maps.transformValues(members, DEEP_TO_STRING_FN));
+    return sb.append(")").toString();
+  }
+
   /**
    * Returns true if the given annotation is retained at runtime.
    */
@@ -89,9 +208,9 @@ public class Annotations {
     private final Collection<Class<? extends Annotation>> annotationTypes;
 
     /** Returns true if the given class has one of the desired annotations. */
-    private Function<Class<? extends Annotation>, Boolean> hasAnnotations =
-        new Function<Class<? extends Annotation>, Boolean>() {
-      public Boolean apply(Class<? extends Annotation> annotationType) {
+    private CacheLoader<Class<? extends Annotation>, Boolean> hasAnnotations =
+        new CacheLoader<Class<? extends Annotation>, Boolean>() {
+      public Boolean load(Class<? extends Annotation> annotationType) {
         for (Annotation annotation : annotationType.getAnnotations()) {
           if (annotationTypes.contains(annotation.annotationType())) {
             return true;
@@ -101,8 +220,8 @@ public class Annotations {
       }
     };
 
-    final Map<Class<? extends Annotation>, Boolean> cache = new MapMaker().weakKeys()
-        .makeComputingMap(hasAnnotations);
+    final LoadingCache<Class<? extends Annotation>, Boolean> cache = CacheBuilder.newBuilder().weakKeys()
+        .build(hasAnnotations);
 
     /**
      * Constructs a new checker that looks for annotations of the given types.
@@ -115,7 +234,7 @@ public class Annotations {
      * Returns true if the given type has one of the desired annotations.
      */
     boolean hasAnnotations(Class<? extends Annotation> annotated) {
-      return cache.get(annotated);
+      return cache.getUnchecked(annotated);
     }
   }
 
@@ -188,7 +307,7 @@ public class Annotations {
    */
   public static Annotation canonicalizeIfNamed(Annotation annotation) {
     if(annotation instanceof javax.inject.Named) {
-      return Names.named(((javax.inject.Named)annotation).value());       
+      return Names.named(((javax.inject.Named)annotation).value());
     } else {
       return annotation;
     }
diff --git a/core/src/com/google/inject/internal/BindingBuilder.java b/core/src/com/google/inject/internal/BindingBuilder.java
index 7b89dcc..a6260f3 100644
--- a/core/src/com/google/inject/internal/BindingBuilder.java
+++ b/core/src/com/google/inject/internal/BindingBuilder.java
@@ -41,7 +41,7 @@ import java.util.Set;
  * @author jessewilson at google.com (Jesse Wilson)
  */
 public class BindingBuilder<T> extends AbstractBindingBuilder<T>
-    implements AnnotatedBindingBuilder<T> {
+    implements AnnotatedBindingBuilder<T>, RehashableKeys {
 
   public BindingBuilder(Binder binder, List<Element> elements, Object source, Key<T> key) {
     super(binder, elements, source, key);
@@ -157,7 +157,6 @@ public class BindingBuilder<T> extends AbstractBindingBuilder<T>
     }
 
     try {
-      @SuppressWarnings("unchecked") // safe; constructor is a subtype of toConstruct
       InjectionPoint constructorPoint = InjectionPoint.forConstructor(constructor, type);
       setBinding(new ConstructorBindingImpl<T>(base.getKey(), base.getSource(), base.getScoping(),
           constructorPoint, injectionPoints));
@@ -167,6 +166,10 @@ public class BindingBuilder<T> extends AbstractBindingBuilder<T>
 
     return this;
   }
+  
+  public void rehashKeys() {
+    setBinding(getBinding().withRehashedKeys());
+  }
 
   @Override public String toString() {
     return "BindingBuilder<" + getBinding().getKey().getTypeLiteral() + ">";
diff --git a/core/src/com/google/inject/internal/BindingImpl.java b/core/src/com/google/inject/internal/BindingImpl.java
index 9bcb7e1..7319dd6 100644
--- a/core/src/com/google/inject/internal/BindingImpl.java
+++ b/core/src/com/google/inject/internal/BindingImpl.java
@@ -104,6 +104,10 @@ public abstract class BindingImpl<T> implements Binding<T> {
   protected BindingImpl<T> withKey(Key<T> key) {
     throw new AssertionError();
   }
+  
+  public BindingImpl<T> withRehashedKeys() {
+    throw new AssertionError();
+  }
 
   @Override public String toString() {
     return Objects.toStringHelper(Binding.class)
diff --git a/core/src/com/google/inject/internal/BindingProcessor.java b/core/src/com/google/inject/internal/BindingProcessor.java
index 952cfaa..7ce3ea0 100644
--- a/core/src/com/google/inject/internal/BindingProcessor.java
+++ b/core/src/com/google/inject/internal/BindingProcessor.java
@@ -67,11 +67,12 @@ final class BindingProcessor extends AbstractBindingProcessor {
     }
     
     return command.acceptTargetVisitor(new Processor<T, Boolean>((BindingImpl<T>)command) {
+      @Override
       public Boolean visit(ConstructorBinding<? extends T> binding) {
         prepareBinding();
         try {
           ConstructorBindingImpl<T> onInjector = ConstructorBindingImpl.create(injector, key, 
-              binding.getConstructor(), source, scoping, errors, false);
+              binding.getConstructor(), source, scoping, errors, false, false);
           scheduleInitialization(onInjector);
           putBinding(onInjector);
         } catch (ErrorsException e) {
@@ -81,12 +82,15 @@ final class BindingProcessor extends AbstractBindingProcessor {
         return true;
       }
 
+      @Override
       public Boolean visit(InstanceBinding<? extends T> binding) {
         prepareBinding();
         Set<InjectionPoint> injectionPoints = binding.getInjectionPoints();
         T instance = binding.getInstance();
+        @SuppressWarnings("unchecked") // safe to cast to binding<T> because
+                                       // the processor was constructed w/ it
         Initializable<T> ref = initializer.requestInjection(
-            injector, instance, key, source, injectionPoints);
+            injector, instance, (Binding<T>) binding, source, injectionPoints);
         ConstantFactory<? extends T> factory = new ConstantFactory<T>(ref);
         InternalFactory<? extends T> scopedFactory
             = Scoping.scope(key, injector, factory, source, scoping);
@@ -95,15 +99,18 @@ final class BindingProcessor extends AbstractBindingProcessor {
         return true;
       }
 
+      @Override
       public Boolean visit(ProviderInstanceBinding<? extends T> binding) {
         prepareBinding();
         Provider<? extends T> provider = binding.getProviderInstance();
         Set<InjectionPoint> injectionPoints = binding.getInjectionPoints();
         Initializable<Provider<? extends T>> initializable = initializer
             .<Provider<? extends T>>requestInjection(injector, provider, null, source, injectionPoints);
+        // always visited with Binding<T>
+        @SuppressWarnings("unchecked") 
         InternalFactory<T> factory = new InternalFactoryToInitializableAdapter<T>(
             initializable, source, !injector.options.disableCircularProxies,
-            injector.provisionListenerStore.get(key));
+            injector.provisionListenerStore.get((ProviderInstanceBinding<T>)binding));
         InternalFactory<? extends T> scopedFactory
             = Scoping.scope(key, injector, factory, source, scoping);
         putBinding(new ProviderInstanceBindingImpl<T>(injector, key, source, scopedFactory, scoping,
@@ -111,12 +118,15 @@ final class BindingProcessor extends AbstractBindingProcessor {
         return true;
       }
 
+      @Override
       public Boolean visit(ProviderKeyBinding<? extends T> binding) {
         prepareBinding();
         Key<? extends javax.inject.Provider<? extends T>> providerKey = binding.getProviderKey();
+        // always visited with Binding<T>
+        @SuppressWarnings("unchecked") 
         BoundProviderFactory<T> boundProviderFactory = new BoundProviderFactory<T>(
             injector, providerKey, source, !injector.options.disableCircularProxies,
-            injector.provisionListenerStore.get(key));
+            injector.provisionListenerStore.get((ProviderKeyBinding<T>)binding));
         bindingData.addCreationListener(boundProviderFactory);
         InternalFactory<? extends T> scopedFactory = Scoping.scope(
             key, injector, (InternalFactory<? extends T>) boundProviderFactory, source, scoping);
@@ -125,6 +135,7 @@ final class BindingProcessor extends AbstractBindingProcessor {
         return true;
       }
 
+      @Override
       public Boolean visit(LinkedKeyBinding<? extends T> binding) {
         prepareBinding();
         Key<? extends T> linkedKey = binding.getLinkedKey();
@@ -141,18 +152,22 @@ final class BindingProcessor extends AbstractBindingProcessor {
         return true;
       }
 
+      @Override
       public Boolean visit(UntargettedBinding<? extends T> untargetted) {
         return false;
       }
 
+      @Override
       public Boolean visit(ExposedBinding<? extends T> binding) {
         throw new IllegalArgumentException("Cannot apply a non-module element");
       }
-
+      
+      @Override
       public Boolean visit(ConvertedConstantBinding<? extends T> binding) {
         throw new IllegalArgumentException("Cannot apply a non-module element");
       }
-
+      
+      @Override
       public Boolean visit(ProviderBinding<? extends T> binding) {
         throw new IllegalArgumentException("Cannot apply a non-module element");
       }
diff --git a/core/src/com/google/inject/internal/BoundProviderFactory.java b/core/src/com/google/inject/internal/BoundProviderFactory.java
index 97ce323..27ae1aa 100644
--- a/core/src/com/google/inject/internal/BoundProviderFactory.java
+++ b/core/src/com/google/inject/internal/BoundProviderFactory.java
@@ -16,6 +16,8 @@
 
 package com.google.inject.internal;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
 import com.google.inject.Key;
 import com.google.inject.internal.InjectorImpl.JitLimitation;
 import com.google.inject.spi.Dependency;
@@ -27,6 +29,7 @@ import javax.inject.Provider;
  */
 final class BoundProviderFactory<T> extends ProviderInternalFactory<T> implements CreationListener {
 
+  private final ProvisionListenerStackCallback<T> provisionCallback;
   private final InjectorImpl injector;
   final Key<? extends javax.inject.Provider<? extends T>> providerKey;
   private InternalFactory<? extends javax.inject.Provider<? extends T>> providerFactory;
@@ -37,7 +40,8 @@ final class BoundProviderFactory<T> extends ProviderInternalFactory<T> implement
       Object source,
       boolean allowProxy,
       ProvisionListenerStackCallback<T> provisionCallback) {
-    super(source, allowProxy, provisionCallback);
+    super(source, allowProxy);
+    this.provisionCallback = checkNotNull(provisionCallback, "provisionCallback");
     this.injector = injector;
     this.providerKey = providerKey;
   }
@@ -56,7 +60,7 @@ final class BoundProviderFactory<T> extends ProviderInternalFactory<T> implement
     try {
       errors = errors.withSource(providerKey);
       javax.inject.Provider<? extends T> provider = providerFactory.get(errors, context, dependency, true);
-      return circularGet(provider, errors, context, dependency, linked);
+      return circularGet(provider, errors, context, dependency, linked, provisionCallback);
     } finally {
       context.popState();
     }
diff --git a/core/src/com/google/inject/internal/BytecodeGen.java b/core/src/com/google/inject/internal/BytecodeGen.java
index 55ad161..f0ebc0a 100644
--- a/core/src/com/google/inject/internal/BytecodeGen.java
+++ b/core/src/com/google/inject/internal/BytecodeGen.java
@@ -16,9 +16,9 @@
 
 package com.google.inject.internal;
 
-import com.google.common.base.Function;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.MapMaker;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
 
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Member;
@@ -26,7 +26,6 @@ import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
-import java.util.Map;
 import java.util.logging.Logger;
 
 /**
@@ -83,7 +82,7 @@ public final class BytecodeGen {
     @Override protected String getTag() {
       return "ByGuice";
     }
-    
+
     @Override
     public String getClassName(String prefix, String source, Object key,
         net.sf.cglib.core.Predicate names) {
@@ -94,7 +93,7 @@ public final class BytecodeGen {
       return super.getClassName(prefix, "FastClass", key, names);
     }
   };
-  
+
   static final net.sf.cglib.core.NamingPolicy ENHANCER_NAMING_POLICY
       = new net.sf.cglib.core.DefaultNamingPolicy() {
     @Override
@@ -124,7 +123,7 @@ public final class BytecodeGen {
    * Weak cache of bridge class loaders that make the Guice implementation
    * classes visible to various code-generated proxies of client classes.
    */
-  private static final Map<ClassLoader, ClassLoader> CLASS_LOADER_CACHE;
+  private static final LoadingCache<ClassLoader, ClassLoader> CLASS_LOADER_CACHE;
 
   static {
     boolean customLoaderEnabled;
@@ -135,21 +134,21 @@ public final class BytecodeGen {
     }
     CUSTOM_LOADER_ENABLED = customLoaderEnabled;
 
-    if (CUSTOM_LOADER_ENABLED) {
-      CLASS_LOADER_CACHE = new MapMaker().weakKeys().weakValues().makeComputingMap(
-          new Function<ClassLoader, ClassLoader>() {
-            public ClassLoader apply(final ClassLoader typeClassLoader) {
-              logger.fine("Creating a bridge ClassLoader for " + typeClassLoader);
-              return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
-                public ClassLoader run() {
-                  return new BridgeClassLoader(typeClassLoader);
-                }
-              });
-            }
-          });
-    } else {
-      CLASS_LOADER_CACHE = ImmutableMap.of();
+    CacheBuilder<Object, Object> builder = CacheBuilder.newBuilder().weakKeys().weakValues();
+    if (!CUSTOM_LOADER_ENABLED) {
+      builder.maximumSize(0);
     }
+    CLASS_LOADER_CACHE = builder.build(
+        new CacheLoader<ClassLoader, ClassLoader>() {
+          public ClassLoader load(final ClassLoader typeClassLoader) {
+            logger.fine("Creating a bridge ClassLoader for " + typeClassLoader);
+            return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
+              public ClassLoader run() {
+                return new BridgeClassLoader(typeClassLoader);
+              }
+            });
+          }
+        });
   }
 
   /**
@@ -173,7 +172,7 @@ public final class BytecodeGen {
     if (!CUSTOM_LOADER_ENABLED) {
       return delegate;
     }
-    
+
     // java.* types can be seen everywhere
     if (type.getName().startsWith("java.")) {
       return GUICE_CLASS_LOADER;
@@ -190,7 +189,7 @@ public final class BytecodeGen {
     if (Visibility.forType(type) == Visibility.PUBLIC) {
       if (delegate != SystemBridgeHolder.SYSTEM_BRIDGE.getParent()) {
         // delegate guaranteed to be non-null here
-        return CLASS_LOADER_CACHE.get(delegate);
+        return CLASS_LOADER_CACHE.getUnchecked(delegate);
       }
       // delegate may or may not be null here
       return SystemBridgeHolder.SYSTEM_BRIDGE;
diff --git a/core/src/com/google/inject/internal/ConstructorBindingImpl.java b/core/src/com/google/inject/internal/ConstructorBindingImpl.java
index 19a2c9c..3a89154 100644
--- a/core/src/com/google/inject/internal/ConstructorBindingImpl.java
+++ b/core/src/com/google/inject/internal/ConstructorBindingImpl.java
@@ -18,11 +18,14 @@ package com.google.inject.internal;
 
 import static com.google.common.base.Preconditions.checkState;
 import static com.google.inject.internal.Annotations.findScopeAnnotation;
+import static com.google.inject.internal.RehashableKeys.Keys.needsRehashing;
+import static com.google.inject.internal.RehashableKeys.Keys.rehash;
 
 import com.google.common.base.Objects;
 import com.google.common.collect.ImmutableSet;
 import com.google.inject.Binder;
 import com.google.inject.ConfigurationException;
+import com.google.inject.Inject;
 import com.google.inject.Key;
 import com.google.inject.TypeLiteral;
 import com.google.inject.internal.util.Classes;
@@ -69,14 +72,14 @@ final class ConstructorBindingImpl<T> extends BindingImpl<T>
    * @param failIfNotLinked true if this ConstructorBindingImpl's InternalFactory should
    *                             only succeed if retrieved from a linked binding
    */
-  static <T> ConstructorBindingImpl<T> create(InjectorImpl injector, Key<T> key, 
+  static <T> ConstructorBindingImpl<T> create(InjectorImpl injector, Key<T> key,
       InjectionPoint constructorInjector, Object source, Scoping scoping, Errors errors,
-      boolean failIfNotLinked)
+      boolean failIfNotLinked, boolean failIfNotExplicit)
       throws ErrorsException {
     int numErrors = errors.size();
 
     @SuppressWarnings("unchecked") // constructorBinding guarantees type is consistent
-    Class<? super T> rawType = constructorInjector == null 
+    Class<? super T> rawType = constructorInjector == null
         ? key.getTypeLiteral().getRawType()
         : (Class) constructorInjector.getDeclaringType().getRawType();
 
@@ -96,6 +99,9 @@ final class ConstructorBindingImpl<T> extends BindingImpl<T>
     if (constructorInjector == null) {
       try {
         constructorInjector = InjectionPoint.forConstructorOf(key.getTypeLiteral());
+        if (failIfNotExplicit && !hasAtInject((Constructor) constructorInjector.getMember())) {
+          errors.atInjectRequired(rawType);
+        }
       } catch (ConfigurationException e) {
         throw errors.merge(e.getErrorMessages()).toException();
       }
@@ -121,15 +127,21 @@ final class ConstructorBindingImpl<T> extends BindingImpl<T>
         injector, key, source, scopedFactory, scoping, factoryFactory, constructorInjector);
   }
 
+  /** Returns true if the inject annotation is on the constructor. */
+  private static boolean hasAtInject(Constructor cxtor) {
+    return cxtor.isAnnotationPresent(Inject.class)
+        || cxtor.isAnnotationPresent(javax.inject.Inject.class);
+  }
+
   @SuppressWarnings("unchecked") // the result type always agrees with the ConstructorInjector type
   public void initialize(InjectorImpl injector, Errors errors) throws ErrorsException {
     factory.allowCircularProxy = !injector.options.disableCircularProxies;
     factory.constructorInjector =
         (ConstructorInjector<T>) injector.constructors.get(constructorInjectionPoint, errors);
     factory.provisionCallback =
-      injector.provisionListenerStore.get(getKey());
+      injector.provisionListenerStore.get(this);
   }
-  
+
   /** True if this binding has been initialized and is ready for use. */
   boolean isInitialized() {
     return factory.constructorInjector != null;
@@ -143,7 +155,7 @@ final class ConstructorBindingImpl<T> extends BindingImpl<T>
       return constructorInjectionPoint;
     }
   }
-  
+
   /** Returns a set of dependencies that can be iterated over to clean up stray JIT bindings. */
   Set<Dependency<?>> getInternalDependencies() {
     ImmutableSet.Builder<InjectionPoint> builder = ImmutableSet.builder();
@@ -158,10 +170,10 @@ final class ConstructorBindingImpl<T> extends BindingImpl<T>
       builder.add(getConstructor())
              .addAll(getInjectableMembers());
     }
-    
-    return Dependency.forInjectionPoints(builder.build());   
+
+    return Dependency.forInjectionPoints(builder.build());
   }
-  
+
   public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
     checkState(factory.constructorInjector != null, "not initialized");
     return visitor.visit(this);
@@ -201,6 +213,14 @@ final class ConstructorBindingImpl<T> extends BindingImpl<T>
         null, key, getSource(), factory, getScoping(), factory, constructorInjectionPoint);
   }
 
+  @Override public BindingImpl<T> withRehashedKeys() {
+    if (needsRehashing(getKey())) {
+      return withKey(rehash(getKey()));
+    } else {
+      return this;
+    }
+  }
+
   @SuppressWarnings("unchecked") // the raw constructor member and declaring type always agree
   public void applyTo(Binder binder) {
     InjectionPoint constructor = getConstructor();
@@ -215,7 +235,7 @@ final class ConstructorBindingImpl<T> extends BindingImpl<T>
         .add("scope", getScoping())
         .toString();
   }
-  
+
   @Override
   public boolean equals(Object obj) {
     if(obj instanceof ConstructorBindingImpl) {
@@ -227,7 +247,7 @@ final class ConstructorBindingImpl<T> extends BindingImpl<T>
       return false;
     }
   }
-  
+
   @Override
   public int hashCode() {
     return Objects.hashCode(getKey(), getScoping(), constructorInjectionPoint);
@@ -239,7 +259,7 @@ final class ConstructorBindingImpl<T> extends BindingImpl<T>
     private boolean allowCircularProxy;
     private ConstructorInjector<T> constructorInjector;
     private ProvisionListenerStackCallback<T> provisionCallback;
-    
+
     Factory(boolean failIfNotLinked, Key<?> key) {
       this.failIfNotLinked = failIfNotLinked;
       this.key = key;
@@ -249,7 +269,7 @@ final class ConstructorBindingImpl<T> extends BindingImpl<T>
     public T get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked)
         throws ErrorsException {
       checkState(constructorInjector != null, "Constructor not ready");
-      
+
       if(failIfNotLinked && !linked) {
         throw errors.jitDisabled(key).toException();
       }
diff --git a/core/src/com/google/inject/internal/ConstructorInjector.java b/core/src/com/google/inject/internal/ConstructorInjector.java
index 2c1219b..ca7bb27 100644
--- a/core/src/com/google/inject/internal/ConstructorInjector.java
+++ b/core/src/com/google/inject/internal/ConstructorInjector.java
@@ -81,6 +81,7 @@ final class ConstructorInjector<T> {
       return t;
     }
 
+    constructionContext.startConstruction();
     try {
       // Optimization: Don't go through the callback stack if we have no listeners.
       if (!provisionCallback.hasListeners()) {
@@ -93,7 +94,7 @@ final class ConstructorInjector<T> {
         });
       }
     } finally {
-      constructionContext.removeCurrentReference();
+      constructionContext.finishConstruction();
     }
   }
 
@@ -101,7 +102,6 @@ final class ConstructorInjector<T> {
   private T provision(Errors errors, InternalContext context,
       ConstructionContext<T> constructionContext) throws ErrorsException {
     try {
-      constructionContext.startConstruction();
       T t;
       try {
         Object[] parameters = SingleParameterInjector.getAll(errors, context, parameterInjectors);
@@ -110,13 +110,13 @@ final class ConstructorInjector<T> {
       } finally {
         constructionContext.finishConstruction();
       }
-  
+
       // Store reference. If an injector re-enters this factory, they'll get the same reference.
       constructionContext.setCurrentReference(t);
-  
+
       membersInjector.injectMembers(t, errors, context, false);
       membersInjector.notifyListeners(t, errors);
-  
+
       return t;
     } catch (InvocationTargetException userException) {
       Throwable cause = userException.getCause() != null
@@ -124,6 +124,8 @@ final class ConstructorInjector<T> {
           : userException;
       throw errors.withSource(constructionProxy.getInjectionPoint())
           .errorInjectingConstructor(cause).toException();
+    } finally {
+      constructionContext.removeCurrentReference();
     }
   }
 }
diff --git a/core/src/com/google/inject/internal/DelayedInitialize.java b/core/src/com/google/inject/internal/DelayedInitialize.java
index 80e52d1..82a8463 100644
--- a/core/src/com/google/inject/internal/DelayedInitialize.java
+++ b/core/src/com/google/inject/internal/DelayedInitialize.java
@@ -1,4 +1,18 @@
-// Copyright 2011 Google Inc. All Rights Reserved.
+/**
+ * Copyright (C) 2011 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;
 
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 @@ package com.google.inject.internal;
 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 @@ final class EncounterImpl<T> implements TypeEncounter<T> {
   }
   /*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/Errors.java b/core/src/com/google/inject/internal/Errors.java
index 4eddfe8..d8dd43c 100644
--- a/core/src/com/google/inject/internal/Errors.java
+++ b/core/src/com/google/inject/internal/Errors.java
@@ -31,9 +31,11 @@ import com.google.inject.internal.util.Classes;
 import com.google.inject.internal.util.SourceProvider;
 import com.google.inject.internal.util.StackTraceElements;
 import com.google.inject.spi.Dependency;
+import com.google.inject.spi.ElementSource;
 import com.google.inject.spi.InjectionListener;
 import com.google.inject.spi.InjectionPoint;
 import com.google.inject.spi.Message;
+import com.google.inject.spi.ScopeBinding;
 import com.google.inject.spi.TypeConverterBinding;
 import com.google.inject.spi.TypeListenerBinding;
 
@@ -62,7 +64,7 @@ import java.util.Set;
  * returned instance will contain full context.
  *
  * <p>To avoid messages with redundant context, {@link #withSource} should be added sparingly. A
- * good rule of thumb is to assume a ethod's caller has already specified enough context to
+ * good rule of thumb is to assume a method's caller has already specified enough context to
  * identify that method. When calling a method that's defined in a different context, call that
  * method with an errors object that includes its context.
  *
@@ -113,7 +115,7 @@ public final class Errors implements Serializable {
    * Returns an instance that uses {@code source} as a reference point for newly added errors.
    */
   public Errors withSource(Object source) {
-    return source == SourceProvider.UNKNOWN_SOURCE
+    return source == this.source || source == SourceProvider.UNKNOWN_SOURCE
         ? this
         : new Errors(this, source);
   }
@@ -139,6 +141,13 @@ public final class Errors implements Serializable {
     return addMessage("Explicit bindings are required and %s is not explicitly bound.", key);
   }
 
+  public Errors atInjectRequired(Class clazz) {
+    return addMessage(
+        "Explicit @Inject annotations are required on constructors,"
+        + " but %s has no constructors annotated with @Inject.",
+        clazz);
+  }
+
   public Errors converterReturnedNull(String stringValue, Object source,
       TypeLiteral<?> type, TypeConverterBinding typeConverterBinding) {
     return addMessage("Received null converting '%s' (bound at %s) to %s%n"
@@ -192,13 +201,12 @@ public final class Errors implements Serializable {
     return addMessage("@ProvidedBy points to the same class it annotates.");
   }
 
-  public Errors missingRuntimeRetention(Object source) {
-    return addMessage("Please annotate with @Retention(RUNTIME).%n"
-        + " Bound at %s.", convert(source));
+  public Errors missingRuntimeRetention(Class<? extends Annotation> annotation) {
+    return addMessage(format("Please annotate %s with @Retention(RUNTIME).", annotation));
   }
 
-  public Errors missingScopeAnnotation() {
-    return addMessage("Please annotate with @ScopeAnnotation.");
+  public Errors missingScopeAnnotation(Class<? extends Annotation> annotation) {
+    return addMessage(format("Please annotate %s with @ScopeAnnotation.", annotation));
   }
 
   public Errors optionalConstructor(Constructor constructor) {
@@ -243,10 +251,10 @@ public final class Errors implements Serializable {
     return addMessage("%s does not define %s", type, constructor);
   }
 
-  public Errors duplicateScopes(Scope existing,
+  public Errors duplicateScopes(ScopeBinding existing,
       Class<? extends Annotation> annotationType, Scope scope) {
-    return addMessage("Scope %s is already bound to %s. Cannot bind %s.", existing,
-        annotationType, scope);
+    return addMessage("Scope %s is already bound to %s at %s.%n Cannot bind %s.",
+        existing.getScope(), annotationType, existing.getSource(), scope);
   }
 
   public Errors voidProviderMethod() {
@@ -647,17 +655,17 @@ public final class Errors implements Serializable {
 
   private static final Collection<Converter<?>> converters = ImmutableList.of(
       new Converter<Class>(Class.class) {
-        public String toString(Class c) {
+        @Override public String toString(Class c) {
           return c.getName();
         }
       },
       new Converter<Member>(Member.class) {
-        public String toString(Member member) {
+        @Override public String toString(Member member) {
           return Classes.toString(member);
         }
       },
       new Converter<Key>(Key.class) {
-        public String toString(Key key) {
+        @Override public String toString(Key key) {
           if (key.getAnnotationType() != null) {
             return key.getTypeLiteral() + " annotated with "
                 + (key.getAnnotation() != null ? key.getAnnotation() : key.getAnnotationType());
@@ -668,57 +676,117 @@ public final class Errors implements Serializable {
       });
 
   public static Object convert(Object o) {
+    ElementSource source = null;
+    if (o instanceof ElementSource) {
+      source = (ElementSource)o;
+      o = source.getDeclaringSource();
+    }
+    return convert(o, source);
+  }
+
+  public static Object convert(Object o, ElementSource source) {
     for (Converter<?> converter : converters) {
       if (converter.appliesTo(o)) {
-        return converter.convert(o);
+        return appendModules(converter.convert(o), source);
       }
     }
-    return o;
+    return appendModules(o, source);
+  }
+
+  private static Object appendModules(Object source, ElementSource elementSource) {
+    String modules = moduleSourceString(elementSource);
+    if (modules.length() == 0) {
+      return source;
+    } else {
+      return source + modules;
+    }
+  }
+
+  private static String moduleSourceString(ElementSource elementSource) {
+    // if we only have one module (or don't know what they are), then don't bother
+    // reporting it, because the source already is going to report exactly that module.
+    if (elementSource == null) {
+      return "";
+    }
+    List<String> modules = Lists.newArrayList(elementSource.getModuleClassNames());
+    // Insert any original element sources w/ module info into the path.
+    while(elementSource.getOriginalElementSource() != null) {
+      elementSource = elementSource.getOriginalElementSource();
+      modules.addAll(0, elementSource.getModuleClassNames());
+    }
+    if (modules.size() <= 1) {
+      return "";
+    }
+
+    // Ideally we'd do:
+    //    return Joiner.on(" -> ")
+    //        .appendTo(new StringBuilder(" (via modules: "), Lists.reverse(modules))
+    //        .append(")").toString();
+    // ... but for some reason we can't find Lists.reverse, so do it the boring way.
+    StringBuilder builder = new StringBuilder(" (via modules: ");
+    for (int i = modules.size() - 1; i >= 0; i--) {
+      builder.append(modules.get(i));
+      if (i != 0) {
+        builder.append(" -> ");
+      }
+    }
+    builder.append(")");
+    return builder.toString();
   }
 
   public static void formatSource(Formatter formatter, Object source) {
+    ElementSource elementSource = null;
+    if (source instanceof ElementSource) {
+      elementSource = (ElementSource)source;
+      source = elementSource.getDeclaringSource();
+    }
+    formatSource(formatter, source, elementSource);
+  }
+
+  public static void formatSource(Formatter formatter, Object source, ElementSource elementSource) {
+    String modules = moduleSourceString(elementSource);
     if (source instanceof Dependency) {
       Dependency<?> dependency = (Dependency<?>) source;
       InjectionPoint injectionPoint = dependency.getInjectionPoint();
       if (injectionPoint != null) {
-        formatInjectionPoint(formatter, dependency, injectionPoint);
+        formatInjectionPoint(formatter, dependency, injectionPoint, elementSource);
       } else {
-        formatSource(formatter, dependency.getKey());
+        formatSource(formatter, dependency.getKey(), elementSource);
       }
 
     } else if (source instanceof InjectionPoint) {
-      formatInjectionPoint(formatter, null, (InjectionPoint) source);
+      formatInjectionPoint(formatter, null, (InjectionPoint) source, elementSource);
 
     } else if (source instanceof Class) {
-      formatter.format("  at %s%n", StackTraceElements.forType((Class<?>) source));
+      formatter.format("  at %s%s%n", StackTraceElements.forType((Class<?>) source), modules);
 
     } else if (source instanceof Member) {
-      formatter.format("  at %s%n", StackTraceElements.forMember((Member) source));
+      formatter.format("  at %s%s%n", StackTraceElements.forMember((Member) source), modules);
 
     } else if (source instanceof TypeLiteral) {
-      formatter.format("  while locating %s%n", source);
+      formatter.format("  while locating %s%s%n", source, modules);
 
     } else if (source instanceof Key) {
       Key<?> key = (Key<?>) source;
-      formatter.format("  while locating %s%n", convert(key));
+      formatter.format("  while locating %s%n", convert(key, elementSource));
 
     } else {
-      formatter.format("  at %s%n", source);
+      formatter.format("  at %s%s%n", source, modules);
     }
   }
 
   public static void formatInjectionPoint(Formatter formatter, Dependency<?> dependency,
-      InjectionPoint injectionPoint) {
+      InjectionPoint injectionPoint, ElementSource elementSource) {
     Member member = injectionPoint.getMember();
     Class<? extends Member> memberType = Classes.memberType(member);
 
     if (memberType == Field.class) {
       dependency = injectionPoint.getDependencies().get(0);
-      formatter.format("  while locating %s%n", convert(dependency.getKey()));
+      formatter.format("  while locating %s%n", convert(dependency.getKey(), elementSource));
       formatter.format("    for field at %s%n", StackTraceElements.forMember(member));
 
     } else if (dependency != null) {
-      formatter.format("  while locating %s%n", convert(dependency.getKey()));
+      formatter.format("  while locating %s%n", convert(dependency.getKey(), elementSource));
       formatter.format("    for parameter %s at %s%n",
           dependency.getParameterIndex(), StackTraceElements.forMember(member));
 
diff --git a/core/src/com/google/inject/internal/FailableCache.java b/core/src/com/google/inject/internal/FailableCache.java
index e392e0d..acd8be5 100644
--- a/core/src/com/google/inject/internal/FailableCache.java
+++ b/core/src/com/google/inject/internal/FailableCache.java
@@ -16,10 +16,9 @@
 
 package com.google.inject.internal;
 
-import com.google.common.base.Function;
-import com.google.common.collect.MapMaker;
-
-import java.util.Map;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
 
 /**
  * Lazily creates (and caches) values for keys. If creating the value fails (with errors), an
@@ -29,9 +28,9 @@ import java.util.Map;
  */
 public abstract class FailableCache<K, V> {
   
-  private final Map<K, Object> delegate = new MapMaker().makeComputingMap(
-      new Function<K, Object>() {
-        public Object apply(K key) {
+  private final LoadingCache<K, Object> delegate = CacheBuilder.newBuilder().build(
+      new CacheLoader<K, Object>() {
+        public Object load(K key) {
           Errors errors = new Errors();
           V result = null;
           try {
@@ -46,7 +45,7 @@ public abstract class FailableCache<K, V> {
   protected abstract V create(K key, Errors errors) throws ErrorsException;
   
   public V get(K key, Errors errors) throws ErrorsException {
-    Object resultOrError = delegate.get(key);
+    Object resultOrError = delegate.getUnchecked(key);
     if (resultOrError instanceof Errors) {
       errors.merge((Errors) resultOrError);
       throw errors.toException();
@@ -58,6 +57,6 @@ public abstract class FailableCache<K, V> {
   }
   
   boolean remove(K key) {
-    return delegate.remove(key) != null;
+    return delegate.asMap().remove(key) != null;
   }
 }
diff --git a/core/src/com/google/inject/internal/InheritingState.java b/core/src/com/google/inject/internal/InheritingState.java
index 056adce..2d694cb 100644
--- a/core/src/com/google/inject/internal/InheritingState.java
+++ b/core/src/com/google/inject/internal/InheritingState.java
@@ -19,6 +19,7 @@ package com.google.inject.internal;
 import static com.google.common.base.Preconditions.checkNotNull;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.inject.Binding;
@@ -26,6 +27,7 @@ import com.google.inject.Key;
 import com.google.inject.Scope;
 import com.google.inject.TypeLiteral;
 import com.google.inject.spi.ProvisionListenerBinding;
+import com.google.inject.spi.ScopeBinding;
 import com.google.inject.spi.TypeConverterBinding;
 import com.google.inject.spi.TypeListenerBinding;
 
@@ -47,7 +49,7 @@ final class InheritingState implements State {
   private final Map<Key<?>, Binding<?>> explicitBindingsMutable = Maps.newLinkedHashMap();
   private final Map<Key<?>, Binding<?>> explicitBindings
       = Collections.unmodifiableMap(explicitBindingsMutable);
-  private final Map<Class<? extends Annotation>, Scope> scopes = Maps.newHashMap();
+  private final Map<Class<? extends Annotation>, ScopeBinding> scopes = Maps.newHashMap();
   private final List<TypeConverterBinding> converters = Lists.newArrayList();
   /*if[AOP]*/
   private final List<MethodAspect> methodAspects = Lists.newArrayList();
@@ -80,12 +82,12 @@ final class InheritingState implements State {
     explicitBindingsMutable.put(key, binding);
   }
 
-  public Scope getScope(Class<? extends Annotation> annotationType) {
-    Scope scope = scopes.get(annotationType);
-    return scope != null ? scope : parent.getScope(annotationType);
+  public ScopeBinding getScopeBinding(Class<? extends Annotation> annotationType) {
+    ScopeBinding scopeBinding = scopes.get(annotationType);
+    return scopeBinding != null ? scopeBinding : parent.getScopeBinding(annotationType);
   }
 
-  public void putAnnotation(Class<? extends Annotation> annotationType, Scope scope) {
+  public void putScopeBinding(Class<? extends Annotation> annotationType, ScopeBinding scope) {
     scopes.put(annotationType, scope);
   }
 
@@ -170,6 +172,10 @@ final class InheritingState implements State {
   }
 
   public Map<Class<? extends Annotation>, Scope> getScopes() {
-    return scopes;
+    ImmutableMap.Builder<Class<? extends Annotation>, Scope> builder = ImmutableMap.builder();
+    for (Map.Entry<Class<? extends Annotation>, ScopeBinding> entry : scopes.entrySet()) {
+      builder.put(entry.getKey(), entry.getValue().getScope());
+    }
+    return builder.build();
   }
 }
diff --git a/core/src/com/google/inject/internal/Initializer.java b/core/src/com/google/inject/internal/Initializer.java
index 33dee8d..ff83ea9 100644
--- a/core/src/com/google/inject/internal/Initializer.java
+++ b/core/src/com/google/inject/internal/Initializer.java
@@ -21,6 +21,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
+import com.google.inject.Binding;
 import com.google.inject.Key;
 import com.google.inject.Stage;
 import com.google.inject.TypeLiteral;
@@ -57,21 +58,25 @@ final class Initializer {
    *
    * @param instance an instance that optionally has members to be injected (each annotated with
    *      @Inject).
-   * @param key a key to use for keeping the state of the dependency chain
+   * @param binding the binding that caused this initializable to be created, if it exists.
    * @param source the source location that this injection was requested
    */
-  <T> Initializable<T> requestInjection(InjectorImpl injector, T instance, Key<T> key,
+  <T> Initializable<T> requestInjection(InjectorImpl injector, T instance, Binding<T> binding,
       Object source, Set<InjectionPoint> injectionPoints) {
     checkNotNull(source);
-
-    // short circuit if the object has no injections
-    if (instance == null
-        || (injectionPoints.isEmpty() && !injector.membersInjectorStore.hasTypeListeners())) {
+    
+    ProvisionListenerStackCallback<T> provisionCallback =
+        binding == null ? null : injector.provisionListenerStore.get(binding);
+
+    // short circuit if the object has no injections or listeners.
+    if (instance == null || (injectionPoints.isEmpty()
+        && !injector.membersInjectorStore.hasTypeListeners()
+        && (provisionCallback == null || !provisionCallback.hasListeners()))) {
       return Initializables.of(instance);
     }
 
-    InjectableReference<T> initializable =
-        new InjectableReference<T>(injector, instance, key, source);
+    InjectableReference<T> initializable = new InjectableReference<T>(
+        injector, instance, binding == null ? null : binding.getKey(), provisionCallback, source);
     pendingInjection.put(instance, initializable);
     return initializable;
   }
@@ -118,10 +123,13 @@ final class Initializer {
     private final T instance;
     private final Object source;
     private final Key<T> key;
+    private final ProvisionListenerStackCallback<T> provisionCallback;
 
-    public InjectableReference(InjectorImpl injector, T instance, Key<T> key, Object source) {
+    public InjectableReference(InjectorImpl injector, T instance, Key<T> key,
+        ProvisionListenerStackCallback<T> provisionCallback, Object source) {
       this.injector = injector;
       this.key = key; // possibly null!
+      this.provisionCallback = provisionCallback; // possibly null!
       this.instance = checkNotNull(instance, "instance");
       this.source = checkNotNull(source, "source");
     }
@@ -163,7 +171,11 @@ final class Initializer {
         
         // if in Stage.TOOL, we only want to inject & notify toolable injection points.
         // (otherwise we'll inject all of them)
-        membersInjector.injectAndNotify(instance, errors.withSource(source), key, source, 
+        membersInjector.injectAndNotify(instance,
+            errors.withSource(source),
+            key,
+            provisionCallback,
+            source,
             injector.options.stage == Stage.TOOL);
       }
 
diff --git a/core/src/com/google/inject/internal/InjectorImpl.java b/core/src/com/google/inject/internal/InjectorImpl.java
index 22be9e8..3c25b40 100644
--- a/core/src/com/google/inject/internal/InjectorImpl.java
+++ b/core/src/com/google/inject/internal/InjectorImpl.java
@@ -71,11 +71,16 @@ final class InjectorImpl implements Injector, Lookups {
     final Stage stage;
     final boolean jitDisabled;
     final boolean disableCircularProxies;
-    
-    InjectorOptions(Stage stage, boolean jitDisabled, boolean disableCircularProxies) {
+    final boolean atInjectRequired;
+    final boolean exactBindingAnnotationsRequired;
+
+    InjectorOptions(Stage stage, boolean jitDisabled, boolean disableCircularProxies,
+        boolean atInjectRequired, boolean exactBindingAnnotationsRequired) {
       this.stage = stage;
       this.jitDisabled = jitDisabled;
       this.disableCircularProxies = disableCircularProxies;
+      this.atInjectRequired = atInjectRequired;
+      this.exactBindingAnnotationsRequired = exactBindingAnnotationsRequired;
     }
     
     @Override
@@ -84,6 +89,8 @@ final class InjectorImpl implements Injector, Lookups {
           .add("stage", stage)
           .add("jitDisabled", jitDisabled)
           .add("disableCircularProxies", disableCircularProxies)
+          .add("atInjectRequired", atInjectRequired)
+          .add("exactBindingAnnotationsRequired", exactBindingAnnotationsRequired)
           .toString();
     }
   }
@@ -121,12 +128,7 @@ final class InjectorImpl implements Injector, Lookups {
     if (parent != null) {
       localContext = parent.localContext;
     } else {
-      localContext = new ThreadLocal<Object[]>() {
-        @Override
-        protected Object[] initialValue() {
-          return new Object[1];
-        }
-      };
+      localContext = new ThreadLocal<Object[]>();
     }
   }
 
@@ -179,7 +181,7 @@ final class InjectorImpl implements Injector, Lookups {
     if(isProvider(key)) {
       try {
         // This is safe because isProvider above ensures that T is a Provider<?>
-        @SuppressWarnings("unchecked")
+        @SuppressWarnings({"unchecked", "cast"})
         Key<?> providedKey = (Key<?>)getProvidedKey((Key)key, new Errors());
         if(getExistingBinding(providedKey) != null) {
           return getBinding(key);
@@ -258,7 +260,7 @@ final class InjectorImpl implements Injector, Lookups {
           }
         }
       }
-      
+
       // If we previously failed creating this JIT binding and our Errors has
       // already recorded an error, then just directly throw that error.
       // We need to do this because it's possible we already cleaned up the
@@ -284,7 +286,7 @@ final class InjectorImpl implements Injector, Lookups {
   private static boolean isProvider(Key<?> key) {
     return key.getTypeLiteral().getRawType().equals(Provider.class);
   }
-  
+
   private static boolean isTypeLiteral(Key<?> key) {
     return key.getTypeLiteral().getRawType().equals(TypeLiteral.class);
   }
@@ -548,7 +550,7 @@ final class InjectorImpl implements Injector, Lookups {
           // We do not pass cb.getInternalConstructor as the second parameter
           // so that cached exceptions while constructing it get stored.
           // See TypeListenerTest#testTypeListenerThrows
-          removeFailedJitBinding(key, null);
+          removeFailedJitBinding(binding, null);
           cleanup(binding, new HashSet<Key>());
         }
       }
@@ -579,7 +581,7 @@ final class InjectorImpl implements Injector, Lookups {
             }
           }
           if(failed) {
-            removeFailedJitBinding(depKey, ip);
+            removeFailedJitBinding(depBinding, ip);
             bindingFailed = true;
           }
         } else if(state.getExplicitBinding(depKey) == null) {
@@ -593,11 +595,11 @@ final class InjectorImpl implements Injector, Lookups {
   }
   
   /** Cleans up any state that may have been cached when constructing the JIT binding. */
-  private void removeFailedJitBinding(Key<?> key, InjectionPoint ip) {
-    failedJitBindings.add(key);
-    jitBindings.remove(key);
-    membersInjectorStore.remove(key.getTypeLiteral());
-    provisionListenerStore.remove(key);
+  private void removeFailedJitBinding(Binding<?> binding, InjectionPoint ip) {
+    failedJitBindings.add(binding.getKey());
+    jitBindings.remove(binding.getKey());
+    membersInjectorStore.remove(binding.getKey().getTypeLiteral());
+    provisionListenerStore.remove(binding);
     if(ip != null) {
       constructors.remove(ip);
     }
@@ -651,7 +653,14 @@ final class InjectorImpl implements Injector, Lookups {
     }
 
     
-    return ConstructorBindingImpl.create(this, key, null, source, scoping, errors, jitBinding && options.jitDisabled);
+    return ConstructorBindingImpl.create(this,
+        key,
+        null, /* use default constructor */
+        source,
+        scoping,
+        errors,
+        jitBinding && options.jitDisabled,
+        options.atInjectRequired);
   }
 
   /**
@@ -700,10 +709,9 @@ final class InjectorImpl implements Injector, Lookups {
     Key<? extends Provider<T>> providerKey = (Key<? extends Provider<T>>) Key.get(providerType);
     ProvidedByInternalFactory<T> internalFactory =
         new ProvidedByInternalFactory<T>(rawType, providerType,
-            providerKey, !options.disableCircularProxies,
-            provisionListenerStore.get(key));
+            providerKey, !options.disableCircularProxies);
     Object source = rawType;
-    return LinkedProviderBindingImpl.createWithInitializer(
+    BindingImpl<T> binding = LinkedProviderBindingImpl.createWithInitializer(
         this,
         key,
         source,
@@ -711,6 +719,8 @@ final class InjectorImpl implements Injector, Lookups {
         scoping,
         providerKey,
         internalFactory);
+    internalFactory.setProvisionListenerCallback(provisionListenerStore.get(binding));
+    return binding;
   }
 
   /** Creates a binding for a type annotated with @ImplementedBy. */
@@ -812,7 +822,7 @@ final class InjectorImpl implements Injector, Lookups {
     if (isProvider(key)) {
       // These casts are safe. We know T extends Provider<X> and that given Key<Provider<X>>,
       // createProviderBinding() will return BindingImpl<Provider<X>>.
-      @SuppressWarnings("unchecked")
+      @SuppressWarnings({"unchecked", "cast"})
       BindingImpl<T> binding = (BindingImpl<T>) createProviderBinding((Key) key, errors);
       return binding;
     }
@@ -821,7 +831,7 @@ final class InjectorImpl implements Injector, Lookups {
     if (isMembersInjector(key)) {
       // These casts are safe. T extends MembersInjector<X> and that given Key<MembersInjector<X>>,
       // createMembersInjectorBinding() will return BindingImpl<MembersInjector<X>>.
-      @SuppressWarnings("unchecked")
+      @SuppressWarnings({"unchecked", "cast"})
       BindingImpl<T> binding = (BindingImpl<T>) createMembersInjectorBinding((Key) key, errors);
       return binding;
     }
@@ -831,7 +841,7 @@ final class InjectorImpl implements Injector, Lookups {
     if (convertedBinding != null) {
       return convertedBinding;
     }
-    
+
     if (!isTypeLiteral(key) 
         && jitDisabled
         && jitType != JitLimitation.NEW_OR_EXISTING_JIT) {
@@ -841,7 +851,7 @@ final class InjectorImpl implements Injector, Lookups {
     // If the key has an annotation...
     if (key.getAnnotationType() != null) {
       // Look for a binding without annotation attributes or return null.
-      if (key.hasAttributes()) {
+      if (key.hasAttributes() && !options.exactBindingAnnotationsRequired) {
         try {
           Errors ignored = new Errors();
           return getBindingOrThrow(key.withoutAttributes(), ignored, JitLimitation.NO_JIT);
@@ -1025,11 +1035,15 @@ final class InjectorImpl implements Injector, Lookups {
     return getProvider(type).get();
   }
 
-  final ThreadLocal<Object[]> localContext;
+  private final ThreadLocal<Object[]> localContext;
 
   /** Looks up thread local context. Creates (and removes) a new context if necessary. */
   <T> T callInContext(ContextualCallable<T> callable) throws ErrorsException {
     Object[] reference = localContext.get();
+    if (reference == null) {
+      reference = new Object[1];
+      localContext.set(reference);
+    }
     if (reference[0] == null) {
       reference[0] = new InternalContext();
       try {
diff --git a/core/src/com/google/inject/internal/InjectorOptionsProcessor.java b/core/src/com/google/inject/internal/InjectorOptionsProcessor.java
index 4434dce..61cc6ee 100644
--- a/core/src/com/google/inject/internal/InjectorOptionsProcessor.java
+++ b/core/src/com/google/inject/internal/InjectorOptionsProcessor.java
@@ -22,6 +22,8 @@ import static com.google.common.base.Preconditions.checkState;
 import com.google.inject.Stage;
 import com.google.inject.internal.InjectorImpl.InjectorOptions;
 import com.google.inject.spi.DisableCircularProxiesOption;
+import com.google.inject.spi.RequireAtInjectOnConstructorsOption;
+import com.google.inject.spi.RequireExactBindingAnnotationsOption;
 import com.google.inject.spi.RequireExplicitBindingsOption;
 
 /**
@@ -33,6 +35,8 @@ class InjectorOptionsProcessor extends AbstractProcessor {
 
   private boolean disableCircularProxies = false;
   private boolean jitDisabled = false;
+  private boolean atInjectRequired = false;
+  private boolean exactBindingAnnotationsRequired = false;
 
   InjectorOptionsProcessor(Errors errors) {
     super(errors);
@@ -49,6 +53,18 @@ class InjectorOptionsProcessor extends AbstractProcessor {
     jitDisabled = true;
     return true;
   }
+  
+  @Override
+  public Boolean visit(RequireAtInjectOnConstructorsOption option) {
+    atInjectRequired = true;
+    return true;    
+  }
+
+  @Override
+  public Boolean visit(RequireExactBindingAnnotationsOption option) {
+    exactBindingAnnotationsRequired = true;
+    return true;
+  }
 
   InjectorOptions getOptions(Stage stage, InjectorOptions parentOptions) {
     checkNotNull(stage, "stage must be set");
@@ -56,13 +72,17 @@ class InjectorOptionsProcessor extends AbstractProcessor {
       return new InjectorOptions(
           stage,
           jitDisabled,
-          disableCircularProxies);
+          disableCircularProxies,
+          atInjectRequired,
+          exactBindingAnnotationsRequired);
     } else {
       checkState(stage == parentOptions.stage, "child & parent stage don't match");
       return new InjectorOptions(
           stage,
           jitDisabled || parentOptions.jitDisabled,
-          disableCircularProxies || parentOptions.disableCircularProxies); 
+          disableCircularProxies || parentOptions.disableCircularProxies,
+          atInjectRequired || parentOptions.atInjectRequired,
+          exactBindingAnnotationsRequired || parentOptions.exactBindingAnnotationsRequired);
     }
   }
 
diff --git a/core/src/com/google/inject/internal/InjectorShell.java b/core/src/com/google/inject/internal/InjectorShell.java
index 6f3ade3..243274f 100644
--- a/core/src/com/google/inject/internal/InjectorShell.java
+++ b/core/src/com/google/inject/internal/InjectorShell.java
@@ -129,9 +129,9 @@ final class InjectorShell {
       checkState(privateElements == null || parent != null, "PrivateElements with no parent");
       checkState(state != null, "no state. Did you remember to lock() ?");
 
-      // bind Stage and Singleton if this is a top-level injector
+      // bind Singleton if this is a top-level injector
       if (parent == null) {
-        modules.add(0, new RootModule(stage));
+        modules.add(0, new RootModule());
       }
       elements.addAll(Elements.getElements(stage, modules));
       
@@ -147,7 +147,7 @@ final class InjectorShell {
 
       // add default type converters if this is a top-level injector
       if (parent == null) {
-        new TypeConverterBindingProcessor(errors).prepareBuiltInConverters(injector);
+        TypeConverterBindingProcessor.prepareBuiltInConverters(injector);
       }
 
       stopwatch.resetAndLog("Module execution");
@@ -174,6 +174,7 @@ final class InjectorShell {
       new TypeConverterBindingProcessor(errors).process(injector, elements);
       stopwatch.resetAndLog("Converters creation");
 
+      bindStage(injector, stage);
       bindInjector(injector);
       bindLogger(injector);
       
@@ -278,7 +279,7 @@ final class InjectorShell {
       return "Provider<Logger>";
     }
   }
-
+  
   private static class SLF4JLoggerFactory implements InternalFactory<org.slf4j.Logger>, Provider<org.slf4j.Logger> {
     private final Injector injector;
 
@@ -317,16 +318,21 @@ final class InjectorShell {
     }
   }
 
-  private static class RootModule implements Module {
-    final Stage stage;
-
-    private RootModule(Stage stage) {
-      this.stage = checkNotNull(stage, "stage");
-    }
+  private static void bindStage(InjectorImpl injector, Stage stage) {
+    Key<Stage> key = Key.get(Stage.class);
+    InstanceBindingImpl<Stage> stageBinding = new InstanceBindingImpl<Stage>(
+        injector,
+        key,
+        SourceProvider.UNKNOWN_SOURCE,
+        new ConstantFactory<Stage>(Initializables.of(stage)),
+        ImmutableSet.<InjectionPoint>of(),
+        stage);
+    injector.state.putBinding(key, stageBinding);
+  }
 
+  private static class RootModule implements Module {
     public void configure(Binder binder) {
       binder = binder.withSource(SourceProvider.UNKNOWN_SOURCE);
-      binder.bind(Stage.class).toInstance(stage);
       binder.bindScope(Singleton.class, SINGLETON);
       binder.bindScope(javax.inject.Singleton.class, SINGLETON);
     }
diff --git a/core/src/com/google/inject/internal/InstanceBindingImpl.java b/core/src/com/google/inject/internal/InstanceBindingImpl.java
index 57129b1..82d8908 100644
--- a/core/src/com/google/inject/internal/InstanceBindingImpl.java
+++ b/core/src/com/google/inject/internal/InstanceBindingImpl.java
@@ -16,6 +16,9 @@
 
 package com.google.inject.internal;
 
+import static com.google.inject.internal.RehashableKeys.Keys.needsRehashing;
+import static com.google.inject.internal.RehashableKeys.Keys.rehash;
+
 import com.google.common.base.Objects;
 import com.google.common.collect.ImmutableSet;
 import com.google.inject.Binder;
@@ -83,6 +86,14 @@ final class InstanceBindingImpl<T> extends BindingImpl<T> implements InstanceBin
     return new InstanceBindingImpl<T>(getSource(), key, getScoping(), injectionPoints, instance);
   }
 
+  public BindingImpl<T> withRehashedKeys() {
+    if (needsRehashing(getKey())) {
+      return withKey(rehash(getKey()));
+    } else {
+      return this;
+    }
+  }
+
   public void applyTo(Binder binder) {
     // instance bindings aren't scoped
     binder.withSource(getSource()).bind(getKey()).toInstance(instance);
@@ -95,7 +106,7 @@ final class InstanceBindingImpl<T> extends BindingImpl<T> implements InstanceBin
         .add("instance", instance)
         .toString();
   }
-  
+
   @Override
   public boolean equals(Object obj) {
     if(obj instanceof InstanceBindingImpl) {
@@ -107,7 +118,7 @@ final class InstanceBindingImpl<T> extends BindingImpl<T> implements InstanceBin
       return false;
     }
   }
-  
+
   @Override
   public int hashCode() {
     return Objects.hashCode(getKey(), getScoping());
diff --git a/core/src/com/google/inject/internal/InterceptorStackCallback.java b/core/src/com/google/inject/internal/InterceptorStackCallback.java
index 8912ee2..f12ddaf 100644
--- a/core/src/com/google/inject/internal/InterceptorStackCallback.java
+++ b/core/src/com/google/inject/internal/InterceptorStackCallback.java
@@ -52,7 +52,7 @@ final class InterceptorStackCallback implements net.sf.cglib.proxy.MethodInterce
 
   public Object intercept(Object proxy, Method method, Object[] arguments,
       MethodProxy methodProxy) throws Throwable {
-    return new InterceptedMethodInvocation(proxy, methodProxy, arguments).proceed();
+    return new InterceptedMethodInvocation(proxy, methodProxy, arguments, 0).proceed();
   }
 
   private class InterceptedMethodInvocation implements MethodInvocation {
@@ -60,26 +60,25 @@ final class InterceptorStackCallback implements net.sf.cglib.proxy.MethodInterce
     final Object proxy;
     final Object[] arguments;
     final MethodProxy methodProxy;
-    int index = -1;
+    final int index;
 
     public InterceptedMethodInvocation(Object proxy, MethodProxy methodProxy,
-        Object[] arguments) {
+        Object[] arguments, int index) {
       this.proxy = proxy;
       this.methodProxy = methodProxy;
       this.arguments = arguments;
+      this.index = index;
     }
 
     public Object proceed() throws Throwable {
       try {
-        index++;
         return index == interceptors.length
             ? methodProxy.invokeSuper(proxy, arguments)
-            : interceptors[index].invoke(this);
+            : interceptors[index].invoke(
+                new InterceptedMethodInvocation(proxy, methodProxy, arguments, index + 1));
       } catch (Throwable t) {
         pruneStacktrace(t);
         throw t;
-      } finally {
-        index--;
       }
     }
 
diff --git a/core/src/com/google/inject/internal/InternalContext.java b/core/src/com/google/inject/internal/InternalContext.java
index 2ff2af6..71d953c 100644
--- a/core/src/com/google/inject/internal/InternalContext.java
+++ b/core/src/com/google/inject/internal/InternalContext.java
@@ -17,12 +17,13 @@
 package com.google.inject.internal;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.inject.Key;
 import com.google.inject.spi.Dependency;
 import com.google.inject.spi.DependencyAndSource;
 
-import java.util.LinkedList;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 
@@ -35,10 +36,18 @@ import java.util.Map;
 final class InternalContext {
 
   private Map<Object, ConstructionContext<?>> constructionContexts = Maps.newHashMap();
+
   /** Keeps track of the type that is currently being requested for injection. */
-  private Dependency dependency;
-  /** Keeps track of the hierarchy of types needed during injection. */
-  private LinkedList<DependencyAndSource> state = new LinkedList<DependencyAndSource>();
+  private Dependency<?> dependency;
+
+  /**
+   * Keeps track of the hierarchy of types needed during injection.
+   *
+   * <p>This is a pairwise combination of dependencies and sources, with dependencies on even
+   * indices, and sources on odd indices. This structure is to avoid the memory overhead of
+   * DependencyAndSource objects, which can add to several tens of megabytes in large applications.
+   */
+  private final List<Object> state = Lists.newArrayList();
 
   @SuppressWarnings("unchecked")
   public <T> ConstructionContext<T> getConstructionContext(Object key) {
@@ -51,36 +60,44 @@ final class InternalContext {
     return constructionContext;
   }
 
-  public Dependency getDependency() {
+  public Dependency<?> getDependency() {
     return dependency;
   }
 
   /** Sets the new current dependency & adds it to the state. */
-  public Dependency pushDependency(Dependency dependency, Object source) {
-    Dependency previous = this.dependency;
+  public Dependency<?> pushDependency(Dependency<?> dependency, Object source) {
+    Dependency<?> previous = this.dependency;
     this.dependency = dependency;
-    state.addLast(new DependencyAndSource(dependency, source));
+    state.add(dependency);
+    state.add(source);
     return previous;
   }
   
   /** Pops the current state & sets the new dependency. */
-  public void popStateAndSetDependency(Dependency newDependency) {
+  public void popStateAndSetDependency(Dependency<?> newDependency) {
     popState();
     this.dependency = newDependency;
   }
   
   /** Adds to the state without setting the dependency. */
   public void pushState(Key<?> key, Object source) {
-    state.addLast(new DependencyAndSource(key == null ? null : Dependency.get(key), source));
+    state.add(key == null ? null : Dependency.get(key));
+    state.add(source);
   }
   
   /** Pops from the state without setting a dependency. */
   public void popState() {
-    state.removeLast();
+    state.remove(state.size() - 1);
+    state.remove(state.size() - 1);
   }
   
   /** Returns the current dependency chain (all the state). */
   public List<DependencyAndSource> getDependencyChain() {
-    return ImmutableList.copyOf(state);
+    ImmutableList.Builder<DependencyAndSource> builder = ImmutableList.builder();
+    for (int i = 0; i < state.size(); i += 2) {
+      builder.add(new DependencyAndSource(
+          (Dependency<?>) state.get(i), state.get(i + 1)));
+    }
+    return builder.build();
   }
 }
diff --git a/core/src/com/google/inject/internal/InternalFactoryToInitializableAdapter.java b/core/src/com/google/inject/internal/InternalFactoryToInitializableAdapter.java
index 4f0f6d8..03417e6 100644
--- a/core/src/com/google/inject/internal/InternalFactoryToInitializableAdapter.java
+++ b/core/src/com/google/inject/internal/InternalFactoryToInitializableAdapter.java
@@ -30,19 +30,22 @@ import com.google.inject.spi.ProviderInstanceBinding;
 */
 final class InternalFactoryToInitializableAdapter<T> extends ProviderInternalFactory<T> {
 
+  private final ProvisionListenerStackCallback<T> provisionCallback;
   private final Initializable<Provider<? extends T>> initializable;
 
   public InternalFactoryToInitializableAdapter(
       Initializable<Provider<? extends T>> initializable,
       Object source, boolean allowProxy,
       ProvisionListenerStackCallback<T> provisionCallback) {
-    super(source, allowProxy, provisionCallback);
+    super(source, allowProxy);
+    this.provisionCallback = checkNotNull(provisionCallback, "provisionCallback");
     this.initializable = checkNotNull(initializable, "provider");
   }
 
   public T get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked)
       throws ErrorsException {
-    return circularGet(initializable.get(errors), errors, context, dependency, linked);
+    return circularGet(initializable.get(errors), errors, context, dependency, linked,
+        provisionCallback);
   }
   
   @Override
diff --git a/core/src/com/google/inject/internal/InternalFlags.java b/core/src/com/google/inject/internal/InternalFlags.java
new file mode 100644
index 0000000..31d412d
--- /dev/null
+++ b/core/src/com/google/inject/internal/InternalFlags.java
@@ -0,0 +1,52 @@
+package com.google.inject.internal;
+
+import java.util.Arrays;
+import java.util.logging.Logger;
+/**
+ * Contains flags for Guice.
+ */
+public class InternalFlags {
+  private final static Logger logger = Logger.getLogger(InternalFlags.class.getName());
+
+  private static final IncludeStackTraceOption INCLUDE_STACK_TRACES;
+  static {
+    IncludeStackTraceOption includeStackTraces;
+    try {
+      includeStackTraces = parseIncludeStackTraceOption();
+    } catch (Throwable e) {
+      includeStackTraces = IncludeStackTraceOption.ONLY_FOR_DECLARING_SOURCE;
+    }
+    INCLUDE_STACK_TRACES = includeStackTraces;
+  }
+
+  /**
+   * The options for Guice stack trace collection.
+   */
+  public enum IncludeStackTraceOption {
+    /** No stack trace collection */
+    OFF,
+    /**  Minimum stack trace collection (Default) */
+    ONLY_FOR_DECLARING_SOURCE,
+    /** Full stack trace for everything */
+    COMPLETE
+  }
+
+
+  public static IncludeStackTraceOption getIncludeStackTraceOption() {
+    return INCLUDE_STACK_TRACES;
+  }
+
+  private static IncludeStackTraceOption parseIncludeStackTraceOption() {
+    String flag = System.getProperty("guice_include_stack_traces");
+    try {
+      return (flag == null || flag.length() == 0)
+          ? IncludeStackTraceOption.ONLY_FOR_DECLARING_SOURCE
+          : IncludeStackTraceOption.valueOf(flag);
+    } catch (IllegalArgumentException e) {
+      logger.warning(flag
+          + " is not a valid flag value for guice_include_stack_traces. "
+          + " Values must be one of " + Arrays.asList(IncludeStackTraceOption.values()));
+      return IncludeStackTraceOption.ONLY_FOR_DECLARING_SOURCE;
+    }
+  }
+}
diff --git a/core/src/com/google/inject/internal/LinkedBindingImpl.java b/core/src/com/google/inject/internal/LinkedBindingImpl.java
index 33b9a7c..71ccf8e 100644
--- a/core/src/com/google/inject/internal/LinkedBindingImpl.java
+++ b/core/src/com/google/inject/internal/LinkedBindingImpl.java
@@ -16,6 +16,9 @@
 
 package com.google.inject.internal;
 
+import static com.google.inject.internal.RehashableKeys.Keys.needsRehashing;
+import static com.google.inject.internal.RehashableKeys.Keys.rehash;
+
 import com.google.common.base.Objects;
 import com.google.common.collect.ImmutableSet;
 import com.google.inject.Binder;
@@ -50,7 +53,7 @@ public final class LinkedBindingImpl<T> extends BindingImpl<T> implements Linked
   public Key<? extends T> getLinkedKey() {
     return targetKey;
   }
-  
+
   public Set<Dependency<?>> getDependencies() {
     return ImmutableSet.<Dependency<?>>of(Dependency.get(targetKey));
   }
@@ -63,6 +66,20 @@ public final class LinkedBindingImpl<T> extends BindingImpl<T> implements Linked
     return new LinkedBindingImpl<T>(getSource(), key, getScoping(), targetKey);
   }
 
+  public BindingImpl<T> withRehashedKeys() {
+    boolean keyNeedsRehashing = needsRehashing(getKey());
+    boolean targetKeyNeedsRehashing = needsRehashing(targetKey);
+    if (keyNeedsRehashing || targetKeyNeedsRehashing) {
+      return new LinkedBindingImpl<T>(
+          getSource(),
+          keyNeedsRehashing ? rehash(getKey()) : getKey(),
+          getScoping(),
+          targetKeyNeedsRehashing ? rehash(targetKey) : targetKey);
+    } else {
+      return this;
+    }
+  }
+
   public void applyTo(Binder binder) {
     getScoping().applyTo(binder.withSource(getSource()).bind(getKey()).to(getLinkedKey()));
   }
@@ -75,7 +92,7 @@ public final class LinkedBindingImpl<T> extends BindingImpl<T> implements Linked
         .add("target", targetKey)
         .toString();
   }
-  
+
   @Override
   public boolean equals(Object obj) {
     if(obj instanceof LinkedBindingImpl) {
@@ -87,7 +104,7 @@ public final class LinkedBindingImpl<T> extends BindingImpl<T> implements Linked
       return false;
     }
   }
-  
+
   @Override
   public int hashCode() {
     return Objects.hashCode(getKey(), getScoping(), targetKey);
diff --git a/core/src/com/google/inject/internal/LinkedProviderBindingImpl.java b/core/src/com/google/inject/internal/LinkedProviderBindingImpl.java
index f2204ba..4841e92 100644
--- a/core/src/com/google/inject/internal/LinkedProviderBindingImpl.java
+++ b/core/src/com/google/inject/internal/LinkedProviderBindingImpl.java
@@ -16,6 +16,9 @@
 
 package com.google.inject.internal;
 
+import static com.google.inject.internal.RehashableKeys.Keys.needsRehashing;
+import static com.google.inject.internal.RehashableKeys.Keys.rehash;
+
 import com.google.common.base.Objects;
 import com.google.common.collect.ImmutableSet;
 import com.google.inject.Binder;
@@ -32,7 +35,7 @@ final class LinkedProviderBindingImpl<T>
 
   final Key<? extends javax.inject.Provider<? extends T>> providerKey;
   final DelayedInitialize delayedInitializer;
-  
+
   private LinkedProviderBindingImpl(InjectorImpl injector, Key<T> key, Object source,
       InternalFactory<? extends T> internalFactory, Scoping scoping,
       Key<? extends javax.inject.Provider<? extends T>> providerKey,
@@ -54,7 +57,7 @@ final class LinkedProviderBindingImpl<T>
     this.providerKey = providerKey;
     this.delayedInitializer = null;
   }
-  
+
   static <T> LinkedProviderBindingImpl<T> createWithInitializer(InjectorImpl injector, Key<T> key,
       Object source, InternalFactory<? extends T> internalFactory, Scoping scoping,
       Key<? extends javax.inject.Provider<? extends T>> providerKey,
@@ -70,17 +73,17 @@ final class LinkedProviderBindingImpl<T>
   public Key<? extends javax.inject.Provider<? extends T>> getProviderKey() {
     return providerKey;
   }
-  
+
   public void initialize(InjectorImpl injector, Errors errors) throws ErrorsException {
     if (delayedInitializer != null) {
       delayedInitializer.initialize(injector, errors);
     }
   }
-  
+
   public Set<Dependency<?>> getDependencies() {
     return ImmutableSet.<Dependency<?>>of(Dependency.get(providerKey));
   }
-  
+
   public BindingImpl<T> withScoping(Scoping scoping) {
     return new LinkedProviderBindingImpl<T>(getSource(), getKey(), scoping, providerKey);
   }
@@ -89,6 +92,20 @@ final class LinkedProviderBindingImpl<T>
     return new LinkedProviderBindingImpl<T>(getSource(), key, getScoping(), providerKey);
   }
 
+  public BindingImpl<T> withRehashedKeys() {
+    boolean keyNeedsRehashing = needsRehashing(getKey());
+    boolean providerKeyNeedsRehashing = needsRehashing(providerKey);
+    if (keyNeedsRehashing || providerKeyNeedsRehashing) {
+      return new LinkedProviderBindingImpl<T>(
+          getSource(),
+          keyNeedsRehashing ? rehash(getKey()) : getKey(),
+          getScoping(),
+          providerKeyNeedsRehashing ? rehash(providerKey) : providerKey);
+    } else {
+      return this;
+    }
+  }
+
   public void applyTo(Binder binder) {
     getScoping().applyTo(binder.withSource(getSource())
         .bind(getKey()).toProvider(getProviderKey()));
@@ -102,7 +119,7 @@ final class LinkedProviderBindingImpl<T>
         .add("provider", providerKey)
         .toString();
   }
-  
+
   @Override
   public boolean equals(Object obj) {
     if(obj instanceof LinkedProviderBindingImpl) {
@@ -114,7 +131,7 @@ final class LinkedProviderBindingImpl<T>
       return false;
     }
   }
-  
+
   @Override
   public int hashCode() {
     return Objects.hashCode(getKey(), getScoping(), providerKey);
diff --git a/core/src/com/google/inject/internal/MembersInjectorImpl.java b/core/src/com/google/inject/internal/MembersInjectorImpl.java
index d405de5..498b441 100644
--- a/core/src/com/google/inject/internal/MembersInjectorImpl.java
+++ b/core/src/com/google/inject/internal/MembersInjectorImpl.java
@@ -21,6 +21,7 @@ import com.google.common.collect.ImmutableSet;
 import com.google.inject.Key;
 import com.google.inject.MembersInjector;
 import com.google.inject.TypeLiteral;
+import com.google.inject.internal.ProvisionListenerStackCallback.ProvisionCallback;
 import com.google.inject.spi.InjectionListener;
 import com.google.inject.spi.InjectionPoint;
 
@@ -33,8 +34,8 @@ final class MembersInjectorImpl<T> implements MembersInjector<T> {
   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]*/
@@ -58,7 +59,7 @@ final class MembersInjectorImpl<T> implements MembersInjector<T> {
   public void injectMembers(T instance) {
     Errors errors = new Errors(typeLiteral);
     try {
-      injectAndNotify(instance, errors, null, typeLiteral, false);
+      injectAndNotify(instance, errors, null, null, typeLiteral, false);
     } catch (ErrorsException e) {
       errors.merge(e.getErrors());
     }
@@ -66,18 +67,31 @@ final class MembersInjectorImpl<T> implements MembersInjector<T> {
     errors.throwProvisionExceptionIfErrorsExist();
   }
 
-  void injectAndNotify(final T instance, final Errors errors,
-      final Key<T> key, final Object source, final boolean toolableOnly)
-      throws ErrorsException {
+  void injectAndNotify(final T instance,
+      final Errors errors,
+      final Key<T> key, // possibly null!
+      final ProvisionListenerStackCallback<T> provisionCallback, // possibly null!
+      final Object source,
+      final boolean toolableOnly) throws ErrorsException {
     if (instance == null) {
       return;
     }
 
     injector.callInContext(new ContextualCallable<Void>() {
-      public Void call(InternalContext context) throws ErrorsException {
+      @Override
+      public Void call(final InternalContext context) throws ErrorsException {
         context.pushState(key, source);
         try {
-          injectMembers(instance, errors, context, toolableOnly);
+          if (provisionCallback != null && provisionCallback.hasListeners()) {
+            provisionCallback.provision(errors, context, new ProvisionCallback<T>() {
+              @Override public T call() {
+                injectMembers(instance, errors, context, toolableOnly);
+                return instance;
+              }
+            });
+          } else {
+            injectMembers(instance, errors, context, toolableOnly);
+          }
         } finally {
           context.popState();
         }
@@ -121,9 +135,7 @@ final class MembersInjectorImpl<T> implements MembersInjector<T> {
 
     // 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 @@ package com.google.inject.internal;
 
 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 @@ final class MembersInjectorStore {
     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/MoreTypes.java b/core/src/com/google/inject/internal/MoreTypes.java
index d51016b..21613f4 100644
--- a/core/src/com/google/inject/internal/MoreTypes.java
+++ b/core/src/com/google/inject/internal/MoreTypes.java
@@ -228,7 +228,7 @@ public class MoreTypes {
       }
       TypeVariable<?> va = (TypeVariable) a;
       TypeVariable<?> vb = (TypeVariable) b;
-      return va.getGenericDeclaration() == vb.getGenericDeclaration()
+      return va.getGenericDeclaration().equals(vb.getGenericDeclaration())
           && va.getName().equals(vb.getName());
 
     } else {
diff --git a/core/src/com/google/inject/internal/Nullability.java b/core/src/com/google/inject/internal/Nullability.java
index f90342e..dcec9c9 100644
--- a/core/src/com/google/inject/internal/Nullability.java
+++ b/core/src/com/google/inject/internal/Nullability.java
@@ -1,3 +1,19 @@
+/**
+ * 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.internal;
 
 import java.lang.annotation.Annotation;
diff --git a/core/src/com/google/inject/internal/ProvidedByInternalFactory.java b/core/src/com/google/inject/internal/ProvidedByInternalFactory.java
index c1298c3..0a0191d 100644
--- a/core/src/com/google/inject/internal/ProvidedByInternalFactory.java
+++ b/core/src/com/google/inject/internal/ProvidedByInternalFactory.java
@@ -37,25 +37,28 @@ class ProvidedByInternalFactory<T> extends ProviderInternalFactory<T>
   private final Class<? extends Provider<?>> providerType;
   private final Key<? extends Provider<T>> providerKey;
   private BindingImpl<? extends Provider<T>> providerBinding;
+  private ProvisionListenerStackCallback<T> provisionCallback;
   
   ProvidedByInternalFactory(
       Class<?> rawType,
       Class<? extends Provider<?>> providerType,
       Key<? extends Provider<T>> providerKey,
-      boolean allowProxy,
-      ProvisionListenerStackCallback<T> provisionCallback) {
-    super(providerKey, allowProxy, provisionCallback);
+      boolean allowProxy) {
+    super(providerKey, allowProxy);
     this.rawType = rawType;
     this.providerType = providerType; 
     this.providerKey = providerKey;
   }
   
+  void setProvisionListenerCallback(ProvisionListenerStackCallback<T> listener) {
+    provisionCallback = listener;
+  }
+  
   public void initialize(InjectorImpl injector, Errors errors) throws ErrorsException {
     providerBinding =
-        injector.getBindingOrThrow(providerKey, errors, JitLimitation.NEW_OR_EXISTING_JIT); 
+        injector.getBindingOrThrow(providerKey, errors, JitLimitation.NEW_OR_EXISTING_JIT);
   }
-  
-  @SuppressWarnings("unchecked")// 
+
   public T get(Errors errors, InternalContext context, Dependency dependency, boolean linked)
       throws ErrorsException {
     checkState(providerBinding != null, "not initialized");
@@ -63,14 +66,15 @@ class ProvidedByInternalFactory<T> extends ProviderInternalFactory<T>
     context.pushState(providerKey, providerBinding.getSource());
     try {
       errors = errors.withSource(providerKey);
-      Provider provider = providerBinding.getInternalFactory().get(
+      Provider<? extends T> provider = providerBinding.getInternalFactory().get(
           errors, context, dependency, true);
-      return circularGet(provider, errors, context, dependency, linked);
+      return circularGet(provider, errors, context, dependency, linked, provisionCallback);
     } finally {
       context.popState();
     }
   }
   
+  @Override
   protected T provision(javax.inject.Provider<? extends T> provider, Errors errors,
       Dependency<?> dependency, ConstructionContext<T> constructionContext)
       throws ErrorsException {
diff --git a/core/src/com/google/inject/internal/ProviderInstanceBindingImpl.java b/core/src/com/google/inject/internal/ProviderInstanceBindingImpl.java
index ad02fef..971a1d0 100644
--- a/core/src/com/google/inject/internal/ProviderInstanceBindingImpl.java
+++ b/core/src/com/google/inject/internal/ProviderInstanceBindingImpl.java
@@ -16,6 +16,9 @@ limitations under the License.
 
 package com.google.inject.internal;
 
+import static com.google.inject.internal.RehashableKeys.Keys.needsRehashing;
+import static com.google.inject.internal.RehashableKeys.Keys.rehash;
+
 import com.google.common.base.Objects;
 import com.google.common.collect.ImmutableSet;
 import com.google.inject.Binder;
@@ -86,6 +89,14 @@ final class ProviderInstanceBindingImpl<T> extends BindingImpl<T>
         getSource(), key, getScoping(), injectionPoints, providerInstance);
   }
 
+  public BindingImpl<T> withRehashedKeys() {
+    if (needsRehashing(getKey())) {
+      return withKey(rehash(getKey()));
+    } else {
+      return this;
+    }
+  }
+
   public void applyTo(Binder binder) {
     getScoping().applyTo(
         binder.withSource(getSource()).bind(getKey()).toProvider(getProviderInstance()));
@@ -100,7 +111,7 @@ final class ProviderInstanceBindingImpl<T> extends BindingImpl<T>
         .add("provider", providerInstance)
         .toString();
   }
-  
+
   @Override
   public boolean equals(Object obj) {
     if(obj instanceof ProviderInstanceBindingImpl) {
@@ -112,7 +123,7 @@ final class ProviderInstanceBindingImpl<T> extends BindingImpl<T>
       return false;
     }
   }
-  
+
   @Override
   public int hashCode() {
     return Objects.hashCode(getKey(), getScoping());
diff --git a/core/src/com/google/inject/internal/ProviderInternalFactory.java b/core/src/com/google/inject/internal/ProviderInternalFactory.java
index b061070..e0e7d1d 100644
--- a/core/src/com/google/inject/internal/ProviderInternalFactory.java
+++ b/core/src/com/google/inject/internal/ProviderInternalFactory.java
@@ -32,23 +32,21 @@ import javax.inject.Provider;
  */
 abstract class ProviderInternalFactory<T> implements InternalFactory<T> {
   
-  private final ProvisionListenerStackCallback<T> provisionCallback;
   private final boolean allowProxy;
   protected final Object source;
   
-  ProviderInternalFactory(Object source, boolean allowProxy,
-      ProvisionListenerStackCallback<T> provisionCallback) {
-    this.provisionCallback = checkNotNull(provisionCallback, "provisionCallback");
+  ProviderInternalFactory(Object source, boolean allowProxy) {
     this.source = checkNotNull(source, "source");
     this.allowProxy = allowProxy;
   }
   
   protected T circularGet(final Provider<? extends T> provider, final Errors errors,
-      InternalContext context, final Dependency<?> dependency, boolean linked)
+      InternalContext context, final Dependency<?> dependency, boolean linked,
+      ProvisionListenerStackCallback<T> provisionCallback)
       throws ErrorsException {    
     Class<?> expectedType = dependency.getKey().getTypeLiteral().getRawType();
     final ConstructionContext<T> constructionContext = context.getConstructionContext(this);
-    
+
     // We have a circular reference between constructors. Return a proxy.
     if (constructionContext.isConstructing()) {
       if (!allowProxy) {
@@ -62,14 +60,20 @@ abstract class ProviderInternalFactory<T> implements InternalFactory<T> {
     }
 
     // Optimization: Don't go through the callback stack if no one's listening.
-    if (!provisionCallback.hasListeners()) {
-      return provision(provider, errors, dependency, constructionContext);
-    } else {
-      return provisionCallback.provision(errors, context, new ProvisionCallback<T>() {
-        public T call() throws ErrorsException {
-          return provision(provider, errors, dependency, constructionContext);
-        }
-      });
+    constructionContext.startConstruction();
+    try {
+      if (!provisionCallback.hasListeners()) {
+        return provision(provider, errors, dependency, constructionContext);
+      } else {
+        return provisionCallback.provision(errors, context, new ProvisionCallback<T>() {
+          public T call() throws ErrorsException {
+            return provision(provider, errors, dependency, constructionContext);
+          }
+        });
+      }
+    } finally {
+      constructionContext.removeCurrentReference();
+      constructionContext.finishConstruction();
     }
   }
 
@@ -79,13 +83,8 @@ abstract class ProviderInternalFactory<T> implements InternalFactory<T> {
    */
   protected T provision(Provider<? extends T> provider, Errors errors, Dependency<?> dependency,
       ConstructionContext<T> constructionContext) throws ErrorsException {
-    constructionContext.startConstruction();
-    try {
-      T t = errors.checkForNull(provider.get(), source, dependency);
-      constructionContext.setProxyDelegates(t);
-      return t;
-    } finally {
-      constructionContext.finishConstruction();
-    }
+    T t = errors.checkForNull(provider.get(), source, dependency);
+    constructionContext.setProxyDelegates(t);
+    return t;
   }
 }
diff --git a/core/src/com/google/inject/internal/ProvisionListenerCallbackStore.java b/core/src/com/google/inject/internal/ProvisionListenerCallbackStore.java
index 8300fcb..1074628 100644
--- a/core/src/com/google/inject/internal/ProvisionListenerCallbackStore.java
+++ b/core/src/com/google/inject/internal/ProvisionListenerCallbackStore.java
@@ -16,16 +16,22 @@
 
 package com.google.inject.internal;
 
-import com.google.common.base.Function;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
-import com.google.common.collect.MapMaker;
+import com.google.inject.Binding;
+import com.google.inject.Injector;
 import com.google.inject.Key;
+import com.google.inject.Stage;
 import com.google.inject.spi.ProvisionListener;
 import com.google.inject.spi.ProvisionListenerBinding;
 
 import java.util.List;
-import java.util.Map;
+import java.util.Set;
+import java.util.logging.Logger;
 
 /**
  * {@link ProvisionListenerStackCallback} for each key.
@@ -33,13 +39,19 @@ import java.util.Map;
  * @author sameb at google.com (Sam Berlin)
  */
 final class ProvisionListenerCallbackStore {
+
+  // TODO(sameb): Consider exposing this in the API somehow?  Maybe?
+  // Lots of code often want to skip over the internal stuffs.
+  private static final Set<Key<?>> INTERNAL_BINDINGS =
+      ImmutableSet.of(Key.get(Injector.class), Key.get(Stage.class), Key.get(Logger.class));
+  
   private final ImmutableList<ProvisionListenerBinding> listenerBindings;
 
-  private final Map<Key<?>, ProvisionListenerStackCallback<?>> cache
-      = new MapMaker().makeComputingMap(
-          new Function<Key<?>, ProvisionListenerStackCallback<?>>() {
-            public ProvisionListenerStackCallback<?> apply(Key<?> key) {
-              return create(key);
+  private final LoadingCache<KeyBinding, ProvisionListenerStackCallback<?>> cache
+      = CacheBuilder.newBuilder().build(
+          new CacheLoader<KeyBinding, ProvisionListenerStackCallback<?>>() {
+            public ProvisionListenerStackCallback<?> load(KeyBinding key) {
+              return create(key.binding);
             }
           });
 
@@ -50,8 +62,13 @@ final class ProvisionListenerCallbackStore {
   /** Returns a new {@link ProvisionListenerStackCallback} for the key.
    */
   @SuppressWarnings("unchecked") // the ProvisionListenerStackCallback type always agrees with the passed type
-  public <T> ProvisionListenerStackCallback<T> get(Key<T> key) {
-    return (ProvisionListenerStackCallback<T>) cache.get(key);
+  public <T> ProvisionListenerStackCallback<T> get(Binding<T> binding) {
+    // Never notify any listeners for internal bindings.
+    if (!INTERNAL_BINDINGS.contains(binding.getKey())) {
+      return (ProvisionListenerStackCallback<T>) cache.getUnchecked(
+          new KeyBinding(binding.getKey(), binding));
+    }
+    return ProvisionListenerStackCallback.emptyListener();
   }
 
   /**
@@ -63,27 +80,49 @@ final class ProvisionListenerCallbackStore {
    * 
    * Returns true if the type was stored in the cache, false otherwise.
    */
-  boolean remove(Key<?> type) {
-    return cache.remove(type) != null;
+  boolean remove(Binding<?> type) {
+    return cache.asMap().remove(type) != null;
   }
 
   /**
    * Creates a new {@link ProvisionListenerStackCallback} with the correct listeners
    * for the key.
    */
-  private <T> ProvisionListenerStackCallback<T> create(Key<T> key) {
+  private <T> ProvisionListenerStackCallback<T> create(Binding<T> binding) {
     List<ProvisionListener> listeners = null;
-    for (ProvisionListenerBinding binding : listenerBindings) {
-      if (binding.getKeyMatcher().matches(key)) {
+    for (ProvisionListenerBinding provisionBinding : listenerBindings) {
+      if (provisionBinding.getBindingMatcher().matches(binding)) {
         if (listeners == null) {
           listeners = Lists.newArrayList();
         }
-        listeners.addAll(binding.getListeners());
+        listeners.addAll(provisionBinding.getListeners());
       }
     }
-    if (listeners == null) {
-      listeners = ImmutableList.of();
+    if (listeners == null || listeners.isEmpty()) {
+      // Optimization: don't bother constructing the callback if there are
+      // no listeners.
+      return ProvisionListenerStackCallback.emptyListener();
+    }
+    return new ProvisionListenerStackCallback<T>(binding, listeners);
+  }
+  
+  /** A struct that holds key & binding but uses just key for equality/hashcode. */
+  private static class KeyBinding {
+    final Key<?> key;
+    final Binding<?> binding;
+    
+    KeyBinding(Key<?> key, Binding<?> binding) {
+      this.key = key;
+      this.binding = binding;
+    }
+    
+    @Override
+    public boolean equals(Object obj) {
+      return obj instanceof KeyBinding && key.equals(((KeyBinding)obj).key);
+    }
+    @Override
+    public int hashCode() {
+      return key.hashCode();
     }
-    return new ProvisionListenerStackCallback<T>(key, listeners);
   }
 }
diff --git a/core/src/com/google/inject/internal/ProvisionListenerStackCallback.java b/core/src/com/google/inject/internal/ProvisionListenerStackCallback.java
index 7fdfc13..ad292a6 100644
--- a/core/src/com/google/inject/internal/ProvisionListenerStackCallback.java
+++ b/core/src/com/google/inject/internal/ProvisionListenerStackCallback.java
@@ -16,12 +16,15 @@
 
 package com.google.inject.internal;
 
-import com.google.inject.Key;
+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.
@@ -29,17 +32,27 @@ import java.util.List;
  * @author sameb at google.com (Sam Berlin)
  */
 final class ProvisionListenerStackCallback<T> {
+  
+  private static final ProvisionListener EMPTY_LISTENER[] = new ProvisionListener[0];
+  @SuppressWarnings("rawtypes")
+  private static final ProvisionListenerStackCallback<?> EMPTY_CALLBACK =
+      new ProvisionListenerStackCallback(null /* unused, so ok */, ImmutableList.of());
 
-  private static final ProvisionListener EMPTY_LISTENER[] = new ProvisionListener[0]; 
   private final ProvisionListener[] listeners;
-  private final Key<T> key;
+  private final Binding<T> binding;
+  
+  @SuppressWarnings("unchecked")
+  public static <T> ProvisionListenerStackCallback<T> emptyListener() {
+    return (ProvisionListenerStackCallback<T>) EMPTY_CALLBACK;
+  }
 
-  public ProvisionListenerStackCallback(Key<T> key, List<ProvisionListener> listeners) {
-    this.key = key;
+  public ProvisionListenerStackCallback(Binding<T> binding, List<ProvisionListener> listeners) {
+    this.binding = binding;
     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()]);
     }
   }
   
@@ -64,7 +77,7 @@ final class ProvisionListenerStackCallback<T> {
           provision.erredListener.getClass() : "(unknown)";
       throw errors
           .errorInUserCode(caught, "Error notifying ProvisionListener %s of %s.%n"
-              + " Reason: %s", listener, key, caught)
+              + " Reason: %s", listener, binding.getKey(), caught)
           .toException();
     } else {
       return provision.result;
@@ -105,6 +118,7 @@ final class ProvisionListenerStackCallback<T> {
       } else if (index < listeners.length) {
         int currentIdx = index;
         try {
+
           listeners[index].onProvision(this);
         } catch(RuntimeException re) {
           erredListener = listeners[currentIdx];
@@ -121,8 +135,11 @@ final class ProvisionListenerStackCallback<T> {
     }
     
     @Override
-    public Key<T> getKey() {
-      return key;
+    public Binding<T> getBinding() {
+      // TODO(sameb): Because so many places cast directly to BindingImpl & subclasses,
+      // we can't decorate this to prevent calling getProvider().get(), which means
+      // if someone calls that they'll get strange errors.
+      return binding;
     }
     
     @Override
diff --git a/core/src/com/google/inject/internal/ProxyFactory.java b/core/src/com/google/inject/internal/ProxyFactory.java
index f4e93d2..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 static com.google.inject.internal.BytecodeGen.newFastClass;
 
 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 @@ final class ProxyFactory<T> implements ConstructionProxyFactory<T> {
         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
@@ -170,7 +173,7 @@ final class ProxyFactory<T> implements ConstructionProxyFactory<T> {
     // to this injector. Otherwise, the proxies for each injector will waste PermGen memory
     try {
     Enhancer enhancer = BytecodeGen.newEnhancer(declaringClass, visibility);
-    enhancer.setCallbackFilter(new IndicesCallbackFilter(declaringClass, methods));
+    enhancer.setCallbackFilter(new IndicesCallbackFilter(methods));
     enhancer.setCallbackTypes(callbackTypes);
     return new ProxyConstructor<T>(enhancer, injectionPoint, callbacks, interceptors);
     } catch (Throwable e) {
@@ -199,21 +202,22 @@ final class ProxyFactory<T> implements ConstructionProxyFactory<T> {
   }
 
   /**
-   * A callback filter that maps methods to unique IDs. We define equals and hashCode using
-   * the method-wrapper:indices map so that enhanced classes can be shared between injectors.
+   * A callback filter that maps methods to unique IDs. We define equals and
+   * hashCode without using any state related to the injector so that enhanced
+   * classes intercepting the same methods can be shared between injectors (and
+   * child injectors, etc).
    */
   private static class IndicesCallbackFilter implements CallbackFilter {
     final Map<Object, Integer> indices;
     final int hashCode;
 
-    IndicesCallbackFilter(Class<?> declaringClass, List<Method> methods) {
+    IndicesCallbackFilter(List<Method> methods) {
       final Map<Object, Integer> indices = Maps.newHashMap();
       for (int i = 0; i < methods.size(); i++) {
         indices.put(MethodWrapper.create(methods.get(i)), i);
       }
-
       this.indices = indices;
-      hashCode = indices.hashCode();
+      this.hashCode = indices.hashCode();
     }
 
     public int accept(Method method) {
diff --git a/core/src/com/google/inject/internal/RehashableKeys.java b/core/src/com/google/inject/internal/RehashableKeys.java
new file mode 100644
index 0000000..2fff215
--- /dev/null
+++ b/core/src/com/google/inject/internal/RehashableKeys.java
@@ -0,0 +1,40 @@
+package com.google.inject.internal;
+
+import com.google.inject.Key;
+
+/**
+ * Interface for types that can rehash their {@link Key} instances.
+ *
+ * @author chrispurcell at google.com (Chris Purcell)
+ */
+public interface RehashableKeys {
+  void rehashKeys();
+
+  /** Utility methods for key rehashing. */
+  public static class Keys {
+
+    /**
+     * Returns true if the cached hashcode for the given key is out-of-date. This will only occur
+     * if the key contains a mutable annotation.
+     */
+    public static boolean needsRehashing(Key<?> key) {
+      if (!key.hasAttributes()) {
+        return false;
+      }
+      int newHashCode = key.getTypeLiteral().hashCode() * 31 + key.getAnnotation().hashCode();
+      return (key.hashCode() != newHashCode);
+    }
+
+    /** Returns a copy of the given key with an up-to-date hashcode. */
+    public static <T> Key<T> rehash(Key<T> key) {
+      if (key.hasAttributes()) {
+        // This will recompute the hashcode for us.
+        return Key.get(key.getTypeLiteral(), key.getAnnotation());
+      } else {
+        return key;
+      }
+    }
+
+    private Keys() { }
+  }
+}
diff --git a/core/src/com/google/inject/internal/ScopeBindingProcessor.java b/core/src/com/google/inject/internal/ScopeBindingProcessor.java
index cf0a99f..5d49de3 100644
--- a/core/src/com/google/inject/internal/ScopeBindingProcessor.java
+++ b/core/src/com/google/inject/internal/ScopeBindingProcessor.java
@@ -40,21 +40,21 @@ final class ScopeBindingProcessor extends AbstractProcessor {
     Class<? extends Annotation> annotationType = command.getAnnotationType();
 
     if (!Annotations.isScopeAnnotation(annotationType)) {
-      errors.withSource(annotationType).missingScopeAnnotation();
+      errors.missingScopeAnnotation(annotationType);
       // Go ahead and bind anyway so we don't get collateral errors.
     }
 
     if (!Annotations.isRetainedAtRuntime(annotationType)) {
-      errors.withSource(annotationType)
-          .missingRuntimeRetention(command.getSource());
+      errors.missingRuntimeRetention(annotationType);
       // Go ahead and bind anyway so we don't get collateral errors.
     }
 
-    Scope existing = injector.state.getScope(checkNotNull(annotationType, "annotation type"));
+    ScopeBinding existing = injector.state.getScopeBinding(checkNotNull(annotationType, "annotation type"));
     if (existing != null) {
       errors.duplicateScopes(existing, annotationType, scope);
     } else {
-      injector.state.putAnnotation(annotationType, checkNotNull(scope, "scope"));
+      checkNotNull(scope, "scope");
+      injector.state.putScopeBinding(annotationType, command);
     }
 
     return true;
diff --git a/core/src/com/google/inject/internal/Scoping.java b/core/src/com/google/inject/internal/Scoping.java
index 9dd9524..334aaef 100644
--- a/core/src/com/google/inject/internal/Scoping.java
+++ b/core/src/com/google/inject/internal/Scoping.java
@@ -25,6 +25,7 @@ import com.google.inject.Singleton;
 import com.google.inject.Stage;
 import com.google.inject.binder.ScopedBindingBuilder;
 import com.google.inject.spi.BindingScopingVisitor;
+import com.google.inject.spi.ScopeBinding;
 
 import java.lang.annotation.Annotation;
 
@@ -41,7 +42,7 @@ public abstract class Scoping {
    * in(Scopes.NO_SCOPE)}, where the 'NO_SCOPE' has been explicitly applied.
    */
   public static final Scoping UNSCOPED = new Scoping() {
-    public <V> V acceptVisitor(BindingScopingVisitor<V> visitor) {
+    @Override public <V> V acceptVisitor(BindingScopingVisitor<V> visitor) {
       return visitor.visitNoScoping();
     }
 
@@ -53,13 +54,13 @@ public abstract class Scoping {
       return Scopes.NO_SCOPE.toString();
     }
 
-    public void applyTo(ScopedBindingBuilder scopedBindingBuilder) {
+    @Override public void applyTo(ScopedBindingBuilder scopedBindingBuilder) {
       // do nothing
     }
   };
 
   public static final Scoping SINGLETON_ANNOTATION = new Scoping() {
-    public <V> V acceptVisitor(BindingScopingVisitor<V> visitor) {
+    @Override public <V> V acceptVisitor(BindingScopingVisitor<V> visitor) {
       return visitor.visitScopeAnnotation(Singleton.class);
     }
 
@@ -71,13 +72,13 @@ public abstract class Scoping {
       return Singleton.class.getName();
     }
 
-    public void applyTo(ScopedBindingBuilder scopedBindingBuilder) {
+    @Override public void applyTo(ScopedBindingBuilder scopedBindingBuilder) {
       scopedBindingBuilder.in(Singleton.class);
     }
   };
 
   public static final Scoping SINGLETON_INSTANCE = new Scoping() {
-    public <V> V acceptVisitor(BindingScopingVisitor<V> visitor) {
+    @Override public <V> V acceptVisitor(BindingScopingVisitor<V> visitor) {
       return visitor.visitScope(Scopes.SINGLETON);
     }
 
@@ -89,13 +90,13 @@ public abstract class Scoping {
       return Scopes.SINGLETON.toString();
     }
 
-    public void applyTo(ScopedBindingBuilder scopedBindingBuilder) {
+    @Override public void applyTo(ScopedBindingBuilder scopedBindingBuilder) {
       scopedBindingBuilder.in(Scopes.SINGLETON);
     }
   };
 
   public static final Scoping EAGER_SINGLETON = new Scoping() {
-    public <V> V acceptVisitor(BindingScopingVisitor<V> visitor) {
+    @Override public <V> V acceptVisitor(BindingScopingVisitor<V> visitor) {
       return visitor.visitEagerSingleton();
     }
 
@@ -107,7 +108,7 @@ public abstract class Scoping {
       return "eager singleton";
     }
 
-    public void applyTo(ScopedBindingBuilder scopedBindingBuilder) {
+    @Override public void applyTo(ScopedBindingBuilder scopedBindingBuilder) {
       scopedBindingBuilder.asEagerSingleton();
     }
   };
@@ -119,7 +120,7 @@ public abstract class Scoping {
     }
 
     return new Scoping() {
-      public <V> V acceptVisitor(BindingScopingVisitor<V> visitor) {
+      @Override public <V> V acceptVisitor(BindingScopingVisitor<V> visitor) {
         return visitor.visitScopeAnnotation(scopingAnnotation);
       }
 
@@ -131,7 +132,7 @@ public abstract class Scoping {
         return scopingAnnotation.getName();
       }
 
-      public void applyTo(ScopedBindingBuilder scopedBindingBuilder) {
+      @Override public void applyTo(ScopedBindingBuilder scopedBindingBuilder) {
         scopedBindingBuilder.in(scopingAnnotation);
       }
     };
@@ -143,7 +144,7 @@ public abstract class Scoping {
     }
 
     return new Scoping() {
-      public <V> V acceptVisitor(BindingScopingVisitor<V> visitor) {
+      @Override public <V> V acceptVisitor(BindingScopingVisitor<V> visitor) {
         return visitor.visitScope(scope);
       }
 
@@ -155,7 +156,7 @@ public abstract class Scoping {
         return scope.toString();
       }
 
-      public void applyTo(ScopedBindingBuilder scopedBindingBuilder) {
+      @Override public void applyTo(ScopedBindingBuilder scopedBindingBuilder) {
         scopedBindingBuilder.in(scope);
       }
     };
@@ -254,9 +255,9 @@ public abstract class Scoping {
       return scoping;
     }
 
-    Scope scope = injector.state.getScope(scopeAnnotation);
+    ScopeBinding scope = injector.state.getScopeBinding(scopeAnnotation);
     if (scope != null) {
-      return forInstance(scope);
+      return forInstance(scope.getScope());
     }
 
     errors.scopeNotFound(scopeAnnotation);
diff --git a/core/src/com/google/inject/internal/State.java b/core/src/com/google/inject/internal/State.java
index 9a3e921..d824cc1 100644
--- a/core/src/com/google/inject/internal/State.java
+++ b/core/src/com/google/inject/internal/State.java
@@ -24,6 +24,7 @@ import com.google.inject.Key;
 import com.google.inject.Scope;
 import com.google.inject.TypeLiteral;
 import com.google.inject.spi.ProvisionListenerBinding;
+import com.google.inject.spi.ScopeBinding;
 import com.google.inject.spi.TypeConverterBinding;
 import com.google.inject.spi.TypeListenerBinding;
 
@@ -57,11 +58,11 @@ interface State {
       throw new UnsupportedOperationException();
     }
 
-    public Scope getScope(Class<? extends Annotation> scopingAnnotation) {
+    public ScopeBinding getScopeBinding(Class<? extends Annotation> scopingAnnotation) {
       return null;
     }
 
-    public void putAnnotation(Class<? extends Annotation> annotationType, Scope scope) {
+    public void putScopeBinding(Class<? extends Annotation> annotationType, ScopeBinding scope) {
       throw new UnsupportedOperationException();
     }
 
@@ -133,11 +134,10 @@ interface State {
   Map<Key<?>, Binding<?>> getExplicitBindingsThisLevel();
 
   void putBinding(Key<?> key, BindingImpl<?> binding);
+ 
+  ScopeBinding getScopeBinding(Class<? extends Annotation> scopingAnnotation);
 
-  /** Returns the matching scope, or null. */
-  Scope getScope(Class<? extends Annotation> scopingAnnotation);
-
-  void putAnnotation(Class<? extends Annotation> annotationType, Scope scope);
+  void putScopeBinding(Class<? extends Annotation> annotationType, ScopeBinding scope);
 
   void addConverter(TypeConverterBinding typeConverterBinding);
 
diff --git a/core/src/com/google/inject/internal/TypeConverterBindingProcessor.java b/core/src/com/google/inject/internal/TypeConverterBindingProcessor.java
index 3ad1b68..7ab52ce 100644
--- a/core/src/com/google/inject/internal/TypeConverterBindingProcessor.java
+++ b/core/src/com/google/inject/internal/TypeConverterBindingProcessor.java
@@ -41,35 +41,33 @@ final class TypeConverterBindingProcessor extends AbstractProcessor {
   }
 
   /** Installs default converters for primitives, enums, and class literals. */
-  void prepareBuiltInConverters(InjectorImpl injector) {
-    this.injector = injector;
-    try {
-      // 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);
-
-      convertToClass(Character.class, 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);
+  static void prepareBuiltInConverters(InjectorImpl injector) {
+    // Configure type converters.
+    convertToPrimitiveType(injector, int.class, Integer.class);
+    convertToPrimitiveType(injector, long.class, Long.class);
+    convertToPrimitiveType(injector, boolean.class, Boolean.class);
+    convertToPrimitiveType(injector, byte.class, Byte.class);
+    convertToPrimitiveType(injector, short.class, Short.class);
+    convertToPrimitiveType(injector, float.class, Float.class);
+    convertToPrimitiveType(injector, double.class, Double.class);
+
+    convertToClass(injector, Character.class, 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>";
-        }
-      });
+      @Override public String toString() {
+        return "TypeConverter<Character>";
+      }
+    });
 
-      convertToClasses(Matchers.subclassesOf(Enum.class), new TypeConverter() {
-        @SuppressWarnings("unchecked")
-        public Object convert(String value, TypeLiteral<?> toType) {
+    convertToClasses(injector, Matchers.subclassesOf(Enum.class), new TypeConverter() {
+      @SuppressWarnings("unchecked")
+      public Object convert(String value, TypeLiteral<?> toType) {
           return Enum.valueOf((Class) toType.getRawType(), value);
         }
 
@@ -78,37 +76,34 @@ final class TypeConverterBindingProcessor extends AbstractProcessor {
         }
       });
 
-      internalConvertToTypes(
-        new AbstractMatcher<TypeLiteral<?>>() {
-          public boolean matches(TypeLiteral<?> typeLiteral) {
-            return typeLiteral.getRawType() == Class.class;
-          }
+    internalConvertToTypes(injector, 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 "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<?>>";
-          }
+        @Override public String toString() {
+          return "TypeConverter<Class<?>>";
         }
-      );
-    } finally {
-      this.injector = null;
-    }
+      }
+    );
   }
 
-  private <T> void convertToPrimitiveType(Class<T> primitiveType, final Class<T> wrapperType) {
+  private static <T> void convertToPrimitiveType(InjectorImpl injector, Class<T> primitiveType,
+      final Class<T> wrapperType) {
     try {
       final Method parser = wrapperType.getMethod(
           "parse" + capitalize(primitiveType.getName()), String.class);
@@ -130,19 +125,20 @@ final class TypeConverterBindingProcessor extends AbstractProcessor {
         }
       };
 
-      convertToClass(wrapperType, typeConverter);
+      convertToClass(injector, wrapperType, typeConverter);
     } catch (NoSuchMethodException e) {
       throw new AssertionError(e);
     }
   }
 
-  private <T> void convertToClass(Class<T> type, TypeConverter converter) {
-    convertToClasses(Matchers.identicalTo(type), converter);
+  private static <T> void convertToClass(InjectorImpl injector, Class<T> type,
+      TypeConverter converter) {
+    convertToClasses(injector, Matchers.identicalTo(type), converter);
   }
 
-  private void convertToClasses(final Matcher<? super Class<?>> typeMatcher,
-      TypeConverter converter) {
-    internalConvertToTypes(new AbstractMatcher<TypeLiteral<?>>() {
+  private static void convertToClasses(InjectorImpl injector,
+      final Matcher<? super Class<?>> typeMatcher, TypeConverter converter) {
+    internalConvertToTypes(injector, new AbstractMatcher<TypeLiteral<?>>() {
       public boolean matches(TypeLiteral<?> typeLiteral) {
         Type type = typeLiteral.getType();
         if (!(type instanceof Class)) {
@@ -158,7 +154,8 @@ final class TypeConverterBindingProcessor extends AbstractProcessor {
     }, converter);
   }
 
-  private void internalConvertToTypes(Matcher<? super TypeLiteral<?>> typeMatcher,
+  private static void internalConvertToTypes(InjectorImpl injector,
+      Matcher<? super TypeLiteral<?>> typeMatcher,
       TypeConverter converter) {
     injector.state.addConverter(
         new TypeConverterBinding(SourceProvider.UNKNOWN_SOURCE, typeMatcher, converter));
diff --git a/core/src/com/google/inject/internal/UntargettedBindingImpl.java b/core/src/com/google/inject/internal/UntargettedBindingImpl.java
index 8af74a2..0c31130 100644
--- a/core/src/com/google/inject/internal/UntargettedBindingImpl.java
+++ b/core/src/com/google/inject/internal/UntargettedBindingImpl.java
@@ -16,6 +16,9 @@
 
 package com.google.inject.internal;
 
+import static com.google.inject.internal.RehashableKeys.Keys.needsRehashing;
+import static com.google.inject.internal.RehashableKeys.Keys.rehash;
+
 import com.google.common.base.Objects;
 import com.google.inject.Binder;
 import com.google.inject.Key;
@@ -49,6 +52,14 @@ final class UntargettedBindingImpl<T> extends BindingImpl<T> implements Untarget
     return new UntargettedBindingImpl<T>(getSource(), key, getScoping());
   }
 
+  public BindingImpl<T> withRehashedKeys() {
+    if (needsRehashing(getKey())) {
+      return withKey(rehash(getKey()));
+    } else {
+      return this;
+    }
+  }
+
   public void applyTo(Binder binder) {
     getScoping().applyTo(binder.withSource(getSource()).bind(getKey()));
   }
@@ -58,7 +69,7 @@ final class UntargettedBindingImpl<T> extends BindingImpl<T> implements Untarget
         .add("key", getKey())
         .add("source", getSource())
         .toString();
-  }  
+  }
 
   @Override
   public boolean equals(Object obj) {
@@ -70,7 +81,7 @@ final class UntargettedBindingImpl<T> extends BindingImpl<T> implements Untarget
       return false;
     }
   }
-  
+
   @Override
   public int hashCode() {
     return Objects.hashCode(getKey(), getScoping());
diff --git a/core/src/com/google/inject/internal/util/LineNumbers.java b/core/src/com/google/inject/internal/util/LineNumbers.java
index 227c81e..7c133cb 100644
--- a/core/src/com/google/inject/internal/util/LineNumbers.java
+++ b/core/src/com/google/inject/internal/util/LineNumbers.java
@@ -22,7 +22,6 @@ import com.google.common.base.Preconditions;
 import com.google.common.collect.Maps;
 
 import org.objectweb.asm.AnnotationVisitor;
-import org.objectweb.asm.Attribute;
 import org.objectweb.asm.ClassReader;
 import org.objectweb.asm.ClassVisitor;
 import org.objectweb.asm.FieldVisitor;
@@ -123,12 +122,16 @@ final class LineNumbers {
     end[NO_AOP]*/
   }  
 
-  private class LineNumberReader implements ClassVisitor, MethodVisitor, AnnotationVisitor {
+  private class LineNumberReader extends ClassVisitor {
 
     private int line = -1;
     private String pendingMethod;
     private String name;
 
+    LineNumberReader() {
+      super(Opcodes.ASM4);
+    }
+
     public void visit(int version, int access, String name, String signature,
         String superName, String[] interfaces) {
       this.name = name;
@@ -141,7 +144,7 @@ final class LineNumbers {
       }
       pendingMethod = name + desc;
       line = -1;
-      return this;
+      return new LineNumberMethodVisitor();
     }
 
     public void visitSource(String source, String debug) {
@@ -160,113 +163,61 @@ final class LineNumbers {
       }
     }
 
-    public void visitFieldInsn(int opcode, String owner, String name,
-        String desc) {
-      if (opcode == Opcodes.PUTFIELD && this.name.equals(owner)
-          && !lines.containsKey(name) && line != -1) {
-        lines.put(name, line);
-      }
-    }
-
-    public void visitEnd() {
-    }
-
-    public void visitInnerClass(String name, String outerName, String innerName,
-        int access) {
-    }
-
-    public void visitOuterClass(String owner, String name, String desc) {
-    }
-
-    public void visitAttribute(Attribute attr) {
-    }
-
     public FieldVisitor visitField(int access, String name, String desc,
         String signature, Object value) {
       return null;
     }
 
     public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
-      return this;
-    }
-
-    public AnnotationVisitor visitAnnotation(String name, String desc) {
-      return this;
-    }
-
-    public AnnotationVisitor visitAnnotationDefault() {
-      return this;
+      return new LineNumberAnnotationVisitor();
     }
 
     public AnnotationVisitor visitParameterAnnotation(int parameter,
         String desc, boolean visible) {
-      return this;
-    }
-
-    public AnnotationVisitor visitArray(String name) {
-      return this;
-    }
-
-    public void visitEnum(String name, String desc, String value) {
-    }
-
-    public void visit(String name, Object value) {
-    }
-
-    public void visitCode() {
-    }
-
-    public void visitFrame(int type, int nLocal, Object[] local, int nStack,
-        Object[] stack) {
-    }
-
-    public void visitIincInsn(int var, int increment) {
-    }
-
-    public void visitInsn(int opcode) {
-    }
-
-    public void visitIntInsn(int opcode, int operand) {
-    }
-
-    public void visitJumpInsn(int opcode, Label label) {
+      return new LineNumberAnnotationVisitor();
     }
 
-    public void visitLabel(Label label) {
-    }
-
-    public void visitLdcInsn(Object cst) {
-    }
-
-    public void visitLocalVariable(String name, String desc, String signature,
-        Label start, Label end, int index) {
-    }
-
-    public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
-    }
+    class LineNumberMethodVisitor extends MethodVisitor {
+      LineNumberMethodVisitor() {
+        super(Opcodes.ASM4);
+      }
 
-    public void visitMaxs(int maxStack, int maxLocals) {
-    }
+      public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+        return new LineNumberAnnotationVisitor();
+      }
 
-    public void visitMethodInsn(int opcode, String owner, String name,
-        String desc) {
-    }
+      public AnnotationVisitor visitAnnotationDefault() {
+        return new LineNumberAnnotationVisitor();
+      }
 
-    public void visitMultiANewArrayInsn(String desc, int dims) {
-    }
+      public void visitFieldInsn(int opcode, String owner, String name,
+          String desc) {
+        if (opcode == Opcodes.PUTFIELD && LineNumberReader.this.name.equals(owner)
+            && !lines.containsKey(name) && line != -1) {
+          lines.put(name, line);
+        }
+      }
 
-    public void visitTableSwitchInsn(int min, int max, Label dflt,
-        Label[] labels) {
+      public void visitLineNumber(int line, Label start) {
+        LineNumberReader.this.visitLineNumber(line, start);
+      }
     }
 
-    public void visitTryCatchBlock(Label start, Label end, Label handler,
-        String type) {
-    }
+    class LineNumberAnnotationVisitor extends AnnotationVisitor {
+      LineNumberAnnotationVisitor() {
+        super(Opcodes.ASM4);
+      }
+      public AnnotationVisitor visitAnnotation(String name, String desc) {
+        return this;
+      }
+      public AnnotationVisitor visitArray(String name) {
+        return this;
+      }
+      public void visitLocalVariable(String name, String desc, String signature,
+          Label start, Label end, int index) {
+      }
 
-    public void visitTypeInsn(int opcode, String desc) {
     }
 
-    public void visitVarInsn(int opcode, int var) {
-    }
   }
 }
diff --git a/core/src/com/google/inject/internal/util/SourceProvider.java b/core/src/com/google/inject/internal/util/SourceProvider.java
index 75a0353..b36cea8 100644
--- a/core/src/com/google/inject/internal/util/SourceProvider.java
+++ b/core/src/com/google/inject/internal/util/SourceProvider.java
@@ -16,8 +16,7 @@
 
 package com.google.inject.internal.util;
 
-import static com.google.common.collect.Iterables.concat;
-
+import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
 
@@ -33,20 +32,42 @@ public final class SourceProvider {
   /** Indicates that the source is unknown. */
   public static final Object UNKNOWN_SOURCE = "[unknown source]";
 
+  private final SourceProvider parent;
   private final ImmutableSet<String> classNamesToSkip;
-
+  
   public static final SourceProvider DEFAULT_INSTANCE
       = new SourceProvider(ImmutableSet.of(SourceProvider.class.getName()));
 
   private SourceProvider(Iterable<String> classesToSkip) {
-    this.classNamesToSkip = ImmutableSet.copyOf(classesToSkip);
+    this(null, classesToSkip);
+  }
+
+  private SourceProvider(SourceProvider parent, Iterable<String> classesToSkip) {
+    this.parent = parent;
+    
+    ImmutableSet.Builder<String> classNamesToSkipBuilder = ImmutableSet.builder();
+    for (String classToSkip : classesToSkip) {
+      if (parent == null || !parent.shouldBeSkipped(classToSkip)) {
+        classNamesToSkipBuilder.add(classToSkip);
+      }
+    }
+    this.classNamesToSkip = classNamesToSkipBuilder.build();
   }
 
   /** Returns a new instance that also skips {@code moreClassesToSkip}. */
   public SourceProvider plusSkippedClasses(Class... moreClassesToSkip) {
-    return new SourceProvider(concat(classNamesToSkip, asStrings(moreClassesToSkip)));
+    return new SourceProvider(this, asStrings(moreClassesToSkip));
   }
 
+  /** Returns true if the className should be skipped. */
+  private boolean shouldBeSkipped(String className) {
+    if ((parent != null && parent.shouldBeSkipped(className))
+        || classNamesToSkip.contains(className)) {
+      return true;
+    }
+    return false;
+  }
+  
   /** Returns the class names as Strings */
   private static List<String> asStrings(Class... classes) {
     List<String> strings = Lists.newArrayList();
@@ -60,13 +81,28 @@ public final class SourceProvider {
    * Returns the calling line of code. The selected line is the nearest to the top of the stack that
    * is not skipped.
    */
-  public StackTraceElement get() {
-    for (final StackTraceElement element : new Throwable().getStackTrace()) {
+  public StackTraceElement get(StackTraceElement[] stackTraceElements) {
+    Preconditions.checkNotNull(stackTraceElements, "The stack trace elements cannot be null.");
+    for (final StackTraceElement element : stackTraceElements) {
       String className = element.getClassName();
-      if (!classNamesToSkip.contains(className)) {
+      
+      if (!shouldBeSkipped(className)) {
         return element;
       }
     }
     throw new AssertionError();
   }
+
+  /**
+   * Returns the non-skipped module class name.
+   */
+  public Object getFromClassNames(List<String> moduleClassNames) {
+    Preconditions.checkNotNull(moduleClassNames, "The list of module class names cannot be null.");
+    for (final String moduleClassName : moduleClassNames) {
+      if (!shouldBeSkipped(moduleClassName)) {
+        return new StackTraceElement(moduleClassName, "configure", null, -1);
+      }
+    }
+    return UNKNOWN_SOURCE;
+  }
 }
diff --git a/core/src/com/google/inject/internal/util/StackTraceElements.java b/core/src/com/google/inject/internal/util/StackTraceElements.java
index 4f5b167..69f930f 100644
--- a/core/src/com/google/inject/internal/util/StackTraceElements.java
+++ b/core/src/com/google/inject/internal/util/StackTraceElements.java
@@ -16,7 +16,9 @@
 
 package com.google.inject.internal.util;
 
-import com.google.common.base.Function;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
 import com.google.common.collect.MapMaker;
 
 import java.io.IOException;
@@ -31,20 +33,28 @@ import java.util.Map;
  */
 public class StackTraceElements {
 
+  private static final StackTraceElement[] EMPTY_STACK_TRACE = new StackTraceElement[0];
+  private static final InMemoryStackTraceElement[] EMPTY_INMEMORY_STACK_TRACE = 
+      new InMemoryStackTraceElement[0];
+
   /*if[AOP]*/
-  static final Map<Class<?>, LineNumbers> lineNumbersCache = new MapMaker().weakKeys().softValues()
-      .makeComputingMap(new Function<Class<?>, LineNumbers>() {
-        public LineNumbers apply(Class<?> key) {
-          try {
-            return new LineNumbers(key);
-          }
-          catch (IOException e) {
-            throw new RuntimeException(e);
-          }
-        }
-      });
+  static final LoadingCache<Class<?>, LineNumbers> lineNumbersCache =
+      CacheBuilder.newBuilder().weakKeys().softValues().build(
+          new CacheLoader<Class<?>, LineNumbers>() {
+            public LineNumbers load(Class<?> key) {
+              try {
+                return new LineNumbers(key);
+              }
+              catch (IOException e) {
+                throw new RuntimeException(e);
+              }
+            }
+          });
   /*end[AOP]*/
 
+  private static Map<Object, Object> cache = new MapMaker().makeMap();
+  private static final String UNKNOWN_SOURCE = "Unknown Source";
+
   public static Object forMember(Member member) {
     if (member == null) {
       return SourceProvider.UNKNOWN_SOURCE;
@@ -53,7 +63,7 @@ public class StackTraceElements {
     Class declaringClass = member.getDeclaringClass();
 
     /*if[AOP]*/
-    LineNumbers lineNumbers = lineNumbersCache.get(declaringClass);
+    LineNumbers lineNumbers = lineNumbersCache.getUnchecked(declaringClass);
     String fileName = lineNumbers.getSource();
     Integer lineNumberOrNull = lineNumbers.getLineNumber(member);
     int lineNumber = lineNumberOrNull == null ? lineNumbers.getFirstLine() : lineNumberOrNull;
@@ -70,7 +80,7 @@ public class StackTraceElements {
 
   public static Object forType(Class<?> implementation) {
     /*if[AOP]*/
-    LineNumbers lineNumbers = lineNumbersCache.get(implementation);
+    LineNumbers lineNumbers = lineNumbersCache.getUnchecked(implementation);
     int lineNumber = lineNumbers.getFirstLine();
     String fileName = lineNumbers.getSource();
     /*end[AOP]*/
@@ -81,4 +91,128 @@ public class StackTraceElements {
 
     return new StackTraceElement(implementation.getName(), "class", fileName, lineNumber);
   }
+  
+  /**
+   * Clears the internal cache for {@link StackTraceElement StackTraceElements}.
+   */
+  public static void clearCache() {
+    cache.clear();
+  }
+  
+  /**
+   * Returns encoded in-memory version of {@link StackTraceElement StackTraceElements}.
+   */
+  public static InMemoryStackTraceElement[] convertToInMemoryStackTraceElement(
+      StackTraceElement[] stackTraceElements) {
+    if (stackTraceElements.length == 0) {
+      return EMPTY_INMEMORY_STACK_TRACE;
+    }
+    InMemoryStackTraceElement[] inMemoryStackTraceElements = 
+        new InMemoryStackTraceElement[stackTraceElements.length];
+    for (int i = 0; i < stackTraceElements.length; i++) {
+      inMemoryStackTraceElements[i] = 
+          weakIntern(new InMemoryStackTraceElement(stackTraceElements[i]));
+    }
+    return inMemoryStackTraceElements;
+  }
+  
+  /**
+   * Decodes in-memory stack trace elements to regular {@link StackTraceElement StackTraceElements}.
+   */
+  public static StackTraceElement[] convertToStackTraceElement(
+      InMemoryStackTraceElement[] inMemoryStackTraceElements) {
+    if (inMemoryStackTraceElements.length == 0) {
+      return EMPTY_STACK_TRACE;
+    }
+    StackTraceElement[] stackTraceElements = 
+        new StackTraceElement[inMemoryStackTraceElements.length];
+    for (int i = 0; i < inMemoryStackTraceElements.length; i++) {
+      String declaringClass = inMemoryStackTraceElements[i].getClassName();
+      String methodName = inMemoryStackTraceElements[i].getMethodName();
+      int lineNumber = inMemoryStackTraceElements[i].getLineNumber();
+      stackTraceElements[i] = 
+          new StackTraceElement(declaringClass, methodName, UNKNOWN_SOURCE, lineNumber);
+    }
+    return stackTraceElements;
+  }
+  
+  private static InMemoryStackTraceElement weakIntern(
+      InMemoryStackTraceElement inMemoryStackTraceElement) {
+    InMemoryStackTraceElement cached = 
+        (InMemoryStackTraceElement) cache.get(inMemoryStackTraceElement);
+    if (cached != null) {
+      return cached;
+    }
+    inMemoryStackTraceElement = new InMemoryStackTraceElement(
+        weakIntern(inMemoryStackTraceElement.getClassName()), 
+        weakIntern(inMemoryStackTraceElement.getMethodName()), 
+        inMemoryStackTraceElement.getLineNumber());
+    cache.put(inMemoryStackTraceElement, inMemoryStackTraceElement);
+    return inMemoryStackTraceElement;
+  }
+  
+  private static String weakIntern(String s) {
+    String cached = (String) cache.get(s);
+    if (cached != null) {
+      return cached;
+    }
+    cache.put(s, s);
+    return s;  
+  }
+  
+  /**
+   * In-Memory version of {@link StackTraceElement} that does not store the file name. 
+   */
+  public static class InMemoryStackTraceElement {
+    private String declaringClass;
+    private String methodName;
+    private int lineNumber;
+
+    InMemoryStackTraceElement(StackTraceElement ste) {
+      this(ste.getClassName(), ste.getMethodName(), ste.getLineNumber());
+    }
+
+    InMemoryStackTraceElement(String declaringClass, String methodName, int lineNumber) {
+      this.declaringClass = declaringClass;
+      this.methodName = methodName;
+      this.lineNumber = lineNumber;
+    }
+
+    String getClassName() {
+      return declaringClass;
+    }
+    
+    String getMethodName() {
+      return methodName;
+    }
+    
+    int getLineNumber() {
+      return lineNumber;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+      if (obj == this) {
+        return true;
+      }
+      if (!(obj instanceof InMemoryStackTraceElement)) {
+        return false;
+      }
+      InMemoryStackTraceElement e = (InMemoryStackTraceElement) obj;
+      return e.declaringClass.equals(declaringClass) && e.lineNumber == lineNumber && 
+          methodName.equals(e.methodName);
+    }
+
+    @Override
+    public int hashCode() {
+      int result = 31 * declaringClass.hashCode() + methodName.hashCode();
+      result = 31 * result + lineNumber;
+      return result;
+    }
+    
+    @Override
+    public String toString() {
+      return declaringClass + "." + methodName + "(" + lineNumber + ")";
+    }
+  }
 }
diff --git a/core/src/com/google/inject/spi/DefaultBindingTargetVisitor.java b/core/src/com/google/inject/spi/DefaultBindingTargetVisitor.java
index e5cfd4d..3deb9dd 100644
--- a/core/src/com/google/inject/spi/DefaultBindingTargetVisitor.java
+++ b/core/src/com/google/inject/spi/DefaultBindingTargetVisitor.java
@@ -69,9 +69,9 @@ public abstract class DefaultBindingTargetVisitor<T, V> implements BindingTarget
     return visitOther(convertedConstantBinding);
   }
 
-   // javac says it's an error to cast ProviderBinding<? extends T> to Binding<? extends T>
   @SuppressWarnings("unchecked")
   public V visit(ProviderBinding<? extends T> providerBinding) {
-    return visitOther((Binding) providerBinding);
+    // TODO(cushon): remove raw (Binding) cast when we don't care about javac 6 anymore
+    return visitOther((Binding<? extends T>) (Binding) providerBinding);
   }
 }
diff --git a/core/src/com/google/inject/spi/DefaultElementVisitor.java b/core/src/com/google/inject/spi/DefaultElementVisitor.java
index cfceea7..0780bf8 100644
--- a/core/src/com/google/inject/spi/DefaultElementVisitor.java
+++ b/core/src/com/google/inject/spi/DefaultElementVisitor.java
@@ -94,4 +94,12 @@ public abstract class DefaultElementVisitor<V> implements ElementVisitor<V> {
   public V visit(RequireExplicitBindingsOption option) {
     return visitOther(option);
   }
+  
+  public V visit(RequireAtInjectOnConstructorsOption option) {
+    return visitOther(option);
+  }
+
+  public V visit(RequireExactBindingAnnotationsOption option) {
+    return visitOther(option);
+  }
 }
diff --git a/core/src/com/google/inject/spi/DependencyAndSource.java b/core/src/com/google/inject/spi/DependencyAndSource.java
index aa1d5db..d2fbb95 100644
--- a/core/src/com/google/inject/spi/DependencyAndSource.java
+++ b/core/src/com/google/inject/spi/DependencyAndSource.java
@@ -16,7 +16,9 @@
 
 package com.google.inject.spi;
 
+import com.google.inject.Binder;
 import com.google.inject.Binding;
+import com.google.inject.Injector;
 import com.google.inject.internal.util.StackTraceElements;
 
 import java.lang.reflect.Member;
@@ -40,7 +42,7 @@ public final class DependencyAndSource {
    * Returns the Dependency, if one exists. For anything that can be referenced
    * by {@link Injector#getBinding}, a dependency exists. A dependency will not
    * exist (and this will return null) for types initialized with
-   * {@link Binder#requestInjection} or {@link Injector#injectMembers(Object),
+   * {@link Binder#requestInjection} or {@link Injector#injectMembers(Object)},
    * nor will it exist for objects injected into Providers bound with
    * LinkedBindingBuilder#toProvider(Provider).
    */
diff --git a/core/src/com/google/inject/spi/ElementSource.java b/core/src/com/google/inject/spi/ElementSource.java
new file mode 100644
index 0000000..35246d4
--- /dev/null
+++ b/core/src/com/google/inject/spi/ElementSource.java
@@ -0,0 +1,166 @@
+package com.google.inject.spi;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.google.inject.internal.util.StackTraceElements;
+import com.google.inject.internal.util.StackTraceElements.InMemoryStackTraceElement;
+
+import java.util.List;
+
+/**
+ * Contains information about where and how an {@link Element element} was bound.
+ * <p> 
+ * The {@link #getDeclaringSource() declaring source} refers to a location in source code that 
+ * defines the Guice {@link Element element}. For example, if the element is created from a method
+ * annotated by {@literal @Provides}, the declaring source of element would be the method itself. 
+ * <p>
+ * The {@link #getStackTrace()} refers to the sequence of calls ends at one of {@link Binder} 
+ * {@code bindXXX()} methods and eventually defines the element. Note that {@link #getStackTrace()} 
+ * lists {@link StackTraceElement StackTraceElements} in reverse chronological order. The first 
+ * element (index zero) is the last method call and the last element is the first method invocation.
+ * By default, the stack trace is not collected. The default behavior can be changed by setting the 
+ * {@code guice_include_stack_traces} flag value. The value can be either {@code OFF}, {@code
+ * ONLY_FOR_DECLARING_SOURCE} or {@code COMPLETE}. Note that collecting stack traces for every
+ * binding can cause a performance hit when the injector is created.
+ * <p>
+ * The sequence of class names of {@link Module modules} involved in the element creation can be 
+ * retrieved by {@link #getModuleClassNames()}. Similar to {@link #getStackTrace()}, the order is 
+ * reverse chronological. The first module (index 0) is the module that installs the {@link Element 
+ * element}. The last module is the root module.
+ * <p>
+ * In order to support the cases where a Guice {@link Element element} is created from another
+ * Guice {@link Element element} (original) (e.g., by {@link Element#applyTo()}), it also
+ * provides a reference to the original element source ({@link #getOriginalElementSource()}).
+ */
+public final class ElementSource {
+
+  /** 
+   * The {@link ElementSource source} of element that this element created from (if there is any),
+   * otherwise {@code null}.
+   */
+  final ElementSource originalElementSource;
+  
+  /** The {@link ModuleSource source} of module creates the element. */
+  final ModuleSource moduleSource;
+  
+  /** 
+   * The partial call stack that starts at the last module {@link Module#Configure(Binder)
+   * configure(Binder)} call. The value is empty if stack trace collection is off.
+   */
+  final InMemoryStackTraceElement[] partialCallStack;
+  
+  /** 
+   * Refers to a single location in source code that causes the element creation. It can be any 
+   * object such as {@link Constructor}, {@link Method}, {@link Field}, {@link StackTraceElement}, 
+   * etc. For example, if the element is created from a method annotated by {@literal @Provides}, 
+   * the declaring source of element would be the method itself.
+   */
+  final Object declaringSource;
+
+  /**
+   * Creates a new {@ElementSource} from the given parameters. 
+   * @param originalElementSource The source of element that this element created from (if there is
+   * any), otherwise {@code null}.
+   * @param declaringSource the source (in)directly declared the element.
+   * @param moduleSource the moduleSource when the element is bound
+   * @param partialCallStack the partial call stack from the top module to where the element is 
+   * bound
+   */
+  ElementSource(/* @Nullable */ ElementSource originalSource, Object declaringSource, 
+      ModuleSource moduleSource, StackTraceElement[] partialCallStack) {
+    Preconditions.checkNotNull(declaringSource, "declaringSource cannot be null.");
+    Preconditions.checkNotNull(moduleSource, "moduleSource cannot be null.");
+    Preconditions.checkNotNull(partialCallStack, "partialCallStack cannot be null.");
+    this.originalElementSource = originalSource;
+    this.declaringSource = declaringSource;
+    this.moduleSource = moduleSource;
+    this.partialCallStack = StackTraceElements.convertToInMemoryStackTraceElement(partialCallStack);
+  }
+  
+  /**
+   * Returns the {@link ElementSource} of the element this was created or copied from. If this was
+   * not created or copied from another element, returns {@code null}.
+   */
+  public ElementSource getOriginalElementSource() {
+    return originalElementSource;
+  }
+  
+  /**
+   * Returns a single location in source code that defines the element. It can be any object
+   * such as {@link Constructor}, {@link Method}, {@link Field}, {@link StackTraceElement}, etc. For
+   * example, if the element is created from a method annotated by {@literal @Provides}, the 
+   * declaring source of element would be the method itself.
+   */
+  public Object getDeclaringSource() {
+    return declaringSource;
+  }
+  
+  /**
+   * Returns the class names of modules involved in creating this {@link Element}. The first
+   * element (index 0) is the class name of module that defined the element, and the last element
+   * is the class name of root module.
+   */
+  public List<String> getModuleClassNames() {
+    return moduleSource.getModuleClassNames();
+  }
+
+  /**
+   * Returns the position of {@link Module#configure(Binder) configure(Binder)} method call in the
+   * {@link #getStackTrace() stack trace} for modules that their classes returned by
+   * {@link #getModuleClassNames()}. For example, if the stack trace looks like the following:
+   * <p>
+   * {@code
+   *  0 - Binder.bind(),
+   *  1 - ModuleTwo.configure(),
+   *  2 - Binder.install(),
+   *  3 - ModuleOne.configure(),
+   *  4 - theRest(). 
+   * }
+   * <p>
+   * 1 and 3 are returned.
+   * <p>
+   * In the cases where stack trace is not available (i.e., the stack trace was not collected),
+   * it returns -1 for all module positions.
+   */
+  public List<Integer> getModuleConfigurePositionsInStackTrace() {
+    int size = moduleSource.size();
+    Integer[] positions = new Integer[size];
+    int chunkSize = partialCallStack.length;
+    positions[0] = chunkSize - 1;
+    ModuleSource current = moduleSource;
+    for (int cursor = 1; cursor < size; cursor++) {
+      chunkSize = current.getPartialCallStackSize();
+      positions[cursor] = positions[cursor - 1] + chunkSize;
+      current = current.getParent();
+    }
+    return ImmutableList.<Integer>copyOf(positions);
+  }
+
+  /**
+   * Returns the sequence of method calls that ends at one of {@link Binder} {@code bindXXX()} 
+   * methods and eventually defines the element. Note that {@link #getStackTrace()} lists {@link 
+   * StackTraceElement StackTraceElements} in reverse chronological order. The first element (index 
+   * zero) is the last method call and the last element is the first method invocation. In the cases
+   * where stack trace is not available (i.e.,the stack trace was not collected), it returns an 
+   * empty array.
+   */
+  public StackTraceElement[] getStackTrace() {
+    int modulesCallStackSize = moduleSource.getStackTraceSize();
+    int chunkSize = partialCallStack.length;
+    int size = moduleSource.getStackTraceSize() + chunkSize;
+    StackTraceElement[] callStack = new StackTraceElement[size];
+    System.arraycopy(
+        StackTraceElements.convertToStackTraceElement(partialCallStack), 0, callStack, 0, 
+        chunkSize);
+    System.arraycopy(moduleSource.getStackTrace(), 0, callStack, chunkSize, modulesCallStackSize);
+    return callStack;
+  }
+
+  /**
+   * Returns {@code getDeclaringSource().toString()} value.
+   */
+  @Override
+  public String toString() {
+    return getDeclaringSource().toString();
+  }
+}
diff --git a/core/src/com/google/inject/spi/ElementVisitor.java b/core/src/com/google/inject/spi/ElementVisitor.java
index 6ebc8cc..5e99086 100644
--- a/core/src/com/google/inject/spi/ElementVisitor.java
+++ b/core/src/com/google/inject/spi/ElementVisitor.java
@@ -17,6 +17,7 @@
 package com.google.inject.spi;
 
 import com.google.inject.Binding;
+import com.google.inject.Inject;
 
 /**
  * Visit elements.
@@ -105,4 +106,18 @@ public interface ElementVisitor<V> {
    * @since 3.0
    */
   V visit(DisableCircularProxiesOption option);
+  
+  /**
+   * Visit a require explicit {@literal @}{@link Inject} command.
+   * 
+   * @since 4.0
+   */
+  V visit(RequireAtInjectOnConstructorsOption option);
+
+  /**
+   * Visit a require exact binding annotations command.
+   *
+   * @since 4.0
+   */
+  V visit(RequireExactBindingAnnotationsOption option);
 }
diff --git a/core/src/com/google/inject/spi/Elements.java b/core/src/com/google/inject/spi/Elements.java
index 58bdfa3..6129255 100644
--- a/core/src/com/google/inject/spi/Elements.java
+++ b/core/src/com/google/inject/spi/Elements.java
@@ -17,10 +17,13 @@
 package com.google.inject.spi;
 
 import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.inject.internal.InternalFlags.IncludeStackTraceOption;
+import static com.google.inject.internal.InternalFlags.getIncludeStackTraceOption;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
+
 import com.google.inject.AbstractModule;
 import com.google.inject.Binder;
 import com.google.inject.Binding;
@@ -43,7 +46,9 @@ import com.google.inject.internal.Errors;
 import com.google.inject.internal.ExposureBuilder;
 import com.google.inject.internal.PrivateElementsImpl;
 import com.google.inject.internal.ProviderMethodsModule;
+import com.google.inject.internal.RehashableKeys;
 import com.google.inject.internal.util.SourceProvider;
+import com.google.inject.internal.util.StackTraceElements;
 import com.google.inject.matcher.Matcher;
 
 import java.lang.annotation.Annotation;
@@ -62,6 +67,7 @@ import java.util.Set;
  * @since 2.0
  */
 public final class Elements {
+
   private static final BindingTargetVisitor<Object, Object> GET_INSTANCE_VISITOR
       = new DefaultBindingTargetVisitor<Object, Object>() {
     @Override public Object visit(InstanceBinding<?> binding) {
@@ -102,20 +108,31 @@ public final class Elements {
     for (Module module : modules) {
       binder.install(module);
     }
+    // Free the memory consumed by the stack trace elements cache
+    StackTraceElements.clearCache();
+    binder.rehashKeys();
     return Collections.unmodifiableList(binder.elements);
   }
+  
+  private static class ElementsAsModule implements Module {
+    private final Iterable<? extends Element> elements;
+
+    ElementsAsModule(Iterable<? extends Element> elements) {
+      this.elements = elements;
+    }
+
+    public void configure(Binder binder) {
+      for (Element element : elements) {
+        element.applyTo(binder);
+      }
+    }
+  }
 
   /**
    * Returns the module composed of {@code elements}.
    */
   public static Module getModule(final Iterable<? extends Element> elements) {
-    return new Module() {
-      public void configure(Binder binder) {
-        for (Element element : elements) {
-          element.applyTo(binder);
-        }
-      }
-    };
+    return new ElementsAsModule(elements);
   }
 
   @SuppressWarnings("unchecked")
@@ -123,11 +140,14 @@ public final class Elements {
     return (BindingTargetVisitor<T, T>) GET_INSTANCE_VISITOR;
   }
 
-  private static class RecordingBinder implements Binder, PrivateBinder {
+  private static class RecordingBinder implements Binder, PrivateBinder, RehashableKeys {
     private final Stage stage;
     private final Set<Module> modules;
     private final List<Element> elements;
+    private final List<RehashableKeys> rehashables;
     private final Object source;
+    /** The current modules stack */
+    private ModuleSource moduleSource = null;
     private final SourceProvider sourceProvider;
 
     /** The binder where exposed bindings will be created */
@@ -138,6 +158,7 @@ public final class Elements {
       this.stage = stage;
       this.modules = Sets.newHashSet();
       this.elements = Lists.newArrayList();
+      this.rehashables = Lists.newArrayList();
       this.source = null;
       this.sourceProvider = SourceProvider.DEFAULT_INSTANCE.plusSkippedClasses(
           Elements.class, RecordingBinder.class, AbstractModule.class,
@@ -154,7 +175,9 @@ public final class Elements {
       this.stage = prototype.stage;
       this.modules = prototype.modules;
       this.elements = prototype.elements;
+      this.rehashables = prototype.rehashables;
       this.source = source;
+      this.moduleSource = prototype.moduleSource;
       this.sourceProvider = sourceProvider;
       this.parent = prototype.parent;
       this.privateElements = prototype.privateElements;
@@ -165,7 +188,9 @@ public final class Elements {
       this.stage = parent.stage;
       this.modules = Sets.newHashSet();
       this.elements = privateElements.getElementsMutable();
+      this.rehashables = Lists.newArrayList();
       this.source = parent.source;
+      this.moduleSource = parent.moduleSource;
       this.sourceProvider = parent.sourceProvider;
       this.parent = parent;
       this.privateElements = privateElements;
@@ -176,26 +201,27 @@ public final class Elements {
         Matcher<? super Class<?>> classMatcher,
         Matcher<? super Method> methodMatcher,
         org.aopalliance.intercept.MethodInterceptor... interceptors) {
-      elements.add(new InterceptorBinding(getSource(), classMatcher, methodMatcher, interceptors));
+      elements.add(new InterceptorBinding(
+          getElementSource(), classMatcher, methodMatcher, interceptors));
     }
     /*end[AOP]*/
 
     public void bindScope(Class<? extends Annotation> annotationType, Scope scope) {
-      elements.add(new ScopeBinding(getSource(), annotationType, scope));
+      elements.add(new ScopeBinding(getElementSource(), annotationType, scope));
     }
 
     @SuppressWarnings("unchecked") // it is safe to use the type literal for the raw type
     public void requestInjection(Object instance) {
-      requestInjection((TypeLiteral) TypeLiteral.get(instance.getClass()), instance);
+      requestInjection((TypeLiteral<Object>) TypeLiteral.get(instance.getClass()), instance);
     }
 
     public <T> void requestInjection(TypeLiteral<T> type, T instance) {
-      elements.add(new InjectionRequest<T>(getSource(), type, instance));
+      elements.add(new InjectionRequest<T>(getElementSource(), type, instance));
     }
 
     public <T> MembersInjector<T> getMembersInjector(final TypeLiteral<T> typeLiteral) {
       final MembersInjectorLookup<T> element
-          = new MembersInjectorLookup<T>(getSource(), typeLiteral);
+          = new MembersInjectorLookup<T>(getElementSource(), typeLiteral);
       elements.add(element);
       return element.getMembersInjector();
     }
@@ -205,26 +231,30 @@ public final class Elements {
     }
 
     public void bindListener(Matcher<? super TypeLiteral<?>> typeMatcher, TypeListener listener) {
-      elements.add(new TypeListenerBinding(getSource(), listener, typeMatcher));
+      elements.add(new TypeListenerBinding(getElementSource(), listener, typeMatcher));
     }
     
-    public void bindListener(Matcher<? super Key<?>> keyMatcher, ProvisionListener... listeners) {
-      elements.add(new ProvisionListenerBinding(getSource(), keyMatcher, listeners));
+    public void bindListener(Matcher<? super Binding<?>> bindingMatcher,
+        ProvisionListener... listeners) {
+      elements.add(new ProvisionListenerBinding(getElementSource(), bindingMatcher, listeners));
     }
 
     public void requestStaticInjection(Class<?>... types) {
       for (Class<?> type : types) {
-        elements.add(new StaticInjectionRequest(getSource(), type));
+        elements.add(new StaticInjectionRequest(getElementSource(), type));
       }
     }
 
     public void install(Module module) {
       if (modules.add(module)) {
         Binder binder = this;
+        // Update the module source for the new module
+        if (!(module instanceof ProviderMethodsModule)) {
+          moduleSource = getModuleSource(module);
+        }
         if (module instanceof PrivateModule) {
           binder = binder.newPrivateBinder();
-        }
-
+        }      
         try {
           module.configure(binder);
         } catch (RuntimeException e) {
@@ -236,6 +266,10 @@ public final class Elements {
           }
         }
         binder.install(ProviderMethodsModule.forModule(module));
+        // We are done with this module, so undo module source change
+        if (!(module instanceof ProviderMethodsModule)) {
+          moduleSource = moduleSource.getParent();
+        }
       }
     }
 
@@ -244,12 +278,12 @@ public final class Elements {
     }
 
     public void addError(String message, Object... arguments) {
-      elements.add(new Message(getSource(), Errors.format(message, arguments)));
+      elements.add(new Message(getElementSource(), Errors.format(message, arguments)));
     }
 
     public void addError(Throwable t) {
       String message = "An exception was caught and reported. Message: " + t.getMessage();
-      elements.add(new Message(ImmutableList.of(getSource()), message, t));
+      elements.add(new Message(ImmutableList.of((Object) getElementSource()), message, t));
     }
 
     public void addError(Message message) {
@@ -257,7 +291,9 @@ public final class Elements {
     }
 
     public <T> AnnotatedBindingBuilder<T> bind(Key<T> key) {
-      return new BindingBuilder<T>(this, elements, getSource(), key);
+      BindingBuilder<T> builder = new BindingBuilder<T>(this, elements, getElementSource(), key);
+      rehashables.add(builder);
+      return builder;
     }
 
     public <T> AnnotatedBindingBuilder<T> bind(TypeLiteral<T> typeLiteral) {
@@ -269,12 +305,13 @@ public final class Elements {
     }
 
     public AnnotatedConstantBindingBuilder bindConstant() {
-      return new ConstantBindingBuilderImpl<Void>(this, elements, getSource());
+      return new ConstantBindingBuilderImpl<Void>(this, elements, getElementSource());
     }
 
     public <T> Provider<T> getProvider(final Key<T> key) {
-      final ProviderLookup<T> element = new ProviderLookup<T>(getSource(), key);
+      final ProviderLookup<T> element = new ProviderLookup<T>(getElementSource(), key);
       elements.add(element);
+      rehashables.add(element.getKeyRehasher());
       return element.getProvider();
     }
 
@@ -284,11 +321,11 @@ public final class Elements {
 
     public void convertToTypes(Matcher<? super TypeLiteral<?>> typeMatcher,
         TypeConverter converter) {
-      elements.add(new TypeConverterBinding(getSource(), typeMatcher, converter));
+      elements.add(new TypeConverterBinding(getElementSource(), typeMatcher, converter));
     }
 
-    public RecordingBinder withSource(final Object source) {
-      return new RecordingBinder(this, source, null);
+    public RecordingBinder withSource(final Object source) {            
+      return source == this.source ? this : new RecordingBinder(this, source, null);
     }
 
     public RecordingBinder skipSources(Class... classesToSkip) {
@@ -302,17 +339,27 @@ public final class Elements {
     }
 
     public PrivateBinder newPrivateBinder() {
-      PrivateElementsImpl privateElements = new PrivateElementsImpl(getSource());
+      PrivateElementsImpl privateElements = new PrivateElementsImpl(getElementSource());
+      RecordingBinder binder = new RecordingBinder(this, privateElements);
       elements.add(privateElements);
-      return new RecordingBinder(this, privateElements);
+      rehashables.add(binder);
+      return binder;
     }
     
     public void disableCircularProxies() {
-      elements.add(new DisableCircularProxiesOption(getSource()));
+      elements.add(new DisableCircularProxiesOption(getElementSource()));
     }
     
     public void requireExplicitBindings() {
-      elements.add(new RequireExplicitBindingsOption(getSource()));     
+      elements.add(new RequireExplicitBindingsOption(getElementSource()));     
+    }
+    
+    public void requireAtInjectOnConstructors() {
+      elements.add(new RequireAtInjectOnConstructorsOption(getElementSource()));
+    }
+
+    public void requireExactBindingAnnotations() {
+      elements.add(new RequireExactBindingAnnotationsOption(getElementSource()));
     }
 
     public void expose(Key<?> key) {
@@ -337,15 +384,84 @@ public final class Elements {
         };
       }
 
-      ExposureBuilder<T> builder = new ExposureBuilder<T>(this, getSource(), key);
+      ExposureBuilder<T> builder = new ExposureBuilder<T>(this, getElementSource(), key);
       privateElements.addExposureBuilder(builder);
       return builder;
     }
 
-    protected Object getSource() {
-      return sourceProvider != null
-          ? sourceProvider.get()
-          : source;
+    private ModuleSource getModuleSource(Module module) {
+      StackTraceElement[] partialCallStack;
+      if (getIncludeStackTraceOption() == IncludeStackTraceOption.COMPLETE) {
+        partialCallStack = getPartialCallStack(new Throwable().getStackTrace());
+      } else {
+        partialCallStack = new StackTraceElement[0];
+      }
+      if (moduleSource == null) {
+        return new ModuleSource(module, partialCallStack);
+      }
+      return moduleSource.createChild(module, partialCallStack);
+    }
+
+    private ElementSource getElementSource() {
+      // Full call stack
+      StackTraceElement[] callStack = null;
+      // The call stack starts from current top module configure and ends at this method caller
+      StackTraceElement[] partialCallStack = new StackTraceElement[0];
+      // The element original source
+      ElementSource originalSource = null;
+      // The element declaring source
+      Object declaringSource = source;
+      if (declaringSource instanceof ElementSource) {
+        originalSource = (ElementSource) declaringSource;
+        declaringSource = originalSource.getDeclaringSource();
+      }
+      IncludeStackTraceOption stackTraceOption = getIncludeStackTraceOption();
+      if (stackTraceOption == IncludeStackTraceOption.COMPLETE ||
+          (stackTraceOption == IncludeStackTraceOption.ONLY_FOR_DECLARING_SOURCE 
+          && declaringSource == null)) {
+        callStack = new Throwable().getStackTrace();
+      }
+      if (stackTraceOption == IncludeStackTraceOption.COMPLETE) {
+        partialCallStack = getPartialCallStack(callStack);
+      }
+      if (declaringSource == null) {
+        // So 'source' and 'originalSource' are null otherwise declaringSource has some value
+        if (stackTraceOption == IncludeStackTraceOption.COMPLETE ||
+            stackTraceOption == IncludeStackTraceOption.ONLY_FOR_DECLARING_SOURCE) {
+          // With the above conditions and assignments 'callStack' is non-null
+          declaringSource = sourceProvider.get(callStack);
+        } else { // or if (stackTraceOption == IncludeStackTraceOptions.OFF)
+          // As neither 'declaring source' nor 'call stack' is available use 'module source'
+          declaringSource = sourceProvider.getFromClassNames(moduleSource.getModuleClassNames());
+        }
+      }
+      // Build the binding call stack
+      return new ElementSource(
+          originalSource, declaringSource, moduleSource, partialCallStack);
+    }
+
+    /**
+     * Removes the {@link #moduleSource} call stack from the beginning of current call stack. It  
+     * also removes the last two elements in order to make {@link #install(Module)} the last call 
+     * in the call stack.  
+     */
+    private StackTraceElement[] getPartialCallStack(StackTraceElement[] callStack) {
+      int toSkip = 0;
+      if (moduleSource != null) {
+        toSkip = moduleSource.getStackTraceSize();
+      }
+      // -1 for skipping 'getModuleSource' and 'getElementSource' calls
+      int chunkSize = callStack.length - toSkip - 1;
+
+      StackTraceElement[] partialCallStack = new StackTraceElement[chunkSize];
+      System.arraycopy(callStack, 1, partialCallStack, 0, chunkSize);
+      return partialCallStack;
+    }
+    
+    @Override public void rehashKeys() {
+      for (RehashableKeys rehashable : rehashables) {
+        rehashable.rehashKeys();
+      }
     }
 
     @Override public String toString() {
diff --git a/core/src/com/google/inject/spi/InjectionPoint.java b/core/src/com/google/inject/spi/InjectionPoint.java
index 891c42d..36c10be 100644
--- a/core/src/com/google/inject/spi/InjectionPoint.java
+++ b/core/src/com/google/inject/spi/InjectionPoint.java
@@ -444,6 +444,7 @@ public final class InjectionPoint {
       this.field = field;
     }
 
+    @Override
     InjectionPoint toInjectionPoint() {
       return new InjectionPoint(declaringType, field, optional);
     }
@@ -463,6 +464,7 @@ public final class InjectionPoint {
       this.method = method;
     }
 
+    @Override
     InjectionPoint toInjectionPoint() {
       return new InjectionPoint(declaringType, method, optional);
     }
@@ -676,7 +678,7 @@ public final class InjectionPoint {
             InjectableMethod injectableMethod = new InjectableMethod(
                 current, method, atInject);
             if (checkForMisplacedBindingAnnotations(method, errors)
-                | !isValidMethod(injectableMethod, errors)) {
+                || !isValidMethod(injectableMethod, errors)) {
               if (overrideIndex != null) {
                 boolean removed = overrideIndex.removeIfOverriddenBy(method, false, injectableMethod);
                 if(removed) {
diff --git a/core/src/com/google/inject/spi/Message.java b/core/src/com/google/inject/spi/Message.java
index 14d3bcc..59c4072 100644
--- a/core/src/com/google/inject/spi/Message.java
+++ b/core/src/com/google/inject/spi/Message.java
@@ -55,6 +55,13 @@ public final class Message implements Serializable, Element {
     this.cause = cause;
   }
 
+  /**
+   * @since 4.0
+   */
+  public Message(String message, Throwable cause) {
+    this(ImmutableList.of(), message, cause);
+  }
+
   public Message(Object source, String message) {
     this(ImmutableList.of(source), message, null);
   }
diff --git a/core/src/com/google/inject/spi/ModuleSource.java b/core/src/com/google/inject/spi/ModuleSource.java
new file mode 100644
index 0000000..4e2a549
--- /dev/null
+++ b/core/src/com/google/inject/spi/ModuleSource.java
@@ -0,0 +1,166 @@
+package com.google.inject.spi;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.google.inject.Module;
+import com.google.inject.internal.util.StackTraceElements;
+import com.google.inject.internal.util.StackTraceElements.InMemoryStackTraceElement;
+
+import java.util.List;
+
+/**
+ * Associated to a {@link Module module}, provides the module class name, the parent module {@link
+ * ModuleSource source}, and the call stack that ends just before the module {@link
+ * Module#configure(Binder) configure(Binder)} method invocation.  
+ */
+final class ModuleSource {
+
+  /**
+   * The class name of module that this {@link ModuleSource} associated to.
+   */
+  private final String moduleClassName;
+  
+  /**
+   * The parent {@link ModuleSource module source}.
+   */
+  private final ModuleSource parent;
+  
+  /** 
+   * The chunk of call stack that starts from the parent module {@link Module#configure(Binder) 
+   * configure(Binder)} call and ends just before the module {@link Module#configure(Binder) 
+   * configure(Binder)} method invocation. For a module without a parent module the chunk starts 
+   * from the bottom of call stack. The array is non-empty if stack trace collection is on.
+   */
+  private final InMemoryStackTraceElement[] partialCallStack;
+
+  /**
+   * Creates a new {@link ModuleSource} with a {@literal null} parent.
+   * @param module the corresponding module
+   * @param partialCallStack the chunk of call stack that starts from the parent module {@link 
+   * Module#configure(Binder) configure(Binder)} call and ends just before the module {@link 
+   * Module#configure(Binder) configure(Binder)} method invocation
+   */
+  ModuleSource(Module module, StackTraceElement[] partialCallStack) {
+    this(null, module, partialCallStack);
+  } 
+  
+ /**
+   * Creates a new {@link ModuleSource} Object.
+   * @param parent the parent module {@link ModuleSource source} 
+   * @param module the corresponding module
+   * @param partialCallStack the chunk of call stack that starts from the parent module {@link 
+   * Module#configure(Binder) configure(Binder)} call and ends just before the module {@link 
+   * Module#configure(Binder) configure(Binder)} method invocation
+   */
+  private ModuleSource(
+      /* @Nullable */ ModuleSource parent, Module module, StackTraceElement[] partialCallStack) {
+    Preconditions.checkNotNull(module, "module cannot be null.");
+    Preconditions.checkNotNull(partialCallStack, "partialCallStack cannot be null.");
+    this.parent = parent;
+    this.moduleClassName = module.getClass().getName();
+    this.partialCallStack = StackTraceElements.convertToInMemoryStackTraceElement(partialCallStack);
+  }
+  
+  /** 
+   * Returns the corresponding module class name.
+   *
+   * @see Class#getName()
+   */
+  String getModuleClassName() {
+    return moduleClassName;
+  }
+
+  /**
+   * Returns the chunk of call stack that starts from the parent module {@link
+   * Module#configure(Binder) configure(Binder)} call and ends just before the module {@link
+   * Module#configure(Binder) configure(Binder)} method invocation. The return array is non-empty
+   * only if stack trace collection is on.
+   */
+  StackTraceElement[] getPartialCallStack() {
+    return StackTraceElements.convertToStackTraceElement(partialCallStack);
+  }
+  
+  /**
+   * Returns the size of partial call stack if stack trace collection is on otherwise zero.
+   */
+  int getPartialCallStackSize() {
+    return partialCallStack.length;
+  }
+  
+  /** 
+   * Creates and returns a child {@link ModuleSource} corresponding to the {@link Module module}.
+   * @param module the corresponding module
+   * @param partialCallStack the chunk of call stack that starts from the parent module {@link 
+   * Module#configure(Binder) configure(Binder)} call and ends just before the module {@link 
+   * Module#configure(Binder) configure(Binder)} method invocation
+   */
+  ModuleSource createChild(Module module, StackTraceElement[] partialCallStack) {
+    return new ModuleSource(this, module, partialCallStack);
+  }
+
+  /** 
+   * Returns the parent module {@link ModuleSource source}. 
+   */
+  ModuleSource getParent() {
+    return parent;
+  }
+
+  /**
+   * Returns the class names of modules in this module source. The first element (index 0) is filled
+   * by this object {@link #getModuleClassName()}. The second element is filled by the parent's 
+   * {@link #getModuleClassName()} and so on.
+   */
+  List<String> getModuleClassNames() {
+    ImmutableList.Builder<String> classNames = ImmutableList.builder();
+    ModuleSource current = this;
+    while (current != null) {
+      String className = current.moduleClassName;
+      classNames.add(className);
+      current = current.parent;
+    }
+    return classNames.build();
+  }
+
+  /**
+   * Returns the size of {@link ModuleSource ModuleSources} chain (all parents) that ends at this 
+   * object.
+   */
+  int size() {
+    if (parent == null) {
+      return 1;
+    }
+    return parent.size() + 1;
+  }
+  
+  /**
+   * Returns the size of call stack that ends just before the module {@link Module#configure(Binder)
+   * configure(Binder)} method invocation (see {@link #getStackTrace()}).
+   */
+  int getStackTraceSize() {
+    if (parent == null) {
+      return partialCallStack.length;
+    }
+    return parent.getStackTraceSize() + partialCallStack.length;
+  }
+
+  /**
+   * Returns the full call stack that ends just before the module {@link Module#configure(Binder)
+   * configure(Binder)} method invocation. The return array is non-empty if stack trace collection
+   * on.
+   */
+  StackTraceElement[] getStackTrace() {
+    int stackTraceSize = getStackTraceSize();
+    StackTraceElement[] callStack = new StackTraceElement[stackTraceSize];
+    int cursor = 0;
+    ModuleSource current = this;
+    while (current != null) {
+      StackTraceElement[] chunk = 
+          StackTraceElements.convertToStackTraceElement(current.partialCallStack);
+      int chunkSize = chunk.length;
+      System.arraycopy(chunk, 0, callStack, cursor, chunkSize);
+      current = current.parent;
+      cursor = cursor + chunkSize;
+    }
+    return callStack;
+  }
+}
diff --git a/core/src/com/google/inject/spi/ProviderLookup.java b/core/src/com/google/inject/spi/ProviderLookup.java
index b9ff7a1..ee4337d 100644
--- a/core/src/com/google/inject/spi/ProviderLookup.java
+++ b/core/src/com/google/inject/spi/ProviderLookup.java
@@ -18,10 +18,13 @@ package com.google.inject.spi;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.base.Preconditions.checkState;
+import static com.google.inject.internal.RehashableKeys.Keys.needsRehashing;
+import static com.google.inject.internal.RehashableKeys.Keys.rehash;
 
 import com.google.inject.Binder;
 import com.google.inject.Key;
 import com.google.inject.Provider;
+import com.google.inject.internal.RehashableKeys;
 
 /**
  * A lookup of the provider for a type. Lookups are created explicitly in a module using
@@ -35,7 +38,7 @@ import com.google.inject.Provider;
  */
 public final class ProviderLookup<T> implements Element {
   private final Object source;
-  private final Key<T> key;
+  private Key<T> key;  // effectively final, as it will not change once it escapes into user code
   private Provider<T> delegate;
 
   public ProviderLookup(Object source, Key<T> key) {
@@ -95,4 +98,14 @@ public final class ProviderLookup<T> implements Element {
       }
     };
   }
+
+  RehashableKeys getKeyRehasher() {
+    return new RehashableKeys() {
+      @Override public void rehashKeys() {
+        if (needsRehashing(key)) {
+          key = rehash(key);
+        }
+      }
+    };
+  }
 }
diff --git a/core/src/com/google/inject/spi/ProvisionListener.java b/core/src/com/google/inject/spi/ProvisionListener.java
index 812f4e1..e3f06bd 100644
--- a/core/src/com/google/inject/spi/ProvisionListener.java
+++ b/core/src/com/google/inject/spi/ProvisionListener.java
@@ -16,7 +16,7 @@
 
 package com.google.inject.spi;
 
-import com.google.inject.Key;
+import com.google.inject.Binding;
 import com.google.inject.Provider;
 import com.google.inject.Scope;
 
@@ -35,7 +35,9 @@ public interface ProvisionListener {
    * Invoked by Guice when an object requires provisioning. Provisioning occurs
    * when Guice locates and injects the dependencies for a binding. For types
    * bound to a Provider, provisioning encapsulates the {@link Provider#get}
-   * method. For other types, provisioning encapsulates the construction of the
+   * method. For toInstance or constant bindings, provisioning encapsulates
+   * the injecting of {@literal @}{@code Inject}ed fields or methods.
+   * For other types, provisioning encapsulates the construction of the
    * object. If a type is bound within a {@link Scope}, provisioning depends on
    * the scope. Types bound in Singleton scope will only be provisioned once.
    * Types bound in no scope will be provisioned every time they are injected.
@@ -50,13 +52,19 @@ public interface ProvisionListener {
   /** Encapsulates a single act of provisioning.*/ 
   public abstract static class ProvisionInvocation<T> {
 
-    /** Returns the Key which will be provisioned. */
-    public abstract Key<T> getKey();
+    /**
+     * Returns the Binding this is provisioning.
+     * <p>
+     * You must not call {@link Provider#get()} on the provider returned by
+     * {@link Binding#getProvider}, otherwise you will get confusing error messages.
+     */
+    public abstract Binding<T> getBinding();
 
     /** Performs the provision, returning the object provisioned. */
     public abstract T provision();
     
     /** Returns the dependency chain that led to this object being provisioned. */
     public abstract List<DependencyAndSource> getDependencyChain();
+    
   }
 }
diff --git a/core/src/com/google/inject/spi/ProvisionListenerBinding.java b/core/src/com/google/inject/spi/ProvisionListenerBinding.java
index 479e9f5..4f349d4 100644
--- a/core/src/com/google/inject/spi/ProvisionListenerBinding.java
+++ b/core/src/com/google/inject/spi/ProvisionListenerBinding.java
@@ -18,14 +18,14 @@ package com.google.inject.spi;
 
 import com.google.common.collect.ImmutableList;
 import com.google.inject.Binder;
-import com.google.inject.Key;
+import com.google.inject.Binding;
 import com.google.inject.matcher.Matcher;
 
 import java.util.List;
 
 /**
  * Binds keys (picked using a Matcher) to a provision listener. Listeners are created explicitly in
- * a module using {@link Binder#bindListener(Matcher, ProvisionListener)} statements:
+ * a module using {@link Binder#bindListener(Matcher, ProvisionListener...)} statements:
  *
  * @author sameb at google.com (Sam Berlin)
  * @since 4.0
@@ -33,14 +33,14 @@ import java.util.List;
 public final class ProvisionListenerBinding implements Element {
 
   private final Object source;
-  private final Matcher<? super Key<?>> keyMatcher;
+  private final Matcher<? super Binding<?>> bindingMatcher;
   private final List<ProvisionListener> listeners;
 
   ProvisionListenerBinding(Object source,
-      Matcher<? super Key<?>> typeMatcher,
+      Matcher<? super Binding<?>> bindingMatcher,
       ProvisionListener[] listeners) {
     this.source = source;
-    this.keyMatcher = typeMatcher;
+    this.bindingMatcher = bindingMatcher;
     this.listeners = ImmutableList.copyOf(listeners);
   }
 
@@ -49,9 +49,11 @@ public final class ProvisionListenerBinding implements Element {
     return listeners;
   }
 
-  /** Returns the key matcher which chooses which keys the listener should be notified of. */
-  public Matcher<? super Key<?>> getKeyMatcher() {
-    return keyMatcher;
+  /**
+   * Returns the binding matcher which chooses which bindings the listener should be notified of.
+   */  
+  public Matcher<? super Binding<?>> getBindingMatcher() {
+    return bindingMatcher;
   }
 
   public Object getSource() {
@@ -63,7 +65,7 @@ public final class ProvisionListenerBinding implements Element {
   }
 
   public void applyTo(Binder binder) {
-    binder.withSource(getSource()).bindListener(keyMatcher,
+    binder.withSource(getSource()).bindListener(bindingMatcher,
         listeners.toArray(new ProvisionListener[listeners.size()]));
   }
 }
diff --git a/core/src/com/google/inject/spi/RequireAtInjectOnConstructorsOption.java b/core/src/com/google/inject/spi/RequireAtInjectOnConstructorsOption.java
new file mode 100644
index 0000000..03d8c34
--- /dev/null
+++ b/core/src/com/google/inject/spi/RequireAtInjectOnConstructorsOption.java
@@ -0,0 +1,48 @@
+/**
+ * Copyright (C) 2012 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 com.google.inject.Binder;
+import com.google.inject.Inject;
+
+/**
+ * A request to require explicit {@literal @}{@link Inject} annotations on constructors.
+ *
+ * @author sameb at google.com (Sam Berlin)
+ * @since 4.0
+ */
+public final class RequireAtInjectOnConstructorsOption implements Element {
+  private final Object source;
+
+  RequireAtInjectOnConstructorsOption(Object source) {
+    this.source = checkNotNull(source, "source");
+  }
+
+  public Object getSource() {
+    return source;
+  }
+
+  public void applyTo(Binder binder) {
+    binder.withSource(getSource()).requireAtInjectOnConstructors();
+  }
+
+  public <T> T acceptVisitor(ElementVisitor<T> visitor) {
+    return visitor.visit(this);
+  }
+}
diff --git a/core/src/com/google/inject/spi/RequireExactBindingAnnotationsOption.java b/core/src/com/google/inject/spi/RequireExactBindingAnnotationsOption.java
new file mode 100644
index 0000000..ee52cfc
--- /dev/null
+++ b/core/src/com/google/inject/spi/RequireExactBindingAnnotationsOption.java
@@ -0,0 +1,47 @@
+/**
+ * Copyright (C) 2010 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 com.google.inject.Binder;
+
+/**
+ * A request to require exact binding annotations.
+ *
+ * @author sameb at google.com (Sam Berlin)
+ * @since 4.0
+ */
+public final class RequireExactBindingAnnotationsOption implements Element {
+  private final Object source;
+
+  RequireExactBindingAnnotationsOption(Object source) {
+    this.source = checkNotNull(source, "source");
+  }
+
+  public Object getSource() {
+    return source;
+  }
+
+  public void applyTo(Binder binder) {
+    binder.withSource(getSource()).requireExactBindingAnnotations();
+  }
+
+  public <T> T acceptVisitor(ElementVisitor<T> visitor) {
+    return visitor.visit(this);
+  }
+}
diff --git a/core/src/com/google/inject/spi/Toolable.java b/core/src/com/google/inject/spi/Toolable.java
index 37ed41f..ffabd9d 100644
--- a/core/src/com/google/inject/spi/Toolable.java
+++ b/core/src/com/google/inject/spi/Toolable.java
@@ -1,3 +1,19 @@
+/**
+ * Copyright (C) 2010 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 java.lang.annotation.ElementType.METHOD;
diff --git a/core/src/com/google/inject/spi/TypeConverterBinding.java b/core/src/com/google/inject/spi/TypeConverterBinding.java
index f4925a5..4478627 100644
--- a/core/src/com/google/inject/spi/TypeConverterBinding.java
+++ b/core/src/com/google/inject/spi/TypeConverterBinding.java
@@ -20,6 +20,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
 
 import com.google.inject.Binder;
 import com.google.inject.TypeLiteral;
+import com.google.inject.internal.Errors;
 import com.google.inject.matcher.Matcher;
 
 /**
@@ -67,6 +68,6 @@ public final class TypeConverterBinding implements Element {
 
   @Override public String toString() {
     return typeConverter + " which matches " + typeMatcher
-        + " (bound at " + source + ")";
+        + " (bound at " + Errors.convert(source) + ")";
   }
 }
diff --git a/core/src/com/google/inject/util/Modules.java b/core/src/com/google/inject/util/Modules.java
index 27eed97..c166b8e 100644
--- a/core/src/com/google/inject/util/Modules.java
+++ b/core/src/com/google/inject/util/Modules.java
@@ -29,6 +29,7 @@ import com.google.inject.Module;
 import com.google.inject.PrivateBinder;
 import com.google.inject.PrivateModule;
 import com.google.inject.Scope;
+import com.google.inject.internal.Errors;
 import com.google.inject.spi.DefaultBindingScopingVisitor;
 import com.google.inject.spi.DefaultElementVisitor;
 import com.google.inject.spi.Element;
@@ -51,10 +52,11 @@ import java.util.Set;
  */
 public final class Modules {
   private Modules() {}
-
-  public static final Module EMPTY_MODULE = new Module() {
+  
+  public static final Module EMPTY_MODULE = new EmptyModule();
+  private static class EmptyModule implements Module {
     public void configure(Binder binder) {}
-  };
+  }
 
   /**
    * Returns a builder that creates a module that overlays override modules over the given
@@ -107,15 +109,22 @@ public final class Modules {
    * Returns a new module that installs all of {@code modules}.
    */
   public static Module combine(Iterable<? extends Module> modules) {
-    final Set<Module> modulesSet = ImmutableSet.copyOf(modules);
-    return new Module() {
-      public void configure(Binder binder) {
-        binder = binder.skipSources(getClass());
-        for (Module module : modulesSet) {
-          binder.install(module);
-        }
+    return new CombinedModule(modules);
+  }
+  
+  private static class CombinedModule implements Module {
+    final Set<Module> modulesSet;
+    
+    CombinedModule(Iterable<? extends Module> modules) {
+      this.modulesSet = ImmutableSet.copyOf(modules);
+    }
+    
+    public void configure(Binder binder) {
+      binder = binder.skipSources(getClass());
+      for (Module module : modulesSet) {
+        binder.install(module);
       }
-    };
+    }
   }
 
   /**
@@ -145,141 +154,162 @@ public final class Modules {
       return with(Arrays.asList(overrides));
     }
 
-    public Module with(final Iterable<? extends Module> overrides) {
-      return new AbstractModule() {
-        @Override
-        public void configure() {
-          Binder baseBinder = binder();
-          List<Element> baseElements = Elements.getElements(baseModules);
-
-          // If the sole element was a PrivateElements, we want to override
-          // the private elements within that -- so refocus our elements
-          // and binder.
-          if(baseElements.size() == 1) {
-            Element element = Iterables.getOnlyElement(baseElements);
-            if(element instanceof PrivateElements) {
-              PrivateElements privateElements = (PrivateElements)element;
-              PrivateBinder privateBinder = baseBinder.newPrivateBinder().withSource(privateElements.getSource());
-              for(Key exposed : privateElements.getExposedKeys()) {
-                privateBinder.withSource(privateElements.getExposedSource(exposed)).expose(exposed);
-              }
-              baseBinder = privateBinder;
-              baseElements = privateElements.getElements();
-            }
+    public Module with(Iterable<? extends Module> overrides) {
+      return new OverrideModule(overrides, baseModules);
+    }
+  }
+  
+  static class OverrideModule extends AbstractModule {
+    private final ImmutableSet<Module> overrides;
+    private final ImmutableSet<Module> baseModules;
+    
+    OverrideModule(Iterable<? extends Module> overrides, ImmutableSet<Module> baseModules) {
+      this.overrides = ImmutableSet.copyOf(overrides);
+      this.baseModules = baseModules;
+    }
+
+    @Override
+    public void configure() {
+      Binder baseBinder = binder();
+      List<Element> baseElements = Elements.getElements(currentStage(), baseModules);
+
+      // If the sole element was a PrivateElements, we want to override
+      // the private elements within that -- so refocus our elements
+      // and binder.
+      if(baseElements.size() == 1) {
+        Element element = Iterables.getOnlyElement(baseElements);
+        if(element instanceof PrivateElements) {
+          PrivateElements privateElements = (PrivateElements)element;
+          PrivateBinder privateBinder = baseBinder.newPrivateBinder().withSource(privateElements.getSource());
+          for(Key exposed : privateElements.getExposedKeys()) {
+            privateBinder.withSource(privateElements.getExposedSource(exposed)).expose(exposed);
           }
-          
-          final Binder binder = baseBinder;
-          final LinkedHashSet<Element> elements = new LinkedHashSet<Element>(baseElements);
-          final List<Element> overrideElements = Elements.getElements(overrides);
-
-          final Set<Key<?>> overriddenKeys = Sets.newHashSet();
-          final Set<Class<? extends Annotation>> overridesScopeAnnotations = Sets.newHashSet();
-
-          // execute the overrides module, keeping track of which keys and scopes are bound
-          new ModuleWriter(binder) {
-            @Override public <T> Void visit(Binding<T> binding) {
-              overriddenKeys.add(binding.getKey());
-              return super.visit(binding);
-            }
+          baseBinder = privateBinder;
+          baseElements = privateElements.getElements();
+        }
+      }
+      
+      final Binder binder = baseBinder.skipSources(this.getClass());
+      final LinkedHashSet<Element> elements = new LinkedHashSet<Element>(baseElements);
+      final List<Element> overrideElements = Elements.getElements(currentStage(), overrides);
+
+      final Set<Key<?>> overriddenKeys = Sets.newHashSet();
+      final Map<Class<? extends Annotation>, ScopeBinding> overridesScopeAnnotations =
+          Maps.newHashMap();
+
+      // execute the overrides module, keeping track of which keys and scopes are bound
+      new ModuleWriter(binder) {
+        @Override public <T> Void visit(Binding<T> binding) {
+          overriddenKeys.add(binding.getKey());
+          return super.visit(binding);
+        }
 
-            @Override public Void visit(ScopeBinding scopeBinding) {
-              overridesScopeAnnotations.add(scopeBinding.getAnnotationType());
-              return super.visit(scopeBinding);
-            }
+        @Override public Void visit(ScopeBinding scopeBinding) {
+          overridesScopeAnnotations.put(scopeBinding.getAnnotationType(), scopeBinding);
+          return super.visit(scopeBinding);
+        }
 
-            @Override public Void visit(PrivateElements privateElements) {
-              overriddenKeys.addAll(privateElements.getExposedKeys());
-              return super.visit(privateElements);
-            }
-          }.writeAll(overrideElements);
-
-          // execute the original module, skipping all scopes and overridden keys. We only skip each
-          // overridden binding once so things still blow up if the module binds the same thing
-          // multiple times.
-          final Map<Scope, Object> scopeInstancesInUse = Maps.newHashMap();
-          final List<ScopeBinding> scopeBindings = Lists.newArrayList();
-          new ModuleWriter(binder) {
-            @Override public <T> Void visit(Binding<T> binding) {
-              if (!overriddenKeys.remove(binding.getKey())) {
-                super.visit(binding);
-
-                // Record when a scope instance is used in a binding
-                Scope scope = getScopeInstanceOrNull(binding);
-                if (scope != null) {
-                  scopeInstancesInUse.put(scope, binding.getSource());
-                }
+        @Override public Void visit(PrivateElements privateElements) {
+          overriddenKeys.addAll(privateElements.getExposedKeys());
+          return super.visit(privateElements);
+        }
+      }.writeAll(overrideElements);
+
+      // execute the original module, skipping all scopes and overridden keys. We only skip each
+      // overridden binding once so things still blow up if the module binds the same thing
+      // multiple times.
+      final Map<Scope, List<Object>> scopeInstancesInUse = Maps.newHashMap();
+      final List<ScopeBinding> scopeBindings = Lists.newArrayList();
+      new ModuleWriter(binder) {
+        @Override public <T> Void visit(Binding<T> binding) {
+          if (!overriddenKeys.remove(binding.getKey())) {
+            super.visit(binding);
+
+            // Record when a scope instance is used in a binding
+            Scope scope = getScopeInstanceOrNull(binding);
+            if (scope != null) {
+              List<Object> existing = scopeInstancesInUse.get(scope);
+              if (existing == null) {
+                existing = Lists.newArrayList();
+                scopeInstancesInUse.put(scope, existing);
               }
-
-              return null;
+              existing.add(binding.getSource());
             }
+          }
 
-            void rewrite(Binder binder, PrivateElements privateElements, Set<Key<?>> keysToSkip) {
-              PrivateBinder privateBinder = binder.withSource(privateElements.getSource())
-                  .newPrivateBinder();
+          return null;
+        }
 
-              Set<Key<?>> skippedExposes = Sets.newHashSet();
+        void rewrite(Binder binder, PrivateElements privateElements, Set<Key<?>> keysToSkip) {
+          PrivateBinder privateBinder = binder.withSource(privateElements.getSource())
+              .newPrivateBinder();
 
-              for (Key<?> key : privateElements.getExposedKeys()) {
-                if (keysToSkip.remove(key)) {
-                  skippedExposes.add(key);
-                } else {
-                  privateBinder.withSource(privateElements.getExposedSource(key)).expose(key);
-                }
-              }
+          Set<Key<?>> skippedExposes = Sets.newHashSet();
 
-              for (Element element : privateElements.getElements()) {
-                if (element instanceof Binding
-                    && skippedExposes.remove(((Binding) element).getKey())) {
-                  continue;
-                }
-                if (element instanceof PrivateElements) {
-                  rewrite(privateBinder, (PrivateElements) element, skippedExposes);
-                  continue;
-                }
-                element.applyTo(privateBinder);
-              }
+          for (Key<?> key : privateElements.getExposedKeys()) {
+            if (keysToSkip.remove(key)) {
+              skippedExposes.add(key);
+            } else {
+              privateBinder.withSource(privateElements.getExposedSource(key)).expose(key);
             }
+          }
 
-            @Override public Void visit(PrivateElements privateElements) {
-              rewrite(binder, privateElements, overriddenKeys);
-              return null;
+          for (Element element : privateElements.getElements()) {
+            if (element instanceof Binding
+                && skippedExposes.remove(((Binding) element).getKey())) {
+              continue;
             }
-
-            @Override public Void visit(ScopeBinding scopeBinding) {
-              scopeBindings.add(scopeBinding);
-              return null;
+            if (element instanceof PrivateElements) {
+              rewrite(privateBinder, (PrivateElements) element, skippedExposes);
+              continue;
             }
-          }.writeAll(elements);
-
-          // execute the scope bindings, skipping scopes that have been overridden. Any scope that
-          // is overridden and in active use will prompt an error
-          new ModuleWriter(binder) {
-            @Override public Void visit(ScopeBinding scopeBinding) {
-              if (!overridesScopeAnnotations.remove(scopeBinding.getAnnotationType())) {
-                super.visit(scopeBinding);
-              } else {
-                Object source = scopeInstancesInUse.get(scopeBinding.getScope());
-                if (source != null) {
-                  binder.withSource(source).addError(
-                      "The scope for @%s is bound directly and cannot be overridden.",
-                      scopeBinding.getAnnotationType().getSimpleName());
-                }
-              }
-              return null;
-            }
-          }.writeAll(scopeBindings);
+            element.applyTo(privateBinder);
+          }
+        }
 
-          // TODO: bind the overridden keys using multibinder
+        @Override public Void visit(PrivateElements privateElements) {
+          rewrite(binder, privateElements, overriddenKeys);
+          return null;
         }
 
-        private Scope getScopeInstanceOrNull(Binding<?> binding) {
-          return binding.acceptScopingVisitor(new DefaultBindingScopingVisitor<Scope>() {
-            public Scope visitScope(Scope scope) {
-              return scope;
+        @Override public Void visit(ScopeBinding scopeBinding) {
+          scopeBindings.add(scopeBinding);
+          return null;
+        }
+      }.writeAll(elements);
+
+      // execute the scope bindings, skipping scopes that have been overridden. Any scope that
+      // is overridden and in active use will prompt an error
+      new ModuleWriter(binder) {
+        @Override public Void visit(ScopeBinding scopeBinding) {
+          ScopeBinding overideBinding =
+              overridesScopeAnnotations.remove(scopeBinding.getAnnotationType());
+          if (overideBinding == null) {
+            super.visit(scopeBinding);
+          } else {
+            List<Object> usedSources = scopeInstancesInUse.get(scopeBinding.getScope());
+            if (usedSources != null) {
+              StringBuilder sb = new StringBuilder(
+                  "The scope for @%s is bound directly and cannot be overridden.");
+              sb.append("%n     original binding at " + Errors.convert(scopeBinding.getSource()));
+              for (Object usedSource : usedSources) {
+                sb.append("%n     bound directly at " + Errors.convert(usedSource) + "");
+              }
+              binder.withSource(overideBinding.getSource())
+                  .addError(sb.toString(), scopeBinding.getAnnotationType().getSimpleName());
             }
-          });
+          }
+          return null;
+        }
+      }.writeAll(scopeBindings);
+    }
+
+    private Scope getScopeInstanceOrNull(Binding<?> binding) {
+      return binding.acceptScopingVisitor(new DefaultBindingScopingVisitor<Scope>() {
+        @Override public Scope visitScope(Scope scope) {
+          return scope;
         }
-      };
+      });
     }
   }
 
@@ -287,7 +317,7 @@ public final class Modules {
     protected final Binder binder;
 
     ModuleWriter(Binder binder) {
-      this.binder = binder;
+      this.binder = binder.skipSources(this.getClass());
     }
 
     @Override protected Void visitOther(Element element) {
diff --git a/core/test/com/google/inject/AllTests.java b/core/test/com/google/inject/AllTests.java
index be5b875..54f4dea 100644
--- a/core/test/com/google/inject/AllTests.java
+++ b/core/test/com/google/inject/AllTests.java
@@ -18,6 +18,7 @@ package com.google.inject;
 
 import com.google.common.collect.ImmutableSet;
 import com.google.inject.internal.MoreTypesTest;
+import com.google.inject.internal.RehashableKeysTest;
 import com.google.inject.internal.UniqueAnnotationsTest;
 import com.google.inject.internal.util.LineNumbersTest;
 import com.google.inject.matcher.MatcherTest;
@@ -25,17 +26,21 @@ import com.google.inject.name.NamedEquivalanceTest;
 import com.google.inject.name.NamesTest;
 import com.google.inject.spi.BindingTargetVisitorTest;
 import com.google.inject.spi.ElementApplyToTest;
+import com.google.inject.spi.ElementSourceTest;
 import com.google.inject.spi.ElementsTest;
 import com.google.inject.spi.HasDependenciesTest;
 import com.google.inject.spi.InjectionPointTest;
 import com.google.inject.spi.InjectorSpiTest;
 import com.google.inject.spi.ModuleRewriterTest;
+import com.google.inject.spi.ModuleSourceTest;
 import com.google.inject.spi.ProviderMethodsTest;
 import com.google.inject.spi.SpiBindingsTest;
 import com.google.inject.spi.ToolStageInjectorTest;
 import com.google.inject.util.NoopOverrideTest;
+import com.google.inject.util.OverrideModuleTest;
 import com.google.inject.util.ProvidersTest;
 import com.google.inject.util.TypesTest;
+
 import com.googlecode.guice.GuiceTck;
 import com.googlecode.guice.Jsr330Test;
 
@@ -91,7 +96,9 @@ public class AllTests {
     suite.addTestSuite(ProvisionListenerTest.class);
     // ProxyFactoryTest is AOP-only
     suite.addTestSuite(ReflectionTest.class);
+    suite.addTestSuite(RehashableKeysTest.class);
     suite.addTestSuite(RequestInjectionTest.class);
+    suite.addTestSuite(RequireAtInjectOnConstructorsTest.class);
     suite.addTestSuite(ScopesTest.class);
     suite.addTestSuite(SerializationTest.class);
     suite.addTestSuite(SuperclassTest.class);
@@ -123,6 +130,8 @@ public class AllTests {
     suite.addTestSuite(ProviderMethodsTest.class);
     suite.addTestSuite(SpiBindingsTest.class);
     suite.addTestSuite(ToolStageInjectorTest.class);
+    suite.addTestSuite(ModuleSourceTest.class);
+    suite.addTestSuite(ElementSourceTest.class);
 
     // tools
     // suite.addTestSuite(JmxTest.class); not a testcase
diff --git a/core/test/com/google/inject/Asserts.java b/core/test/com/google/inject/Asserts.java
index 4827fb7..d46a1ff 100644
--- a/core/test/com/google/inject/Asserts.java
+++ b/core/test/com/google/inject/Asserts.java
@@ -17,10 +17,17 @@
 
 package com.google.inject;
 
+import static com.google.inject.internal.InternalFlags.IncludeStackTraceOption;
+import static com.google.inject.internal.InternalFlags.getIncludeStackTraceOption;
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertTrue;
 
+import com.google.common.base.Function;
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+
 import junit.framework.Assert;
 
 import java.io.ByteArrayInputStream;
@@ -37,6 +44,47 @@ public class Asserts {
   private Asserts() {}
 
   /**
+   * Returns the String that would appear in an error message for this chain of classes 
+   * as modules.
+   */
+  public static String asModuleChain(Class... classes) {
+    return Joiner.on(" -> ").appendTo(new StringBuilder(" (via modules: "),
+        Iterables.transform(ImmutableList.copyOf(classes), new Function<Class, String>() {
+          @Override
+          public String apply(Class input) {
+            return input.getName();
+          }
+        })).append(")").toString();
+  }
+
+  /**
+   * Returns the source file appears in error messages based on {@link 
+   * #getIncludeStackTraceOption()} value.
+   */
+  public static String getDeclaringSourcePart(Class clazz) {
+    if (getIncludeStackTraceOption() == IncludeStackTraceOption.OFF) {
+      return ".configure(Unknown Source";
+    }
+    return ".configure(" + clazz.getSimpleName() + ".java:";
+  }
+
+  /**
+   * Returns true if {@link #getIncludeStackTraceOption()} returns {@link
+   * IncludeStackTraceOption#OFF}.
+   */
+  public static boolean isIncludeStackTraceOff() {
+    return getIncludeStackTraceOption() == IncludeStackTraceOption.OFF;
+  }
+
+  /**
+   * Returns true if {@link #getIncludeStackTraceOption()} returns {@link
+   * IncludeStackTraceOption#COMPLETE}.
+   */
+  public static boolean isIncludeStackTraceComplete() {
+    return getIncludeStackTraceOption() == IncludeStackTraceOption.COMPLETE;
+  }
+
+  /**
    * Fails unless {@code expected.equals(actual)}, {@code
    * actual.equals(expected)} and their hash codes are equal. This is useful
    * for testing the equals method itself.
diff --git a/core/test/com/google/inject/BinderTest.java b/core/test/com/google/inject/BinderTest.java
index 193bbad..536d34e 100644
--- a/core/test/com/google/inject/BinderTest.java
+++ b/core/test/com/google/inject/BinderTest.java
@@ -16,10 +16,12 @@
 
 package com.google.inject;
 
+import static com.google.inject.Asserts.asModuleChain;
 import static com.google.inject.Asserts.assertContains;
 import static com.google.inject.Asserts.assertNotSerializable;
+import static com.google.inject.Asserts.getDeclaringSourcePart;
+import static com.google.inject.Asserts.isIncludeStackTraceOff;
 
-import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 import com.google.inject.name.Named;
@@ -98,16 +100,27 @@ public class BinderTest extends TestCase {
       });
     } catch (CreationException e) {
       assertEquals(4, e.getErrorMessages().size());
-      assertContains(e.getMessage(),
-          "1) No implementation for java.lang.Runnable was bound.",
-          "at " + getClass().getName(),
-          "2) No implementation for " + Comparator.class.getName() + " was bound.",
-          "at " + getClass().getName(),
-          "3) No implementation for java.util.concurrent.Callable<java.lang.String> was bound.",
-          "at " + getClass().getName(),
-          "4) No implementation for java.util.Date annotated with @"
-              + Named.class.getName() + "(value=date) was bound.",
-          "at " + getClass().getName());
+      String segment1 = "No implementation for " + Comparator.class.getName() + " was bound.";
+      String segment2 = "No implementation for java.util.Date annotated with @"
+          + Named.class.getName() + "(value=date) was bound.";
+      String segment3 = "No implementation for java.lang.Runnable was bound.";
+      String segment4 = " No implementation for java.util.concurrent.Callable<java.lang.String> was"
+          + " bound.";
+      String atSegment = "at " + getClass().getName();
+      String sourceFileName = getDeclaringSourcePart(getClass());
+      if (isIncludeStackTraceOff()) {
+        assertContains(e.getMessage(),
+            segment1, atSegment, sourceFileName,
+            segment2, atSegment, sourceFileName,
+            segment3, atSegment, sourceFileName,
+            segment4, atSegment, sourceFileName);
+      } else {
+        assertContains(e.getMessage(),
+            segment3, atSegment, sourceFileName,
+            segment1, atSegment, sourceFileName,
+            segment4, atSegment, sourceFileName,
+            segment2, atSegment, sourceFileName);
+      }
     }
   }
 
@@ -124,7 +137,7 @@ public class BinderTest extends TestCase {
       assertContains(e.getMessage(),
           "No implementation for java.lang.Runnable was bound.",
           "for field at " + NeedsRunnable.class.getName(), ".runnable(BinderTest.java:",
-          "at " + getClass().getName(), ".configure(BinderTest.java:");
+          "at " + getClass().getName(), getDeclaringSourcePart(getClass()));
     }
   }
 
@@ -158,7 +171,7 @@ public class BinderTest extends TestCase {
     } catch (CreationException expected) {
       assertContains(expected.getMessage(),
           "1) Binding points to itself.",
-          "at " + getClass().getName(), ".configure(BinderTest.java:");
+          "at " + getClass().getName(), getDeclaringSourcePart(getClass()));
     }
   }
 
@@ -234,7 +247,7 @@ public class BinderTest extends TestCase {
   public void testArrayTypeCanonicalization() {
     final String[] strings = new String[] { "A" };
     final Integer[] integers = new Integer[] { 1 };
-    
+
     Injector injector = Guice.createInjector(new AbstractModule() {
       @Override
       protected void configure() {
@@ -262,10 +275,10 @@ public class BinderTest extends TestCase {
     } catch (CreationException expected) {
       assertContains(expected.getMessage(),
           "1) A binding to java.lang.String[] was already configured at " + getClass().getName(),
-          "at " + getClass().getName(), ".configure(BinderTest.java:");
+          "at " + getClass().getName(), getDeclaringSourcePart(getClass()));
       assertContains(expected.getMessage(), "1 error");
     }
-    
+
     // passes because duplicates are ignored
     injector = Guice.createInjector(new AbstractModule() {
       @Override
@@ -278,28 +291,50 @@ public class BinderTest extends TestCase {
     assertSame(strings, injector.getInstance(new Key<String[]>() {}));
     assertSame(strings, injector.getInstance(String[].class));
   }
-  
+
+  static class ParentModule extends AbstractModule {
+    @Override protected void configure() {
+      install(new FooModule());
+      install(new BarModule());
+    }
+  }
+  static class FooModule extends AbstractModule {
+    @Override protected void configure() {
+      install(new ConstantModule("foo"));
+    }
+  }
+  static class BarModule extends AbstractModule {
+    @Override protected void configure() {
+      install(new ConstantModule("bar"));
+    }
+  }
+  static class ConstantModule extends AbstractModule {
+    private final String constant;
+    ConstantModule(String constant) {
+      this.constant = constant;
+    }
+    @Override protected void configure() {
+      bind(String.class).toInstance(constant);
+    }
+  }
+
   /**
    * Binding something to two different things should give an error.
    */
   public void testSettingBindingTwice() {
     try {
-      Guice.createInjector(new AbstractModule() {
-        @Override
-        protected void configure() {
-          bind(String.class).toInstance("foo");
-          bind(String.class).toInstance("bar");
-        }
-      });
+      Guice.createInjector(new ParentModule());
       fail();
     } catch(CreationException expected) {
       assertContains(expected.getMessage(),
-        "1) A binding to java.lang.String was already configured at " + getClass().getName(),
-        "at " + getClass().getName(), ".configure(BinderTest.java:");
+        "1) A binding to java.lang.String was already configured at " + ConstantModule.class.getName(),
+        asModuleChain(ParentModule.class, FooModule.class, ConstantModule.class),
+        "at " + ConstantModule.class.getName(), getDeclaringSourcePart(getClass()),
+        asModuleChain(ParentModule.class, BarModule.class, ConstantModule.class));
       assertContains(expected.getMessage(), "1 error");
     }
   }
-  
+
   /**
    * Binding an @ImplementedBy thing to something else should also fail.
    */
@@ -318,7 +353,7 @@ public class BinderTest extends TestCase {
       assertContains(expected.getMessage(),
         "1) A binding to " + HasImplementedBy1.class.getName()
         + " was already configured at " + getClass().getName(),
-        "at " + getClass().getName(), ".configure(BinderTest.java:");
+        "at " + getClass().getName(), getDeclaringSourcePart(getClass()));
       assertContains(expected.getMessage(), "1 error");
     }
   }
@@ -339,7 +374,7 @@ public class BinderTest extends TestCase {
     // Also validate that we're using the explicit (and not @ImplementedBy) implementation
     assertFalse(injector.getInstance(HasImplementedBy1.class) instanceof ImplementsHasImplementedBy1);
   }
-  
+
   /**
    * See issue 614, Problem Two
    * http://code.google.com/p/google-guice/issues/detail?id=614
@@ -353,7 +388,7 @@ public class BinderTest extends TestCase {
       }
     });
   }
-  
+
   /**
    * Untargetted bindings should follow @ImplementedBy and @ProvidedBy
    * annotations if they exist. Otherwise the class should be constructed
@@ -370,7 +405,7 @@ public class BinderTest extends TestCase {
         bind(JustAClass.class);
       }
     });
-    
+
     assertNotNull(injector.getInstance(HasProvidedBy1.class));
     assertNotNull(injector.getInstance(HasImplementedBy1.class));
     assertNotSame(HasProvidedBy2.class,
@@ -412,7 +447,7 @@ public class BinderTest extends TestCase {
       Guice.createInjector(new AbstractModule() {
         @Override
         protected void configure() {
-          addError(new Message(ImmutableList.of(), "Whoops!", new IllegalArgumentException()));
+          addError(new Message("Whoops!", new IllegalArgumentException()));
         }
       });
       fail();
@@ -436,43 +471,82 @@ public class BinderTest extends TestCase {
     } catch (CreationException expected) {
       assertContains(expected.getMessage(),
           "1) Binding to Provider is not allowed.",
-          "at " + BinderTest.class.getName(), "configure(BinderTest.java:");
+          "at " + BinderTest.class.getName(), getDeclaringSourcePart(getClass()));
     }
   }
 
-  public void testCannotBindToGuiceTypes() {
+  static class OuterCoreModule extends AbstractModule {
+    @Override protected void configure() {
+      install(new InnerCoreModule());
+    }
+  }
+  static class InnerCoreModule extends AbstractModule {
     final Named red = Names.named("red");
 
+    @Override protected void configure() {
+      bind(AbstractModule.class).annotatedWith(red)
+      .toProvider(Providers.<AbstractModule>of(null));
+      bind(Binder.class).annotatedWith(red).toProvider(Providers.<Binder>of(null));
+      bind(Binding.class).annotatedWith(red).toProvider(Providers.<Binding>of(null));
+      bind(Injector.class).annotatedWith(red).toProvider(Providers.<Injector>of(null));
+      bind(Key.class).annotatedWith(red).toProvider(Providers.<Key>of(null));
+      bind(Module.class).annotatedWith(red).toProvider(Providers.<Module>of(null));
+      bind(Provider.class).annotatedWith(red).toProvider(Providers.<Provider>of(null));
+      bind(Scope.class).annotatedWith(red).toProvider(Providers.<Scope>of(null));
+      bind(Stage.class).annotatedWith(red).toProvider(Providers.<Stage>of(null));
+      bind(TypeLiteral.class).annotatedWith(red).toProvider(Providers.<TypeLiteral>of(null));
+      bind(new TypeLiteral<Key<String>>() {}).toProvider(Providers.<Key<String>>of(null));
+    }
+  }
+  public void testCannotBindToGuiceTypes() {
     try {
-      Guice.createInjector(new AbstractModule() {
-        @Override
-        protected void configure() {
-          bind(AbstractModule.class).annotatedWith(red)
-              .toProvider(Providers.<AbstractModule>of(null));
-          bind(Binder.class).annotatedWith(red).toProvider(Providers.<Binder>of(null));
-          bind(Binding.class).annotatedWith(red).toProvider(Providers.<Binding>of(null));
-          bind(Injector.class).annotatedWith(red).toProvider(Providers.<Injector>of(null));
-          bind(Key.class).annotatedWith(red).toProvider(Providers.<Key>of(null));
-          bind(Module.class).annotatedWith(red).toProvider(Providers.<Module>of(null));
-          bind(Provider.class).annotatedWith(red).toProvider(Providers.<Provider>of(null));
-          bind(Scope.class).annotatedWith(red).toProvider(Providers.<Scope>of(null));
-          bind(TypeLiteral.class).annotatedWith(red).toProvider(Providers.<TypeLiteral>of(null));
-          bind(new TypeLiteral<Key<String>>() {}).toProvider(Providers.<Key<String>>of(null));
-        }
-      });
+      Guice.createInjector(new OuterCoreModule());
       fail();
     } catch (CreationException expected) {
       assertContains(expected.getMessage(),
           "Binding to core guice framework type is not allowed: AbstractModule.",
+          "at " + InnerCoreModule.class.getName() + getDeclaringSourcePart(getClass()),
+          asModuleChain(OuterCoreModule.class, InnerCoreModule.class),
+
           "Binding to core guice framework type is not allowed: Binder.",
+          "at " + InnerCoreModule.class.getName() + getDeclaringSourcePart(getClass()),
+          asModuleChain(OuterCoreModule.class, InnerCoreModule.class),
+
           "Binding to core guice framework type is not allowed: Binding.",
+          "at " + InnerCoreModule.class.getName() + getDeclaringSourcePart(getClass()),
+          asModuleChain(OuterCoreModule.class, InnerCoreModule.class),
+
           "Binding to core guice framework type is not allowed: Injector.",
+          "at " + InnerCoreModule.class.getName() + getDeclaringSourcePart(getClass()),
+          asModuleChain(OuterCoreModule.class, InnerCoreModule.class),
+
           "Binding to core guice framework type is not allowed: Key.",
+          "at " + InnerCoreModule.class.getName() + getDeclaringSourcePart(getClass()),
+          asModuleChain(OuterCoreModule.class, InnerCoreModule.class),
+
           "Binding to core guice framework type is not allowed: Module.",
+          "at " + InnerCoreModule.class.getName() + getDeclaringSourcePart(getClass()),
+          asModuleChain(OuterCoreModule.class, InnerCoreModule.class),
+
           "Binding to Provider is not allowed.",
+          "at " + InnerCoreModule.class.getName() + getDeclaringSourcePart(getClass()),
+          asModuleChain(OuterCoreModule.class, InnerCoreModule.class),
+
           "Binding to core guice framework type is not allowed: Scope.",
+          "at " + InnerCoreModule.class.getName() + getDeclaringSourcePart(getClass()),
+          asModuleChain(OuterCoreModule.class, InnerCoreModule.class),
+
+          "Binding to core guice framework type is not allowed: Stage.",
+          "at " + InnerCoreModule.class.getName() + getDeclaringSourcePart(getClass()),
+          asModuleChain(OuterCoreModule.class, InnerCoreModule.class),
+
           "Binding to core guice framework type is not allowed: TypeLiteral.",
-          "Binding to core guice framework type is not allowed: Key.");
+          "at " + InnerCoreModule.class.getName() + getDeclaringSourcePart(getClass()),
+          asModuleChain(OuterCoreModule.class, InnerCoreModule.class),
+
+          "Binding to core guice framework type is not allowed: Key.",
+          "at " + InnerCoreModule.class.getName() + getDeclaringSourcePart(getClass()),
+          asModuleChain(OuterCoreModule.class, InnerCoreModule.class));
     }
   }
 
@@ -513,27 +587,27 @@ public class BinderTest extends TestCase {
   static class ExtendsHasImplementedBy2 extends HasImplementedBy2 {}
 
   static class JustAClass {}
-  
+
   @ImplementedBy(ImplementsHasImplementedByThatNeedsAnotherImplementedBy.class)
   static interface HasImplementedByThatNeedsAnotherImplementedBy {
   }
-  
+
   static class ImplementsHasImplementedByThatNeedsAnotherImplementedBy
     implements HasImplementedByThatNeedsAnotherImplementedBy {
-    @Inject 
+    @Inject
     ImplementsHasImplementedByThatNeedsAnotherImplementedBy(
-        HasImplementedBy1 h1n1) {}                             
+        HasImplementedBy1 h1n1) {}
   }
-  
+
   @ImplementedBy(ImplementsHasImplementedByThatWantsExplicit.class)
-  static interface HasImplementedByThatWantsExplicit {    
+  static interface HasImplementedByThatWantsExplicit {
   }
-  
+
   static class ImplementsHasImplementedByThatWantsExplicit
       implements HasImplementedByThatWantsExplicit {
     @Inject ImplementsHasImplementedByThatWantsExplicit(JustAnInterface jai) {}
   }
-  
+
   static interface JustAnInterface {}
 
 
diff --git a/core/test/com/google/inject/BindingAnnotationTest.java b/core/test/com/google/inject/BindingAnnotationTest.java
index 0685701..b119953 100644
--- a/core/test/com/google/inject/BindingAnnotationTest.java
+++ b/core/test/com/google/inject/BindingAnnotationTest.java
@@ -17,6 +17,7 @@
 package com.google.inject;
 
 import static com.google.inject.Asserts.assertContains;
+import static com.google.inject.Asserts.getDeclaringSourcePart;
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
 import junit.framework.TestCase;
@@ -29,46 +30,121 @@ import java.lang.annotation.Retention;
  */
 public class BindingAnnotationTest extends TestCase {
 
-  public void testAnnotationWithValueMatchesKeyWithTypeOnly() throws
-      CreationException {
+  public void testAnnotationWithValueMatchesKeyWithTypeOnly() throws CreationException {
     Injector c = Guice.createInjector(new AbstractModule() {
+      @Override
       protected void configure() {
         bindConstant().annotatedWith(Blue.class).to("foo");
-        bind(Foo.class);
+        bind(BlueFoo.class);
       }
     });
 
-    Foo foo = c.getInstance(Foo.class);
+    BlueFoo foo = c.getInstance(BlueFoo.class);
 
     assertEquals("foo", foo.s);
   }
 
+  public void testRequireExactAnnotationsDisablesFallback() {
+    try {
+      Guice.createInjector(new AbstractModule() {
+        @Override
+        protected void configure() {
+          binder().requireExactBindingAnnotations();
+          bindConstant().annotatedWith(Blue.class).to("foo");
+          bind(BlueFoo.class);
+        }
+      });
+      fail();
+    } catch (CreationException expected) {
+      assertContains(expected.getMessage(), "No implementation for java.lang.String annotated with",
+          "BindingAnnotationTest$Blue(value=5) was bound",
+          "at " + BindingAnnotationTest.class.getName(),
+          getDeclaringSourcePart(getClass()));
+    }
+  }
+  
+  public void testRequireExactAnnotationsDoesntBreakIfDefaultsExist() {
+       Guice.createInjector(new AbstractModule() {
+        @Override
+        protected void configure() {
+          binder().requireExactBindingAnnotations();
+          bindConstant().annotatedWith(Red.class).to("foo");
+          bind(RedFoo.class);
+        }
+      }).getInstance(RedFoo.class);      
+  }
+
+  public void testRequireExactAnnotationsRequireAllOptionals() {
+    try {
+      Guice.createInjector(new AbstractModule() {
+        @Override
+        protected void configure() {
+          binder().requireExactBindingAnnotations();
+          bindConstant().annotatedWith(Color.class).to("foo");
+          bind(ColorFoo.class);
+        }
+      });
+      fail();
+    } catch (CreationException expected) {
+      assertContains(expected.getMessage(), "No implementation for java.lang.String annotated with",
+          "BindingAnnotationTest$Color",
+          "at " + BindingAnnotationTest.class.getName(),
+          getDeclaringSourcePart(getClass()));
+    }
+  }
+
   public void testAnnotationWithValueThatDoesntMatch() {
     try {
       Guice.createInjector(new AbstractModule() {
+        @Override
         protected void configure() {
           bindConstant().annotatedWith(createBlue(6)).to("six");
           bind(String.class).toInstance("bar");
-          bind(Foo.class);
+          bind(BlueFoo.class);
         }
       });
       fail();
     } catch (CreationException expected) {
       assertContains(expected.getMessage(), "No implementation for java.lang.String annotated with",
           "BindingAnnotationTest$Blue(value=5) was bound",
-          "at " + BindingAnnotationTest.class.getName(), ".configure(BindingAnnotationTest.java:");
+          "at " + BindingAnnotationTest.class.getName(),
+          getDeclaringSourcePart(getClass()));
     }
   }
 
-  static class Foo {
+  static class BlueFoo {
     @Inject @Blue(5) String s; 
   }
+  
+  static class RedFoo {
+    @Inject @Red String s;
+  }
+  
+  static class ColorFoo {
+    @Inject @Color(b=2) String s;
+  }
 
   @Retention(RUNTIME)
   @BindingAnnotation
   @interface Blue {
     int value();
   }
+  
+  @Retention(RUNTIME)
+  @BindingAnnotation
+  @interface Red {
+    int r() default 42;
+    int g() default 42;
+    int b() default 42;
+  }
+  
+  @Retention(RUNTIME)
+  @BindingAnnotation
+  @interface Color {
+    int r() default 0;
+    int g() default 0;
+    int b();
+  }
 
   public Blue createBlue(final int value) {
     return new Blue() {
diff --git a/core/test/com/google/inject/BindingTest.java b/core/test/com/google/inject/BindingTest.java
index 46359d5..df91c44 100644
--- a/core/test/com/google/inject/BindingTest.java
+++ b/core/test/com/google/inject/BindingTest.java
@@ -370,8 +370,7 @@ public class BindingTest extends TestCase {
       }
     });
     
-    assertEquals(ImmutableSet.of(TypeLiteral.get(Stage.class), TypeLiteral.get(D.class)),
-        heardTypes);
+    assertEquals(ImmutableSet.of(TypeLiteral.get(D.class)), heardTypes);
   }
 
   public void testInterfaceToImplementationConstructor() throws NoSuchMethodException {
diff --git a/core/test/com/google/inject/DuplicateBindingsTest.java b/core/test/com/google/inject/DuplicateBindingsTest.java
index eb9f503..27a2fd1 100644
--- a/core/test/com/google/inject/DuplicateBindingsTest.java
+++ b/core/test/com/google/inject/DuplicateBindingsTest.java
@@ -16,7 +16,7 @@
 
 package com.google.inject;
 
-import static com.google.inject.Asserts.assertContains;
+import static com.google.inject.Asserts.*;
 import static com.google.inject.name.Names.named;
 
 import com.google.common.base.Objects;
@@ -85,7 +85,8 @@ public class DuplicateBindingsTest extends TestCase {
       fail("should have failed");
     } catch(CreationException ce) {
       assertContains(ce.getMessage(),
-          "A binding to " + Foo.class.getName() + " was already configured at " + FailingProviderModule.class.getName(),
+          "A binding to " + Foo.class.getName() + " was already configured " +
+          "at " + FailingProviderModule.class.getName(),
           "at " + FailingProviderModule.class.getName()
           );
     }
@@ -123,7 +124,7 @@ public class DuplicateBindingsTest extends TestCase {
         new ScopedModule(Scopes.NO_SCOPE, foo, pFoo, pclFoo, clFoo, cFoo)
     );
   }
-  
+
   public void testMixedScopeFails() {
     try {
       Guice.createInjector(
@@ -132,20 +133,25 @@ public class DuplicateBindingsTest extends TestCase {
       );
       fail("expected exception");
     } catch(CreationException ce) {
-      assertContains(ce.getMessage(), 
-          "A binding to " + Foo.class.getName() + " annotated with " + named("pInstance") + " was already configured at " + SimpleModule.class.getName(),
-          "at " + ScopedModule.class.getName(), 
-          "A binding to " + Foo.class.getName() + " annotated with " + named("pKey") + " was already configured at " + SimpleModule.class.getName(),
-          "at " + ScopedModule.class.getName(), 
-          "A binding to " + Foo.class.getName() + " annotated with " + named("linkedKey") + " was already configured at " + SimpleModule.class.getName(),
-          "at " + ScopedModule.class.getName(), 
-          "A binding to " + FooImpl.class.getName() + " was already configured at " + SimpleModule.class.getName(),
-          "at " + ScopedModule.class.getName(), 
-          "A binding to " + Foo.class.getName() + " annotated with " + named("constructor") + " was already configured at " + SimpleModule.class.getName(),
-          "at " + ScopedModule.class.getName());
+      String segment1 = "A binding to " + Foo.class.getName() + " annotated with "
+          + named("pInstance") + " was already configured at " + SimpleModule.class.getName();
+      String segment2 = "A binding to " + Foo.class.getName() + " annotated with " + named("pKey")
+          + " was already configured at " + SimpleModule.class.getName();
+      String segment3 = "A binding to " + Foo.class.getName() + " annotated with " 
+          + named("constructor") + " was already configured at " + SimpleModule.class.getName();
+      String segment4 = "A binding to " + FooImpl.class.getName() + " was already configured at "
+          + SimpleModule.class.getName();
+      String atSegment = "at " + ScopedModule.class.getName();
+      if (isIncludeStackTraceOff()) {
+        assertContains(ce.getMessage(), segment1 , atSegment, segment2, atSegment, segment3,
+            atSegment, segment4, atSegment);
+      } else {
+        assertContains(ce.getMessage(), segment1 , atSegment, segment2, atSegment, segment4,
+            atSegment, segment3, atSegment);
+      }
     }
   }
-  
+
   @SuppressWarnings("unchecked")
   public void testMixedTargetsFails() {
     try {
diff --git a/core/test/com/google/inject/ErrorHandlingTest.java b/core/test/com/google/inject/ErrorHandlingTest.java
index 3a1a142..19161ca 100644
--- a/core/test/com/google/inject/ErrorHandlingTest.java
+++ b/core/test/com/google/inject/ErrorHandlingTest.java
@@ -100,7 +100,9 @@ public class ErrorHandlingTest {
     Invalid(String s) {}
   }
 
-  @Singleton @GoodScope
+  @SuppressWarnings("MoreThanOneScopeAnnotationOnClass") // suppress compiler error to test
+  @Singleton 
+  @GoodScope
   static class TooManyScopes {
   }
 
diff --git a/core/test/com/google/inject/KeyTest.java b/core/test/com/google/inject/KeyTest.java
index 74f7a94..c0401e0 100644
--- a/core/test/com/google/inject/KeyTest.java
+++ b/core/test/com/google/inject/KeyTest.java
@@ -106,7 +106,7 @@ public class KeyTest extends TestCase {
       assertEquals(wrappers[t], primitiveKey.getTypeLiteral().getType());
       assertEquals(wrappers[t], wrapperKey.getTypeLiteral().getType());
     }
-    
+
     Key<Integer> integerKey = Key.get(Integer.class);
     Key<Integer> integerKey2 = Key.get(Integer.class, Named.class);
     Key<Integer> integerKey3 = Key.get(Integer.class, Names.named("int"));
@@ -191,6 +191,35 @@ public class KeyTest extends TestCase {
     }
   }
 
+  public void testCannotGetKeyWithUnspecifiedTypeVariables() {
+    TypeLiteral<Integer> typeLiteral = KeyTest.createTypeLiteral();
+    try {
+      Key.get(typeLiteral);
+      fail("Guice should not allow keys for T");
+    } catch (ConfigurationException e) {
+      assertContains(e.getMessage(),
+          "T cannot be used as a key; It is not fully specified.");
+    }
+  }
+
+  private static <T> TypeLiteral<T> createTypeLiteral() {
+    return new TypeLiteral<T>() {};
+  }
+
+  public void testCannotCreateKeySubclassesWithUnspecifiedTypeVariables() {
+    try {
+      KeyTest.<Integer>createKey();
+      fail("Guice should not allow keys for T");
+    } catch (ConfigurationException e) {
+      assertContains(e.getMessage(),
+          "T cannot be used as a key; It is not fully specified.");
+    }
+  }
+
+  private static <T> Key<T> createKey() {
+    return new Key<T>() {};
+  }
+
   interface B {}
 
   @Retention(RUNTIME)
@@ -203,4 +232,47 @@ public class KeyTest extends TestCase {
   class HasTypeParameters<A, B extends List<A> & Runnable, C extends Runnable> {
     A a; B b; C c;
   }
+
+  public void testKeysWithDefaultAnnotations() {
+    AllDefaults allDefaults = HasAnnotations.class.getAnnotation(AllDefaults.class);
+    assertEquals(Key.get(Foo.class, allDefaults), Key.get(Foo.class, AllDefaults.class));
+
+    Marker marker = HasAnnotations.class.getAnnotation(Marker.class);
+    assertEquals(Key.get(Foo.class, marker), Key.get(Foo.class, Marker.class));
+
+    Key<?> noDefaults = Key.get(Foo.class, NoDefaults.class);
+    assertNull(noDefaults.getAnnotation());
+    assertEquals(NoDefaults.class, noDefaults.getAnnotationType());
+
+    Key<?> someDefaults = Key.get(Foo.class, SomeDefaults.class);
+    assertNull(someDefaults.getAnnotation());
+    assertEquals(SomeDefaults.class, someDefaults.getAnnotationType());
+  }
+
+  @Retention(RUNTIME)
+  @BindingAnnotation @interface AllDefaults {
+    int v1() default 1;
+    String v2() default "foo";
+  }
+
+  @Retention(RUNTIME)
+  @BindingAnnotation @interface SomeDefaults {
+    int v1() default 1;
+    String v2() default "foo";
+    Class<?> clazz();
+  }
+
+  @Retention(RUNTIME)
+  @BindingAnnotation @interface NoDefaults {
+    int value();
+  }
+
+  @Retention(RUNTIME)
+  @BindingAnnotation @interface Marker {
+  }
+
+  @AllDefaults
+  @Marker
+  class HasAnnotations {}
+
 }
diff --git a/core/test/com/google/inject/MethodInterceptionTest.java b/core/test/com/google/inject/MethodInterceptionTest.java
index 6d6d596..ac6d7d9 100644
--- a/core/test/com/google/inject/MethodInterceptionTest.java
+++ b/core/test/com/google/inject/MethodInterceptionTest.java
@@ -34,6 +34,7 @@ import org.aopalliance.intercept.MethodInvocation;
 import java.lang.reflect.Method;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Queue;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicReference;
 
@@ -79,7 +80,7 @@ public class MethodInterceptionTest extends TestCase {
 
     Interceptable nullFoosOne = childOne.getInstance(Interceptable.class);
     assertNotNull(nullFoosOne.bar());
-    assertNull(nullFoosOne.foo());
+    assertNull(nullFoosOne.foo()); // confirm it's being intercepted
 
     Injector childTwo = injector.createChildInjector(new AbstractModule() {
       protected void configure() {
@@ -88,10 +89,21 @@ public class MethodInterceptionTest extends TestCase {
     });
 
     Interceptable nullFoosTwo = childTwo.getInstance(Interceptable.class);
-    assertNull(nullFoosTwo.foo());
+    assertNull(nullFoosTwo.foo()); // confirm it's being intercepted
 
     assertSame("Child injectors should share proxy classes, otherwise memory leaks!",
         nullFoosOne.getClass(), nullFoosTwo.getClass());
+    
+    Injector injector2 = Guice.createInjector(new AbstractModule() {
+      protected void configure() {
+        bindInterceptor(Matchers.any(), Matchers.returns(only(Foo.class)),
+            new ReturnNullInterceptor());
+      }
+    });
+    Interceptable separateNullFoos = injector2.getInstance(Interceptable.class);
+    assertNull(separateNullFoos.foo()); // confirm it's being intercepted
+    assertSame("different injectors should share proxy classes, otherwise memory leaks!",
+        nullFoosOne.getClass(), separateNullFoos.getClass());
   }
 
   public void testGetThis() {
@@ -302,4 +314,56 @@ public class MethodInterceptionTest extends TestCase {
     }
   }
   
+  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());
+  }
+
+  public void testCallLater() {
+    final Queue<Runnable> queue = Lists.newLinkedList();
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      protected void configure() {
+        bindInterceptor(Matchers.any(), Matchers.any(), new CallLaterInterceptor(queue));
+      }
+    });
+
+    Interceptable interceptable = injector.getInstance(Interceptable.class);
+    interceptable.foo();
+    assertNull(interceptable.lastElements);
+    assertEquals(1, queue.size());
+
+    queue.remove().run();
+    assertNotNull(interceptable.lastElements);
+  }
+
+  private final class CallLaterInterceptor implements MethodInterceptor {
+    private final Queue<Runnable> queue;
+
+    public CallLaterInterceptor(Queue<Runnable> queue) {
+      this.queue = queue;
+    }
+
+    public Object invoke(final MethodInvocation methodInvocation) throws Throwable {
+      queue.add(new Runnable() {
+        @Override
+        public void run() {
+          try {
+            methodInvocation.proceed();
+          } catch (Throwable t) {
+            throw new RuntimeException(t);
+          }
+        }
+      });
+      return null;
+    }
+  }
 }
diff --git a/core/test/com/google/inject/ModulesTest.java b/core/test/com/google/inject/ModulesTest.java
index 76a28e0..b32e4f6 100644
--- a/core/test/com/google/inject/ModulesTest.java
+++ b/core/test/com/google/inject/ModulesTest.java
@@ -16,6 +16,8 @@
 
 package com.google.inject;
 
+import com.google.common.collect.ImmutableList;
+import com.google.inject.spi.ElementSource;
 import com.google.inject.util.Modules;
 
 import junit.framework.TestCase;
@@ -47,14 +49,23 @@ public class ModulesTest extends TestCase {
    * The module returned by Modules.combine shouldn't show up in binder sources.
    */
   public void testCombineSources() {
+    final Module m1 = newModule(1);
+    final Module m2 = newModule(2L);
+    final Module combined1 = Modules.combine(m1, m2);
     Module skipSourcesModule = new AbstractModule() {
       @Override protected void configure() {
-        install(Modules.combine(newModule(1), newModule(2L)));
+        install(combined1);
       }
     };
-    Injector injector = Guice.createInjector(Modules.combine(skipSourcesModule));
-    StackTraceElement source = (StackTraceElement) injector.getBinding(Integer.class).getSource();
-    assertEquals(skipSourcesModule.getClass().getName(), source.getClassName());
+    final Module combined2 = Modules.combine(skipSourcesModule);
+    Injector injector = Guice.createInjector(combined2);
+    ElementSource source = (ElementSource) injector.getBinding(Integer.class).getSource();
+    assertEquals(source.getModuleClassNames().size(), 4);
+    assertEquals(ImmutableList.of(m1.getClass().getName(),
+        combined1.getClass().getName(), skipSourcesModule.getClass().getName(),
+        combined2.getClass().getName()), source.getModuleClassNames());
+    StackTraceElement stackTraceElement = (StackTraceElement) source.getDeclaringSource();
+    assertEquals(skipSourcesModule.getClass().getName(), stackTraceElement.getClassName());
   }
 
   private <T> Module newModule(final T toBind) {
diff --git a/core/test/com/google/inject/NullableInjectionPointTest.java b/core/test/com/google/inject/NullableInjectionPointTest.java
index 10eaefd..9cf22cc 100644
--- a/core/test/com/google/inject/NullableInjectionPointTest.java
+++ b/core/test/com/google/inject/NullableInjectionPointTest.java
@@ -1,6 +1,7 @@
 package com.google.inject;
 
 import static com.google.inject.Asserts.assertContains;
+import static com.google.inject.Asserts.getDeclaringSourcePart;
 
 import junit.framework.TestCase;
 
@@ -122,7 +123,7 @@ public class NullableInjectionPointTest extends TestCase {
     } catch (CreationException expected) {
       assertContains(expected.getMessage(),
           "Binding to null instances is not allowed.",
-          "at " + getClass().getName(), ".configure(NullableInjectionPointTest.java:");
+          "at " + getClass().getName(), getDeclaringSourcePart(getClass()));
     }
   }
 
diff --git a/core/test/com/google/inject/ParentInjectorTest.java b/core/test/com/google/inject/ParentInjectorTest.java
index abedd6e..4940b77 100644
--- a/core/test/com/google/inject/ParentInjectorTest.java
+++ b/core/test/com/google/inject/ParentInjectorTest.java
@@ -17,6 +17,7 @@ limitations under the License.
 package com.google.inject;
 
 import static com.google.inject.Asserts.assertContains;
+import static com.google.inject.Asserts.getDeclaringSourcePart;
 import static java.lang.annotation.ElementType.TYPE;
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
@@ -44,8 +45,8 @@ public class ParentInjectorTest extends TestCase {
       fail("Created the same explicit binding on both parent and child");
     } catch (CreationException e) {
       assertContains(e.getMessage(), "A binding to ", A.class.getName(), " was already configured",
-          " at ", getClass().getName(), ".configure(ParentInjectorTest.java:",
-          " at ", getClass().getName(), ".configure(ParentInjectorTest.java:");
+          " at ", getClass().getName(), getDeclaringSourcePart(getClass()),
+          " at ", getClass().getName(), getDeclaringSourcePart(getClass()));
     }
   }
 
@@ -116,7 +117,7 @@ public class ParentInjectorTest extends TestCase {
 
   public void testScopesInherited() {
     Injector parent = Guice.createInjector(new AbstractModule() {
-      protected void configure() {
+      @Override protected void configure() {
         bindScope(MyScope.class, Scopes.SINGLETON);
       }
     });
@@ -138,14 +139,14 @@ public class ParentInjectorTest extends TestCase {
 
   public void testInterceptorsInherited() {
     Injector parent = Guice.createInjector(new AbstractModule() {
-      protected void configure() {
+      @Override protected void configure() {
         super.bindInterceptor(Matchers.any(), Matchers.returns(Matchers.identicalTo(A.class)),
             returnNullInterceptor);
       }
     });
 
     Injector child = parent.createChildInjector(new AbstractModule() {
-      protected void configure() {
+      @Override protected void configure() {
         bind(C.class);
       }
     });
@@ -176,7 +177,7 @@ public class ParentInjectorTest extends TestCase {
   public void testInjectorInjectionSpanningInjectors() {
     Injector parent = Guice.createInjector();
     Injector child = parent.createChildInjector(new AbstractModule() {
-      protected void configure() {
+      @Override protected void configure() {
         bind(D.class);
       }
     });
@@ -218,7 +219,7 @@ public class ParentInjectorTest extends TestCase {
   public void testScopeBoundInChildInjectorOnly() {
     Injector parent = Guice.createInjector();
     Injector child = parent.createChildInjector(new AbstractModule() {
-      protected void configure() {
+      @Override protected void configure() {
         bindScope(MyScope.class, Scopes.SINGLETON);
       }
     });
@@ -239,7 +240,7 @@ public class ParentInjectorTest extends TestCase {
   public void testErrorInParentButOkayInChild() {
     Injector parent = Guice.createInjector();
     Injector childInjector = parent.createChildInjector(new AbstractModule() {
-      protected void configure() {
+      @Override protected void configure() {
         bindScope(MyScope.class, Scopes.SINGLETON);
         bind(Object.class).to(F.class);
       }
@@ -267,7 +268,7 @@ public class ParentInjectorTest extends TestCase {
   static class A {}
 
   private final Module bindsA = new AbstractModule() {
-    protected void configure() {
+    @Override protected void configure() {
       bind(A.class).toInstance(new A());
     }
   };
@@ -276,7 +277,7 @@ public class ParentInjectorTest extends TestCase {
   static class RealB implements B {}
 
   private final Module bindsB = new AbstractModule() {
-    protected void configure() {
+    @Override protected void configure() {
       bind(B.class).to(RealB.class);
     }
   };
@@ -291,13 +292,13 @@ public class ParentInjectorTest extends TestCase {
   };
 
   private final Module bindListConverterModule = new AbstractModule() {
-    protected void configure() {
+    @Override protected void configure() {
       convertToTypes(Matchers.any(), listConverter);
     }
   };
 
   private final Module bindStringNamedB = new AbstractModule() {
-    protected void configure() {
+    @Override protected void configure() {
       bind(String.class).annotatedWith(Names.named("B")).toInstance("buzz");
     }
   };
@@ -317,7 +318,7 @@ public class ParentInjectorTest extends TestCase {
   }
 
   private final Module bindsD = new AbstractModule() {
-    protected void configure() {
+    @Override protected void configure() {
       bind(D.class);
     }
   };
diff --git a/core/test/com/google/inject/PrivateModuleTest.java b/core/test/com/google/inject/PrivateModuleTest.java
index 72fa35d..05db62b 100644
--- a/core/test/com/google/inject/PrivateModuleTest.java
+++ b/core/test/com/google/inject/PrivateModuleTest.java
@@ -16,7 +16,9 @@
 
 package com.google.inject;
 
+import static com.google.inject.Asserts.asModuleChain;
 import static com.google.inject.Asserts.assertContains;
+import static com.google.inject.Asserts.getDeclaringSourcePart;
 import static com.google.inject.name.Names.named;
 
 import com.google.common.collect.ImmutableSet;
@@ -40,11 +42,11 @@ public class PrivateModuleTest extends TestCase {
 
   public void testBasicUsage() {
     Injector injector = Guice.createInjector(new AbstractModule() {
-      protected void configure() {
+      @Override protected void configure() {
         bind(String.class).annotatedWith(named("a")).toInstance("public");
 
         install(new PrivateModule() {
-          public void configure() {
+          @Override public void configure() {
             bind(String.class).annotatedWith(named("b")).toInstance("i");
 
             bind(AB.class).annotatedWith(named("one")).to(AB.class);
@@ -53,7 +55,7 @@ public class PrivateModuleTest extends TestCase {
         });
 
         install(new PrivateModule() {
-          public void configure() {
+          @Override public void configure() {
             bind(String.class).annotatedWith(named("b")).toInstance("ii");
 
             bind(AB.class).annotatedWith(named("two")).to(AB.class);
@@ -74,7 +76,7 @@ public class PrivateModuleTest extends TestCase {
   
   public void testWithoutPrivateModules() {
     Injector injector = Guice.createInjector(new AbstractModule() {
-      protected void configure() {
+      @Override protected void configure() {
         PrivateBinder bindA = binder().newPrivateBinder();
         bindA.bind(String.class).annotatedWith(named("a")).toInstance("i");
         bindA.expose(String.class).annotatedWith(named("a"));
@@ -94,7 +96,7 @@ public class PrivateModuleTest extends TestCase {
   public void testMisplacedExposedAnnotation() {
     try {
       Guice.createInjector(new AbstractModule() {
-        protected void configure() {}
+        @Override protected void configure() {}
 
         @Provides @Exposed
         String provideString() {
@@ -112,7 +114,7 @@ public class PrivateModuleTest extends TestCase {
   public void testMisplacedExposeStatement() {
     try {
       Guice.createInjector(new AbstractModule() {
-        protected void configure() {
+        @Override protected void configure() {
           ((PrivateBinder) binder()).expose(String.class).annotatedWith(named("a"));
         }
       });
@@ -120,15 +122,15 @@ public class PrivateModuleTest extends TestCase {
     } catch (CreationException expected) {
       assertContains(expected.getMessage(), "Cannot expose java.lang.String on a standard binder. ",
           "Exposed bindings are only applicable to private binders.",
-          " at " + PrivateModuleTest.class.getName(), "configure(PrivateModuleTest.java:");
+          " at " + PrivateModuleTest.class.getName(), getDeclaringSourcePart(getClass()));
     }
   }
 
   public void testPrivateModulesAndProvidesMethods() {
     Injector injector = Guice.createInjector(new AbstractModule() {
-      protected void configure() {
+      @Override protected void configure() {
         install(new PrivateModule() {
-          public void configure() {
+          @Override public void configure() {
             expose(String.class).annotatedWith(named("a"));
           }
 
@@ -142,7 +144,7 @@ public class PrivateModuleTest extends TestCase {
         });
 
         install(new PrivateModule() {
-          public void configure() {}
+          @Override public void configure() {}
 
           @Provides @Named("c") String providePrivateC() {
             return "private";
@@ -175,16 +177,16 @@ public class PrivateModuleTest extends TestCase {
   public void testCannotBindAKeyExportedByASibling() {
     try {
       Guice.createInjector(new AbstractModule() {
-        protected void configure() {
+        @Override protected void configure() {
           install(new PrivateModule() {
-            public void configure() {
+            @Override public void configure() {
               bind(String.class).toInstance("public");
               expose(String.class);
             }
           });
 
           install(new PrivateModule() {
-            public void configure() {
+            @Override public void configure() {
               bind(String.class).toInstance("private");
             }
           });
@@ -194,20 +196,20 @@ public class PrivateModuleTest extends TestCase {
     } catch (CreationException expected) {
       assertContains(expected.getMessage(),
           "A binding to java.lang.String was already configured at ",
-          getClass().getName(), ".configure(PrivateModuleTest.java:",
-          " at " + getClass().getName(), ".configure(PrivateModuleTest.java:");
+          getClass().getName(), getDeclaringSourcePart(getClass()),
+          " at " + getClass().getName(), getDeclaringSourcePart(getClass()));
     }
   }
 
   public void testExposeButNoBind() {
     try {
       Guice.createInjector(new AbstractModule() {
-        protected void configure() {
+        @Override protected void configure() {
           bind(String.class).annotatedWith(named("a")).toInstance("a");
           bind(String.class).annotatedWith(named("b")).toInstance("b");
 
           install(new PrivateModule() {
-            public void configure() {
+            @Override public void configure() {
               expose(AB.class);
             }
           });
@@ -217,7 +219,7 @@ public class PrivateModuleTest extends TestCase {
     } catch (CreationException expected) {
       assertContains(expected.getMessage(),
           "Could not expose() " + AB.class.getName() + ", it must be explicitly bound",
-          ".configure(PrivateModuleTest.java:");
+          getDeclaringSourcePart(getClass()));
     }
   }
 
@@ -229,12 +231,12 @@ public class PrivateModuleTest extends TestCase {
     try {
       Guice.createInjector(
           new PrivateModule() {
-            public void configure() {
+            @Override public void configure() {
               bind(C.class);
             }
           },
           new PrivateModule() {
-            public void configure() {
+            @Override public void configure() {
               bind(AB.class);
             }
           }
@@ -243,7 +245,7 @@ public class PrivateModuleTest extends TestCase {
     } catch (CreationException expected) {
       assertContains(expected.getMessage(),
           "1) No implementation for " + C.class.getName() + " was bound.",
-          "at " + getClass().getName(), ".configure(PrivateModuleTest.java:",
+          "at " + getClass().getName(), getDeclaringSourcePart(getClass()),
           "2) No implementation for " + String.class.getName(), "Named(value=a) was bound.",
           "for field at " + AB.class.getName() + ".a(PrivateModuleTest.java:",
           "3) No implementation for " + String.class.getName(), "Named(value=b) was bound.",
@@ -254,11 +256,11 @@ public class PrivateModuleTest extends TestCase {
 
   public void testNestedPrivateInjectors() {
     Injector injector = Guice.createInjector(new PrivateModule() {
-      public void configure() {
+      @Override public void configure() {
         expose(String.class);
 
         install(new PrivateModule() {
-          public void configure() {
+          @Override public void configure() {
             bind(String.class).toInstance("nested");
             expose(String.class);
           }
@@ -271,11 +273,11 @@ public class PrivateModuleTest extends TestCase {
 
   public void testInstallingRegularModulesFromPrivateModules() {
     Injector injector = Guice.createInjector(new PrivateModule() {
-      public void configure() {
+      @Override public void configure() {
         expose(String.class);
 
         install(new AbstractModule() {
-          protected void configure() {
+          @Override protected void configure() {
             bind(String.class).toInstance("nested");
           }
         });
@@ -287,14 +289,14 @@ public class PrivateModuleTest extends TestCase {
 
   public void testNestedPrivateModulesWithSomeKeysUnexposed() {
     Injector injector = Guice.createInjector(new PrivateModule() {
-      public void configure() {
+      @Override public void configure() {
         bind(String.class).annotatedWith(named("bound outer, exposed outer")).toInstance("boeo");
         expose(String.class).annotatedWith(named("bound outer, exposed outer"));
         bind(String.class).annotatedWith(named("bound outer, exposed none")).toInstance("boen");
         expose(String.class).annotatedWith(named("bound inner, exposed both"));
 
         install(new PrivateModule() {
-          public void configure() {
+          @Override public void configure() {
             bind(String.class).annotatedWith(named("bound inner, exposed both")).toInstance("bieb");
             expose(String.class).annotatedWith(named("bound inner, exposed both"));
             bind(String.class).annotatedWith(named("bound inner, exposed none")).toInstance("bien");
@@ -324,7 +326,7 @@ public class PrivateModuleTest extends TestCase {
   public void testDependenciesBetweenPrivateAndPublic() {
     Injector injector = Guice.createInjector(
         new PrivateModule() {
-          protected void configure() {}
+          @Override protected void configure() {}
 
           @Provides @Exposed @Named("a") String provideA() {
             return "A";
@@ -335,7 +337,7 @@ public class PrivateModuleTest extends TestCase {
           }
         },
         new AbstractModule() {
-          protected void configure() {}
+          @Override protected void configure() {}
 
           @Provides @Named("ab") String provideAb(@Named("a") String a) {
             return a + "B";
@@ -353,7 +355,7 @@ public class PrivateModuleTest extends TestCase {
   public void testDependenciesBetweenPrivateAndPublicWithPublicEagerSingleton() {
     Injector injector = Guice.createInjector(
         new PrivateModule() {
-          protected void configure() {}
+          @Override protected void configure() {}
 
           @Provides @Exposed @Named("a") String provideA() {
             return "A";
@@ -364,7 +366,7 @@ public class PrivateModuleTest extends TestCase {
           }
         },
         new AbstractModule() {
-          protected void configure() {
+          @Override protected void configure() {
             bind(String.class).annotatedWith(named("abcde")).toProvider(new Provider<String>() {
               @Inject @Named("abcd") String abcd;
 
@@ -390,7 +392,7 @@ public class PrivateModuleTest extends TestCase {
   public void testDependenciesBetweenPrivateAndPublicWithPrivateEagerSingleton() {
     Injector injector = Guice.createInjector(
         new AbstractModule() {
-          protected void configure() {}
+          @Override protected void configure() {}
 
           @Provides @Named("ab") String provideAb(@Named("a") String a) {
             return a + "B";
@@ -401,7 +403,7 @@ public class PrivateModuleTest extends TestCase {
           }
         },
         new PrivateModule() {
-          protected void configure() {
+          @Override protected void configure() {
             bind(String.class).annotatedWith(named("abcde")).toProvider(new Provider<String>() {
               @Inject @Named("abcd") String abcd;
 
@@ -434,7 +436,7 @@ public class PrivateModuleTest extends TestCase {
 
   public void testSpiAccess() {
     Injector injector = Guice.createInjector(new PrivateModule() {
-          public void configure() {
+          @Override public void configure() {
             bind(String.class).annotatedWith(named("a")).toInstance("private");
             bind(String.class).annotatedWith(named("b")).toInstance("exposed");
             expose(String.class).annotatedWith(named("b"));
@@ -449,7 +451,7 @@ public class PrivateModuleTest extends TestCase {
     assertEquals(ImmutableSet.<Key<?>>of(Key.get(String.class, named("b"))),
         privateElements.getExposedKeys());
     assertContains(privateElements.getExposedSource(Key.get(String.class, named("b"))).toString(),
-        PrivateModuleTest.class.getName(), ".configure(PrivateModuleTest.java:");
+        PrivateModuleTest.class.getName(), getDeclaringSourcePart(getClass()));
     Injector privateInjector = privateElements.getInjector();
     assertEquals("private", privateInjector.getInstance(Key.get(String.class, Names.named("a"))));
   }
@@ -464,7 +466,10 @@ public class PrivateModuleTest extends TestCase {
           "Unable to create binding for java.util.List.",
           "It was already configured on one or more child injectors or private modules",
           "bound at " + FailingPrivateModule.class.getName() + ".configure(",
+          asModuleChain(FailingModule.class, ManyPrivateModules.class, FailingPrivateModule.class),
           "bound at " + SecondFailingPrivateModule.class.getName() + ".configure(",
+          asModuleChain(
+              FailingModule.class, ManyPrivateModules.class, SecondFailingPrivateModule.class),
           "If it was in a PrivateModule, did you forget to expose the binding?",
           "at " + FailingModule.class.getName() + ".configure(");
     }
@@ -481,7 +486,9 @@ public class PrivateModuleTest extends TestCase {
           "Unable to create binding for com.google.inject.Provider<java.util.List>.",
           "It was already configured on one or more child injectors or private modules",
           "bound at " + FailingPrivateModule.class.getName() + ".configure(",
+          asModuleChain(ManyPrivateModules.class, FailingPrivateModule.class),
           "bound at " + SecondFailingPrivateModule.class.getName() + ".configure(",
+          asModuleChain(ManyPrivateModules.class, SecondFailingPrivateModule.class),
           "If it was in a PrivateModule, did you forget to expose the binding?",
           "while locating com.google.inject.Provider<java.util.List>");
     }
@@ -502,18 +509,16 @@ public class PrivateModuleTest extends TestCase {
           "while locating " + PrivateFoo.class.getName());
     }
   }
-  
+
   private static class FailingModule extends AbstractModule {
-    @Override
-    protected void configure() {
+    @Override protected void configure() {
       bind(Collection.class).to(List.class);
       install(new ManyPrivateModules());
     }
   }
 
   private static class ManyPrivateModules extends AbstractModule {
-    @Override
-    protected void configure() {
+    @Override protected void configure() {
       // make sure duplicate sources are collapsed
       install(new FailingPrivateModule());
       install(new FailingPrivateModule());
@@ -523,8 +528,7 @@ public class PrivateModuleTest extends TestCase {
   }
 
   private static class FailingPrivateModule extends PrivateModule {
-    @Override
-    protected void configure() {
+    @Override protected void configure() {
       bind(List.class).toInstance(new ArrayList());
       
       // Add the Provider<List> binding, created just-in-time,
@@ -540,8 +544,7 @@ public class PrivateModuleTest extends TestCase {
   
   /** A second class, so we can see another name in the source list. */
   private static class SecondFailingPrivateModule extends PrivateModule {
-    @Override
-    protected void configure() {
+    @Override protected void configure() {
       bind(List.class).toInstance(new ArrayList());
       
       // Add the Provider<List> binding, created just-in-time,
diff --git a/core/test/com/google/inject/ProvisionExceptionTest.java b/core/test/com/google/inject/ProvisionExceptionTest.java
index aa4bfd5..e96a0f3 100644
--- a/core/test/com/google/inject/ProvisionExceptionTest.java
+++ b/core/test/com/google/inject/ProvisionExceptionTest.java
@@ -17,6 +17,7 @@
 package com.google.inject;
 
 import static com.google.inject.Asserts.assertContains;
+import static com.google.inject.Asserts.getDeclaringSourcePart;
 import static com.google.inject.Asserts.reserialize;
 import static java.lang.annotation.ElementType.CONSTRUCTOR;
 import static java.lang.annotation.ElementType.FIELD;
@@ -94,7 +95,8 @@ public class ProvisionExceptionTest extends TestCase {
       assertTrue(e.getCause() instanceof UnsupportedOperationException);
       assertContains(e.getMessage(),
           "1) Error in custom provider, java.lang.UnsupportedOperationException",
-          "at " + ProvisionExceptionTest.class.getName(), ".configure(ProvisionExceptionTest.java");
+          "at " + ProvisionExceptionTest.class.getName(),
+          getDeclaringSourcePart(getClass()));
     }
   }
 
@@ -136,7 +138,8 @@ public class ProvisionExceptionTest extends TestCase {
       fail();
     } catch (ProvisionException e) {
       assertContains(e.getMessage(), "1) User Exception",
-          "at " + ProvisionExceptionTest.class.getName(), ".configure(ProvisionExceptionTest.java");
+          "at " + ProvisionExceptionTest.class.getName(),
+          getDeclaringSourcePart(getClass()));
     }
   }
 
diff --git a/core/test/com/google/inject/ProvisionListenerTest.java b/core/test/com/google/inject/ProvisionListenerTest.java
index 9d08b5c..a66d346 100644
--- a/core/test/com/google/inject/ProvisionListenerTest.java
+++ b/core/test/com/google/inject/ProvisionListenerTest.java
@@ -21,23 +21,30 @@ import static com.google.inject.Asserts.assertContains;
 import static com.google.inject.name.Names.named;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
+import com.google.inject.matcher.AbstractMatcher;
 import com.google.inject.matcher.Matcher;
 import com.google.inject.matcher.Matchers;
 import com.google.inject.name.Named;
 import com.google.inject.spi.DependencyAndSource;
+import com.google.inject.spi.InstanceBinding;
 import com.google.inject.spi.ProvisionListener;
+import com.google.inject.util.Providers;
 
 import junit.framework.TestCase;
 
 import java.util.List;
+import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * Tests for {@link Binder#bindListener(Matcher, ProvisionListener...)}
  * 
  * @author sameb at google.com (Sam Berlin)
  */
+// TODO(sameb): Add some tests for private modules & child injectors.
 public class ProvisionListenerTest extends TestCase {
 
   public void testExceptionInListenerBeforeProvisioning() {
@@ -243,6 +250,8 @@ public class ProvisionListenerTest extends TestCase {
           throw new RuntimeException(ex);
         }
         bind(LinkedFoo.class).to(Foo.class);
+        bind(Interface.class).toInstance(new Implementation());
+        bindConstant().annotatedWith(named("constant")).to("MyConstant");
       }
       
       @Provides @Named("pi") Foo provideFooBar() {
@@ -250,6 +259,11 @@ public class ProvisionListenerTest extends TestCase {
       }
     });
     
+    // toInstance & constant bindings are notified in random order, at the very beginning.
+    assertEquals(
+        ImmutableSet.of(Key.get(Interface.class), Key.get(String.class, named("constant"))),
+        capturer.getAsSetAndClear());
+    
     // simple binding
     assertNotNull(injector.getInstance(Foo.class));
     assertEquals(of(Key.get(Foo.class)), capturer.getAndClear());
@@ -283,6 +297,62 @@ public class ProvisionListenerTest extends TestCase {
     assertEquals(of(Key.get(Foo.class)), capturer.getAndClear());
   }
   
+  public void testSingletonMatcher() {
+    final Counter counter = new Counter();
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      @Override
+      protected void configure() {
+        bindListener(new AbstractMatcher<Binding<?>>() {
+          @Override
+          public boolean matches(Binding<?> t) {
+            return Scopes.isSingleton(t);
+          }
+        }, counter);
+      }
+    });
+    assertEquals(0, counter.count);
+    // no increment for getting Many.
+    injector.getInstance(Many.class);
+    assertEquals(0, counter.count);
+    // but an increment for getting Sole, since it's a singleton.
+    injector.getInstance(Sole.class);
+    assertEquals(1, counter.count);
+  }
+  
+  public void testCallingBindingDotGetProviderDotGet() {
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      @Override
+      protected void configure() {
+        bindListener(Matchers.any(), new ProvisionListener() {
+          @Override
+          public <T> void onProvision(ProvisionInvocation<T> provision) {
+            provision.getBinding().getProvider().get(); // AGH!
+          }
+        });
+      }
+    });
+    
+    try {
+      injector.getInstance(Sole.class);
+      fail();
+    } catch(ProvisionException expected) {
+      // We don't really care what kind of error you get, we only care you get an error.
+    }
+    
+    try {
+      injector.getInstance(Many.class);
+      fail();
+    } catch(ProvisionException expected) {
+      // We don't really care what kind of error you get, we only care you get an error.
+    }
+  }
+  
+  interface Interface {}
+  class Implementation implements Interface {}
+  
+  @Singleton static class Sole {}
+  static class Many {}
+  
   @ImplementedBy(Foo.class) static interface JitFoo {}  
   @ProvidedBy(JitFoo2P.class) static class JitFoo2 {}  
   static interface LinkedFoo {}
@@ -314,9 +384,25 @@ public class ProvisionListenerTest extends TestCase {
   private static class Capturer implements ProvisionListener {
     List<Key> keys = Lists.newArrayList(); 
     public <T> void onProvision(ProvisionInvocation<T> provision) {
-      keys.add(provision.getKey());
+      keys.add(provision.getBinding().getKey());
       T provisioned = provision.provision();
-      assertEquals(provision.getKey().getRawType(), provisioned.getClass());
+      // InstanceBindings are the only kind of binding where the key can
+      // be an instanceof the provisioned, because it isn't linked to any
+      // direct implementation.  I guess maybe it'd also be possible
+      // with a toConstructor binding... but we don't use that in our tests.
+      if (provision.getBinding() instanceof InstanceBinding) {
+        Class<? super T> expected = provision.getBinding().getKey().getRawType();
+        assertTrue("expected instanceof: " + expected + ", but was: " + provisioned,
+            expected.isInstance(provisioned));
+      } else {
+        assertEquals(provision.getBinding().getKey().getRawType(), provisioned.getClass());
+      }
+    }
+    
+    Set<Key> getAsSetAndClear() {
+      Set<Key> copy = ImmutableSet.copyOf(keys);
+      keys.clear();
+      return copy;
     }
     
     List<Key> getAndClear() {
@@ -368,12 +454,17 @@ public class ProvisionListenerTest extends TestCase {
         actual.add(dep.getDependency().getKey().getRawType());
       }
       assertEquals(expected, actual);
-      provisionList.add(provision.getKey().getRawType());
+      provisionList.add(provision.getBinding().getKey().getRawType());
     }
   }
   
-  private static Matcher<Object> keyMatcher(Class<?> clazz) {
-    return Matchers.only(Key.get(clazz));
+  private static Matcher<Binding<?>> keyMatcher(final Class<?> clazz) {
+    return new AbstractMatcher<Binding<?>>() {
+      @Override
+      public boolean matches(Binding<?> t) {
+        return t.getKey().equals(Key.get(clazz));
+      }
+    };
   }
   
   @SuppressWarnings("unchecked")
@@ -389,14 +480,16 @@ public class ProvisionListenerTest extends TestCase {
         
         bindListener(Matchers.any(), new ProvisionListener() {
           public <T> void onProvision(ProvisionInvocation<T> provision) {
-            totalList.add(provision.getKey().getRawType());
+            totalList.add(provision.getBinding().getKey().getRawType());
           }
         });
         
         // Build up a list of asserters for our dependency chains.
         ImmutableList.Builder<Class<?>> chain = ImmutableList.builder();
+        chain.add(Instance.class);
+        bindListener(keyMatcher(Instance.class), new ChainAsserter(pList, chain.build()));
         
-        chain.add(Instance.class).add(A.class);
+        chain.add(A.class);
         bindListener(keyMatcher(A.class), new ChainAsserter(pList, chain.build()));
         
         chain.add(B.class).add(BImpl.class);
@@ -423,7 +516,8 @@ public class ProvisionListenerTest extends TestCase {
     });
     Instance instance = injector.getInstance(Instance.class);
     // make sure we're checking all of the chain asserters..
-    assertEquals(of(A.class, BImpl.class, C.class, DP.class, D.class, E.class, F.class),
+    assertEquals(
+        of(Instance.class, A.class, BImpl.class, C.class, DP.class, D.class, E.class, F.class),
         pList);
     // and make sure that nothing else was notified that we didn't expect.
     assertEquals(totalList, pList);
@@ -490,7 +584,7 @@ public class ProvisionListenerTest extends TestCase {
     
     public <T> void onProvision(ProvisionInvocation<T> provision) {
       notified.set(true);
-      assertEquals(notifyType, provision.getKey().getRawType());            
+      assertEquals(notifyType, provision.getBinding().getKey().getRawType());            
       assertEquals(2, provision.getDependencyChain().size());
       
       assertEquals(null, provision.getDependencyChain().get(0).getDependency());
@@ -526,5 +620,92 @@ public class ProvisionListenerTest extends TestCase {
     @SuppressWarnings("unused")
     @Inject F f;
   }
-  private static class F {}
+  private static class F {
+  }
+
+  public void testBindToInjectorWithListeningGivesSaneException() {
+    try {
+      Guice.createInjector(new AbstractModule() {
+        @Override
+        protected void configure() {
+          bindListener(Matchers.any(), new Counter());
+          bind(Injector.class).toProvider(Providers.<Injector>of(null));
+        }
+      });
+      fail();
+    } catch (CreationException ce) {
+      assertContains(
+          ce.getMessage(), "Binding to core guice framework type is not allowed: Injector.");
+    }
+  }
+
+  public void testProvisionIsNotifiedAfterContextsClear() {
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      @Override
+      protected void configure() {
+        bindListener(Matchers.any(), new ProvisionListener() {
+          @Override
+          public <T> void onProvision(ProvisionInvocation<T> provision) {
+            Object provisioned = provision.provision();
+            if (provisioned instanceof X) {
+              ((X)provisioned).init();
+            } else if (provisioned instanceof Y) {
+              X.createY = false;
+              ((Y)provisioned).init();
+            }
+          }
+        });
+      }
+    });
+
+    X.createY = true;
+    X x = injector.getInstance(X.class);
+    assertNotSame(x, x.y.x);
+    assertFalse("x.ID: " + x.ID + ", x.y.x.iD: " + x.y.x.ID, x.ID == x.y.x.ID);
+  }
+
+  private static class X {
+    final static AtomicInteger COUNTER = new AtomicInteger();
+    static boolean createY;
+
+    final int ID = COUNTER.getAndIncrement();
+    final Provider<Y> yProvider;
+    Y y;
+
+    @Inject X(Provider<Y> yProvider) {
+      this.yProvider = yProvider;
+    }
+
+    void init() {
+      if (createY) {
+        this.y = yProvider.get();
+      }
+    }
+  }
+
+  private static class Y {
+    final Provider<X> xProvider;
+    X x;
+
+    @Inject Y(Provider<X> xProvider) {
+      this.xProvider = xProvider;
+    }
+
+    void init() {
+      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/ReflectionTest.java b/core/test/com/google/inject/ReflectionTest.java
index 15695e4..610f099 100644
--- a/core/test/com/google/inject/ReflectionTest.java
+++ b/core/test/com/google/inject/ReflectionTest.java
@@ -18,6 +18,8 @@ package com.google.inject;
 
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
+import com.google.inject.spi.ElementSource;
+
 import junit.framework.TestCase;
 
 import java.lang.annotation.Retention;
@@ -41,7 +43,8 @@ public class ReflectionTest extends TestCase {
 
     Binding<Foo> fooBinding = injector.getBinding(Key.get(Foo.class));
     assertSame(foo, fooBinding.getProvider().get());
-    assertNotNull(fooBinding.getSource());
+    ElementSource source = (ElementSource) fooBinding.getSource();
+    assertNotNull(source.getDeclaringSource());
     assertEquals(Key.get(Foo.class), fooBinding.getKey());
   }
 
@@ -54,7 +57,8 @@ public class ReflectionTest extends TestCase {
 
     Binding<?> i = injector.getBinding(Key.get(int.class, I.class));
     assertEquals(5, i.getProvider().get());
-    assertNotNull(i.getSource());
+    ElementSource source = (ElementSource) i.getSource();
+    assertNotNull(source.getDeclaringSource());
     assertEquals(Key.get(int.class, I.class), i.getKey());
   }
 
@@ -70,7 +74,8 @@ public class ReflectionTest extends TestCase {
 
     Binding<Foo> fooBinding = injector.getBinding(Key.get(Foo.class));
     assertSame(bar, fooBinding.getProvider().get());
-    assertNotNull(fooBinding.getSource());
+    ElementSource source = (ElementSource) fooBinding.getSource();
+    assertNotNull(source.getDeclaringSource());
     assertEquals(Key.get(Foo.class), fooBinding.getKey());
   }
 
diff --git a/core/test/com/google/inject/RequestInjectionTest.java b/core/test/com/google/inject/RequestInjectionTest.java
index 9d3072f..dfd0f77 100644
--- a/core/test/com/google/inject/RequestInjectionTest.java
+++ b/core/test/com/google/inject/RequestInjectionTest.java
@@ -17,6 +17,7 @@
 package com.google.inject;
 
 import static com.google.inject.Asserts.assertContains;
+import static com.google.inject.Asserts.getDeclaringSourcePart;
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
 import com.google.inject.matcher.Matchers;
@@ -128,7 +129,7 @@ public class RequestInjectionTest extends TestCase {
       assertContains(expected.getMessage(),
           "1) Error in custom provider, java.lang.UnsupportedOperationException",
           "for field at " + NeedsRunnable.class.getName() + ".runnable(RequestInjectionTest.java:",
-          "at " + getClass().getName(), ".configure(RequestInjectionTest.java:");
+          "at " + getClass().getName(), getDeclaringSourcePart(getClass()));
     }
   }
 
diff --git a/core/test/com/google/inject/RequireAtInjectOnConstructorsTest.java b/core/test/com/google/inject/RequireAtInjectOnConstructorsTest.java
new file mode 100644
index 0000000..e5f90c2
--- /dev/null
+++ b/core/test/com/google/inject/RequireAtInjectOnConstructorsTest.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2012 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 junit.framework.TestCase;
+
+/**
+ * Tests for {@link Binder#requireAtInjectOnConstructors()}
+ * 
+ * @author sameb at google.com (Sam Berlin)
+ */
+public class RequireAtInjectOnConstructorsTest extends TestCase {
+  
+  public void testNoCxtors_explicitBinding() {
+    try {
+      Guice.createInjector(new AbstractModule() {
+        @Override
+        protected void configure() {
+          bind(NoCxtors.class);
+          binder().requireAtInjectOnConstructors();
+        }
+      });
+      fail();
+    } catch (CreationException ce) {
+      assertEquals(1, ce.getErrorMessages().size());
+      Asserts.assertContains(ce.getMessage(),
+          "1) Explicit @Inject annotations are required on constructors, but "
+          + NoCxtors.class.getName() + " has no constructors annotated with @Inject",
+          "at " + RequireAtInjectOnConstructorsTest.class.getName() + "$", "configure");
+    }
+  }
+  
+  public void testNoCxtors_jitBinding() {
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      @Override
+      protected void configure() {
+        binder().requireAtInjectOnConstructors();
+      }
+    });
+    try {
+      injector.getInstance(NoCxtors.class);
+      fail();
+    } catch (ConfigurationException ce) {
+      Asserts.assertContains(ce.getMessage(),
+          "1) Explicit @Inject annotations are required on constructors, but "
+          + NoCxtors.class.getName() + " has no constructors annotated with @Inject",
+          "while locating " + NoCxtors.class.getName());
+    }
+  }
+  
+  public void testNoCxtors_implicitBinding() {
+    try {
+      Guice.createInjector(new AbstractModule() {
+        @Override
+        protected void configure() {
+          bind(Interface.class).to(NoCxtors.class);
+          binder().requireAtInjectOnConstructors();
+        }
+      });
+      fail();
+    } catch (CreationException ce) {
+      assertEquals(1, ce.getErrorMessages().size());
+      Asserts.assertContains(ce.getMessage(),
+          "1) Explicit @Inject annotations are required on constructors, but "
+          + NoCxtors.class.getName() + " has no constructors annotated with @Inject",
+          "at " + RequireAtInjectOnConstructorsTest.class.getName() + "$", "configure");
+    }
+  }
+  
+  public void testNoCxtors_inheritedByPrivateModules() {
+    try {
+      Guice.createInjector(new AbstractModule() {
+        @Override
+        protected void configure() {
+          binder().requireAtInjectOnConstructors();
+          install(new PrivateModule() {
+            @Override
+            protected void configure() {
+              bind(NoCxtors.class);
+            }
+          });
+        }
+      });
+      fail();
+    } catch (CreationException ce) {
+      assertEquals(1, ce.getErrorMessages().size());
+      Asserts.assertContains(ce.getMessage(),
+          "1) Explicit @Inject annotations are required on constructors, but "
+          + NoCxtors.class.getName() + " has no constructors annotated with @Inject",
+          "at " + RequireAtInjectOnConstructorsTest.class.getName() + "$", "configure");
+    }
+  }
+  
+  public void testNoCxtors_accumulatesAllErrors() {
+    try {
+      Guice.createInjector(new AbstractModule() {
+        @Override
+        protected void configure() {
+          bind(NoCxtors.class);
+          bind(AnotherNoCxtors.class);
+          binder().requireAtInjectOnConstructors();
+        }
+      });
+      fail();
+    } catch (CreationException ce) {
+      assertEquals(2, ce.getErrorMessages().size());
+      Asserts.assertContains(ce.getMessage(),
+          "1) Explicit @Inject annotations are required on constructors, but "
+          + NoCxtors.class.getName() + " has no constructors annotated with @Inject",
+          "at " + RequireAtInjectOnConstructorsTest.class.getName() + "$", "configure",
+          "2) Explicit @Inject annotations are required on constructors, but "
+          + AnotherNoCxtors.class.getName() + " has no constructors annotated with @Inject",
+          "at " + RequireAtInjectOnConstructorsTest.class.getName() + "$", "configure");
+    }
+  }
+  
+  public void testNoCxtors_separateOptionsForPrivateModules() {
+    try {
+      Guice.createInjector(new AbstractModule() {
+        @Override
+        protected void configure() {
+          bind(AnotherNoCxtors.class);
+          install(new PrivateModule() {
+            @Override
+            protected void configure() {
+              binder().requireAtInjectOnConstructors();
+              bind(NoCxtors.class);
+            }
+          });
+        }
+      });
+      fail();
+    } catch (CreationException ce) {
+      // This is testing that the parent module doesn't fail because it isn't included
+      // in the error message.
+      assertEquals(1, ce.getErrorMessages().size());
+      Asserts.assertContains(ce.getMessage(),
+          "1) Explicit @Inject annotations are required on constructors, but "
+          + NoCxtors.class.getName() + " has no constructors annotated with @Inject",
+          "at " + RequireAtInjectOnConstructorsTest.class.getName() + "$", "configure");
+    }
+  }
+  
+  public void testManyConstructorsButNoneWithAtInject() {
+    try {
+      Guice.createInjector(new AbstractModule() {
+        @Override
+        protected void configure() {
+          bind(ManyConstructors.class);
+          binder().requireAtInjectOnConstructors();
+        }
+      });
+      fail();
+    } catch (CreationException ce) {
+      assertEquals(1, ce.getErrorMessages().size());
+      Asserts.assertContains(ce.getMessage(),
+          "1) Explicit @Inject annotations are required on constructors, but "
+          + ManyConstructors.class.getName() + " has no constructors annotated with @Inject",
+          "at " + RequireAtInjectOnConstructorsTest.class.getName() + "$", "configure");
+    }
+  }
+  
+  public void testRequireAtInjectStillAllowsToConstructorBindings() {
+    Injector injector = Guice.createInjector(new AbstractModule() {
+        @Override
+        protected void configure() {
+          try {
+            bind(ManyConstructors.class)
+                .toConstructor(ManyConstructors.class.getDeclaredConstructor());
+          } catch (Exception e) {
+            throw new RuntimeException(e);
+          }
+          binder().requireAtInjectOnConstructors();
+        }
+      });
+    injector.getInstance(ManyConstructors.class);
+  }
+  
+  private static interface Interface {}
+  private static class NoCxtors implements Interface {}  
+  private static class AnotherNoCxtors {}
+  private static class ManyConstructors {
+    @SuppressWarnings("unused") ManyConstructors() {}
+    @SuppressWarnings("unused") ManyConstructors(String a) {}
+    @SuppressWarnings("unused") ManyConstructors(int a) {}
+  }
+}
diff --git a/core/test/com/google/inject/ScopesTest.java b/core/test/com/google/inject/ScopesTest.java
index 689aec4..d95c797 100644
--- a/core/test/com/google/inject/ScopesTest.java
+++ b/core/test/com/google/inject/ScopesTest.java
@@ -16,7 +16,9 @@
 
 package com.google.inject;
 
+import static com.google.inject.Asserts.asModuleChain;
 import static com.google.inject.Asserts.assertContains;
+import static com.google.inject.Asserts.getDeclaringSourcePart;
 import static com.google.inject.name.Names.named;
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
@@ -46,7 +48,7 @@ import java.util.Map;
 public class ScopesTest extends TestCase {
 
   private final AbstractModule singletonsModule = new AbstractModule() {
-    protected void configure() {
+    @Override protected void configure() {
       bind(BoundAsSingleton.class).in(Scopes.SINGLETON);
       bind(AnnotatedSingleton.class);
       bind(EagerSingleton.class).asEagerSingleton();
@@ -122,7 +124,7 @@ public class ScopesTest extends TestCase {
 
   public void testOverriddingAnnotation() {
     Injector injector = Guice.createInjector(new AbstractModule() {
-      protected void configure() {
+      @Override protected void configure() {
         bind(AnnotatedSingleton.class).in(Scopes.NO_SCOPE);
       }
     });
@@ -135,7 +137,7 @@ public class ScopesTest extends TestCase {
   public void testScopingAnnotationsOnAbstractTypeViaBind() {
     try {
       Guice.createInjector(new AbstractModule() {
-        protected void configure() {
+        @Override protected void configure() {
           bind(A.class).to(AImpl.class);
         }
       });
@@ -191,7 +193,7 @@ public class ScopesTest extends TestCase {
   public void testScopeUsedButNotBound() {
     try {
       Guice.createInjector(new AbstractModule() {
-        protected void configure() {
+        @Override protected void configure() {
           bind(B.class).in(CustomScoped.class);
           bind(C.class);
         }
@@ -200,7 +202,7 @@ public class ScopesTest extends TestCase {
     } catch (CreationException expected) {
       assertContains(expected.getMessage(),
           "1) No scope is bound to " + CustomScoped.class.getName(),
-          "at " + getClass().getName(), ".configure(ScopesTest.java:",
+          "at " + getClass().getName(), getDeclaringSourcePart(getClass()),
           "2) No scope is bound to " + CustomScoped.class.getName(),
           "at " + C.class.getName() + ".class");
     }
@@ -241,11 +243,11 @@ public class ScopesTest extends TestCase {
     Asserts.assertNotSerializable(Scopes.NO_SCOPE);
   }
 
-  public void ignoreTestUnscopedProviderWorksOutsideOfRequestedScope() {
+  public void testUnscopedProviderWorksOutsideOfRequestedScope() {
     final RememberProviderScope scope = new RememberProviderScope();
 
     Injector injector = Guice.createInjector(new AbstractModule() {
-      protected void configure() {
+      @Override protected void configure() {
         bindScope(CustomScoped.class, scope);
         bind(List.class).to(ArrayList.class).in(CustomScoped.class);
       }
@@ -259,56 +261,86 @@ public class ScopesTest extends TestCase {
     assertTrue(listProvider.get() instanceof ArrayList);
   }
 
+  static class OuterRuntimeModule extends AbstractModule {
+    @Override protected void configure() {
+      install(new InnerRuntimeModule());
+    }
+  }
+  static class InnerRuntimeModule extends AbstractModule {
+    @Override protected void configure() {
+      bindScope(NotRuntimeRetainedScoped.class, Scopes.NO_SCOPE);
+    }
+  }
   public void testScopeAnnotationWithoutRuntimeRetention() {
     try {
-      Guice.createInjector(new AbstractModule() {
-        protected void configure() {
-          bindScope(NotRuntimeRetainedScoped.class, Scopes.NO_SCOPE);
-        }
-      });
+      Guice.createInjector(new OuterRuntimeModule());
       fail();
     } catch (CreationException expected) {
       assertContains(expected.getMessage(),
-          "1) Please annotate with @Retention(RUNTIME).",
-          "at " + NotRuntimeRetainedScoped.class.getName() + ".class(ScopesTest.java:");
+          "1) Please annotate " + NotRuntimeRetainedScoped.class.getName()
+              + " with @Retention(RUNTIME).",
+          "at " + InnerRuntimeModule.class.getName() + getDeclaringSourcePart(getClass()),
+          asModuleChain(OuterRuntimeModule.class, InnerRuntimeModule.class));
     }
   }
 
+  static class OuterDeprecatedModule extends AbstractModule {
+    @Override protected void configure() {
+      install(new InnerDeprecatedModule());
+    }
+  }
+  static class InnerDeprecatedModule extends AbstractModule {
+    @Override protected void configure() {
+      bindScope(Deprecated.class, Scopes.NO_SCOPE);
+    }
+  }
   public void testBindScopeToAnnotationWithoutScopeAnnotation() {
     try {
-      Guice.createInjector(new AbstractModule() {
-        protected void configure() {
-          bindScope(Deprecated.class, Scopes.NO_SCOPE);
-        }
-      });
+      Guice.createInjector(new OuterDeprecatedModule());
       fail();
     } catch (CreationException expected) {
       assertContains(expected.getMessage(),
-          "1) Please annotate with @ScopeAnnotation.",
-          "at " + Deprecated.class.getName() + ".class(");
+          "1) Please annotate " + Deprecated.class.getName() + " with @ScopeAnnotation.",
+          "at " + InnerDeprecatedModule.class.getName() + getDeclaringSourcePart(getClass()),
+          asModuleChain(OuterDeprecatedModule.class, InnerDeprecatedModule.class));
+    }
+  }
+
+  static class OuterScopeModule extends AbstractModule {
+    @Override protected void configure() {
+      install(new CustomNoScopeModule());
+      install(new CustomSingletonModule());
+    }
+  }
+  static class CustomNoScopeModule extends AbstractModule {
+    @Override protected void configure() {
+      bindScope(CustomScoped.class, Scopes.NO_SCOPE);
+    }
+  }
+  static class CustomSingletonModule extends AbstractModule {
+    @Override protected void configure() {
+      bindScope(CustomScoped.class, Scopes.SINGLETON);
     }
   }
 
   public void testBindScopeTooManyTimes() {
     try {
-      Guice.createInjector(new AbstractModule() {
-        protected void configure() {
-          bindScope(CustomScoped.class, Scopes.NO_SCOPE);
-          bindScope(CustomScoped.class, Scopes.SINGLETON);
-        }
-      });
+      Guice.createInjector(new OuterScopeModule());
       fail();
     } catch (CreationException expected) {
       assertContains(expected.getMessage(),
-          "1) Scope Scopes.NO_SCOPE is already bound to " + CustomScoped.class.getName(),
+          "1) Scope Scopes.NO_SCOPE is already bound to " + CustomScoped.class.getName()
+              + " at " + CustomNoScopeModule.class.getName() + getDeclaringSourcePart(getClass()),
+          asModuleChain(OuterScopeModule.class, CustomNoScopeModule.class),
           "Cannot bind Scopes.SINGLETON.",
-          "at " + ScopesTest.class.getName(), ".configure(ScopesTest.java:");
+          "at " + ScopesTest.class.getName(), getDeclaringSourcePart(getClass()),
+          asModuleChain(OuterScopeModule.class, CustomSingletonModule.class));
     }
   }
 
   public void testDuplicateScopeAnnotations() {
     Injector injector = Guice.createInjector(new AbstractModule() {
-      protected void configure() {
+      @Override protected void configure() {
         bindScope(CustomScoped.class, Scopes.NO_SCOPE);
       }
     });
@@ -413,7 +445,9 @@ public class ScopesTest extends TestCase {
     final int instanceId = nextInstanceId++;
   }
 
-  @Singleton @CustomScoped
+  @SuppressWarnings("MoreThanOneScopeAnnotationOnClass") // suppress compiler error for testing
+  @Singleton
+  @CustomScoped
   static class SingletonAndCustomScoped {}
 
   @ImplementedBy(Implementation.class)
@@ -438,7 +472,7 @@ public class ScopesTest extends TestCase {
 
   public void testScopeThatGetsAnUnrelatedObject() {
     Injector injector = Guice.createInjector(new AbstractModule() {
-      protected void configure() {
+      @Override protected void configure() {
         bind(B.class);
         bind(C.class);
         ProviderGetScope providerGetScope = new ProviderGetScope();
@@ -475,7 +509,7 @@ public class ScopesTest extends TestCase {
     final Key<String> i = Key.get(String.class, named("I"));
 
     Module singletonBindings = new AbstractModule() {
-      protected void configure() {
+      @Override protected void configure() {
         bind(a).to(b);
         bind(b).to(c);
         bind(c).toProvider(Providers.of("c")).in(Scopes.SINGLETON);
@@ -484,8 +518,7 @@ public class ScopesTest extends TestCase {
         bind(f).toProvider(Providers.of("f")).in(Singleton.class);
         bind(h).to(AnnotatedSingleton.class);
         install(new PrivateModule() {
-          @Override
-          protected void configure() {
+          @Override protected void configure() {
             bind(i).toProvider(Providers.of("i")).in(Singleton.class);
             expose(i);
           }
@@ -531,15 +564,14 @@ public class ScopesTest extends TestCase {
     final Key<String> f = Key.get(String.class, named("F"));
 
     Module singletonBindings = new AbstractModule() {
-      protected void configure() {
+      @Override protected void configure() {
         bind(a).to(b);
         bind(b).to(c);
         bind(c).toProvider(Providers.of("c")).in(Scopes.NO_SCOPE);
         bind(d).toProvider(Providers.of("d")).in(CustomScoped.class);
         bindScope(CustomScoped.class, Scopes.NO_SCOPE);
         install(new PrivateModule() {
-          @Override
-          protected void configure() {
+          @Override protected void configure() {
             bind(f).toProvider(Providers.of("f")).in(CustomScoped.class);
             expose(f);
           }
@@ -580,7 +612,7 @@ public class ScopesTest extends TestCase {
     final Key<String> g = Key.get(String.class, named("G"));
 
     Module customBindings = new AbstractModule() {
-      protected void configure() {
+      @Override protected void configure() {
         bindScope(CustomScoped.class, CUSTOM_SCOPE);
         bind(a).to(b);
         bind(b).to(c);
@@ -588,8 +620,7 @@ public class ScopesTest extends TestCase {
         bind(d).toProvider(Providers.of("d")).in(CustomScoped.class);
         bind(f).to(AnnotatedCustomScoped.class);
         install(new PrivateModule() {
-          @Override
-          protected void configure() {
+          @Override protected void configure() {
             bind(g).toProvider(Providers.of("g")).in(CustomScoped.class);
             expose(g);
           }
@@ -633,14 +664,13 @@ public class ScopesTest extends TestCase {
     final Key<String> h = Key.get(String.class, named("H"));
 
     Module customBindings = new AbstractModule() {
-      protected void configure() {
+      @Override protected void configure() {
         bind(a).to(b);
         bind(b).to(c);
         bind(c).toProvider(Providers.of("c")).in(Scopes.NO_SCOPE);
         bind(d).toProvider(Providers.of("d")).in(Singleton.class);
         install(new PrivateModule() {
-          @Override
-          protected void configure() {
+          @Override protected void configure() {
             bind(f).toProvider(Providers.of("f")).in(Singleton.class);
             expose(f);
           }
diff --git a/core/test/com/google/inject/TypeConversionTest.java b/core/test/com/google/inject/TypeConversionTest.java
index df3ee93..e318477 100644
--- a/core/test/com/google/inject/TypeConversionTest.java
+++ b/core/test/com/google/inject/TypeConversionTest.java
@@ -16,7 +16,9 @@
 
 package com.google.inject;
 
+import static com.google.inject.Asserts.asModuleChain;
 import static com.google.inject.Asserts.assertContains;
+import static com.google.inject.Asserts.getDeclaringSourcePart;
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
 import com.google.common.collect.Iterables;
@@ -73,7 +75,7 @@ public class TypeConversionTest extends TestCase {
 
   public void testOneConstantInjection() throws CreationException {
     Injector injector = Guice.createInjector(new AbstractModule() {
-      protected void configure() {
+      @Override protected void configure() {
         bindConstant().annotatedWith(NumericValue.class).to("5");
         bind(Simple.class);
       }
@@ -89,7 +91,7 @@ public class TypeConversionTest extends TestCase {
 
   public void testConstantInjection() throws CreationException {
     Injector injector = Guice.createInjector(new AbstractModule() {
-      protected void configure() {
+      @Override protected void configure() {
         bindConstant().annotatedWith(NumericValue.class).to("5");
         bindConstant().annotatedWith(BooleanValue.class).to("true");
         bindConstant().annotatedWith(EnumValue.class).to("TEE");
@@ -120,7 +122,7 @@ public class TypeConversionTest extends TestCase {
 
   public void testConstantInjectionWithExplicitBindingsRequired() throws CreationException {
     Injector injector = Guice.createInjector(new AbstractModule() {
-      protected void configure() {
+      @Override protected void configure() {
         binder().requireExplicitBindings();
         bind(Foo.class);
         bindConstant().annotatedWith(NumericValue.class).to("5");
@@ -157,20 +159,31 @@ public class TypeConversionTest extends TestCase {
     }
   }
 
-  public void testInvalidInteger() throws CreationException {
-    Injector injector = Guice.createInjector(new AbstractModule() {
-      protected void configure() {
-        bindConstant().annotatedWith(NumericValue.class).to("invalid");
-      }
-    });
+  static class OuterErrorModule extends AbstractModule {
+    @Override protected void configure() {
+      install(new InnerErrorModule());
+    }
+  }
 
+  static class InnerErrorModule extends AbstractModule {
+    @Override protected void configure() {
+      bindConstant().annotatedWith(NumericValue.class).to("invalid");
+    }
+  }
+
+  public void testInvalidInteger() throws CreationException {
+    Injector injector = Guice.createInjector(new OuterErrorModule());
     try {
       injector.getInstance(InvalidInteger.class);
       fail();
     } catch (ConfigurationException expected) {
-      assertContains(expected.getMessage(), "Error converting 'invalid'");
-      assertContains(expected.getMessage(), "bound at " + getClass().getName());
-      assertContains(expected.getMessage(), "to java.lang.Integer");
+      assertContains(expected.getMessage(),
+          "Error converting 'invalid' (bound at " + InnerErrorModule.class.getName()
+              + getDeclaringSourcePart(getClass()),
+          asModuleChain(OuterErrorModule.class, InnerErrorModule.class),
+          "using TypeConverter<Integer> which matches identicalTo(class java.lang.Integer)"
+              + " (bound at [unknown source]).",
+          "Reason: java.lang.RuntimeException: For input string: \"invalid\"");
     }
   }
 
@@ -180,7 +193,7 @@ public class TypeConversionTest extends TestCase {
 
   public void testInvalidCharacter() throws CreationException {
     Injector injector = Guice.createInjector(new AbstractModule() {
-      protected void configure() {
+      @Override protected void configure() {
         bindConstant().annotatedWith(NumericValue.class).to("invalid");
       }
     });
@@ -201,7 +214,7 @@ public class TypeConversionTest extends TestCase {
 
   public void testInvalidEnum() throws CreationException {
     Injector injector = Guice.createInjector(new AbstractModule() {
-      protected void configure() {
+      @Override protected void configure() {
         bindConstant().annotatedWith(NumericValue.class).to("invalid");
       }
     });
@@ -222,7 +235,7 @@ public class TypeConversionTest extends TestCase {
 
   public void testToInstanceIsTreatedLikeConstant() throws CreationException {
     Injector injector = Guice.createInjector(new AbstractModule() {
-      protected void configure() {
+      @Override protected void configure() {
         bind(String.class).toInstance("5");
         bind(LongHolder.class);
       }
@@ -239,7 +252,7 @@ public class TypeConversionTest extends TestCase {
     final Date result = new Date();
 
     Injector injector = Guice.createInjector(new AbstractModule() {
-      protected void configure() {
+      @Override protected void configure() {
         convertToTypes(Matchers.only(TypeLiteral.get(Date.class)) , mockTypeConverter(result));
         bindConstant().annotatedWith(NumericValue.class).to("Today");
         bind(DateHolder.class);
@@ -257,15 +270,16 @@ public class TypeConversionTest extends TestCase {
     assertTrue(injector.getTypeConverterBindings().contains(converterBinding));
   }
 
-  public void testInvalidCustomValue() throws CreationException {
-    Module module = new AbstractModule() {
-      protected void configure() {
-        convertToTypes(Matchers.only(TypeLiteral.get(Date.class)), failingTypeConverter());
-        bindConstant().annotatedWith(NumericValue.class).to("invalid");
-        bind(DateHolder.class);
-      }
-    };
+  static class InvalidCustomValueModule extends AbstractModule {
+    @Override protected void configure() {
+      convertToTypes(Matchers.only(TypeLiteral.get(Date.class)), failingTypeConverter());
+      bindConstant().annotatedWith(NumericValue.class).to("invalid");
+      bind(DateHolder.class);
+    }
+  }
 
+  public void testInvalidCustomValue() throws CreationException {
+    Module module = new InvalidCustomValueModule();
     try {
       Guice.createInjector(module);
       fail();
@@ -274,56 +288,88 @@ public class TypeConversionTest extends TestCase {
       assertTrue(cause instanceof UnsupportedOperationException);
       assertContains(expected.getMessage(),
           "1) Error converting 'invalid' (bound at ", getClass().getName(),
-          ".configure(TypeConversionTest.java:", "to java.util.Date",
+          getDeclaringSourcePart(getClass()), "to java.util.Date",
           "using BrokenConverter which matches only(java.util.Date) ",
-          "(bound at " + getClass().getName(), ".configure(TypeConversionTest.java:",
+          "(bound at " + getClass().getName(), getDeclaringSourcePart(getClass()),
           "Reason: java.lang.UnsupportedOperationException: Cannot convert",
           "at " + DateHolder.class.getName() + ".date(TypeConversionTest.java:");
     }
   }
 
-  public void testNullCustomValue() {
-    Module module = new AbstractModule() {
-      protected void configure() {
-        convertToTypes(Matchers.only(TypeLiteral.get(Date.class)), mockTypeConverter(null));
-        bindConstant().annotatedWith(NumericValue.class).to("foo");
-        bind(DateHolder.class);
-      }
-    };
+  static class OuterModule extends AbstractModule {
+    private final Module converterModule;
+    OuterModule(Module converterModule) {
+      this.converterModule = converterModule;
+    }
+
+    @Override protected void configure() {
+      install(new InnerModule(converterModule));
+    }
+  }
 
+  static class InnerModule extends AbstractModule {
+    private final Module converterModule;
+    InnerModule(Module converterModule) {
+      this.converterModule = converterModule;
+    }
+
+    @Override protected void configure() {
+      install(converterModule);
+      bindConstant().annotatedWith(NumericValue.class).to("foo");
+      bind(DateHolder.class);
+    }
+  }
+
+  class ConverterNullModule extends AbstractModule {
+    @Override protected void configure() {
+      convertToTypes(Matchers.only(TypeLiteral.get(Date.class)), mockTypeConverter(null));
+    }
+  }
+
+  public void testNullCustomValue() {
     try {
-      Guice.createInjector(module);
+      Guice.createInjector(new OuterModule(new ConverterNullModule()));
       fail();
     } catch (CreationException expected) {
       assertContains(expected.getMessage(),
-          "1) Received null converting 'foo' (bound at ", getClass().getName(),
-          ".configure(TypeConversionTest.java:", "to java.util.Date",
+          "1) Received null converting 'foo' (bound at ",
+          getClass().getName(),
+          getDeclaringSourcePart(getClass()),
+          asModuleChain(OuterModule.class, InnerModule.class),
+          "to java.util.Date",
           "using CustomConverter which matches only(java.util.Date) ",
-          "(bound at " + getClass().getName(), ".configure(TypeConversionTest.java:",
-          "at " + DateHolder.class.getName() + ".date(TypeConversionTest.java:");
+          "(bound at " + getClass().getName(),
+          getDeclaringSourcePart(getClass()),
+          asModuleChain(OuterModule.class, InnerModule.class, ConverterNullModule.class),
+          "at " + DateHolder.class.getName() + ".date(TypeConversionTest.java:",
+          asModuleChain(OuterModule.class, InnerModule.class));
     }
   }
 
-  public void testCustomValueTypeMismatch() {
-    Module module = new AbstractModule() {
-      protected void configure() {
-        convertToTypes(Matchers.only(TypeLiteral.get(Date.class)), mockTypeConverter(-1));
-        bindConstant().annotatedWith(NumericValue.class).to("foo");
-        bind(DateHolder.class);
-      }
-    };
+  class ConverterCustomModule extends AbstractModule {
+    @Override protected void configure() {
+      convertToTypes(Matchers.only(TypeLiteral.get(Date.class)), mockTypeConverter(-1));
+    }
+  }
 
+  public void testCustomValueTypeMismatch() {
     try {
-      Guice.createInjector(module);
+      Guice.createInjector(new OuterModule(new ConverterCustomModule()));
       fail();
     } catch (CreationException expected) {
       assertContains(expected.getMessage(),
-          "1) Type mismatch converting 'foo' (bound at ", getClass().getName(),
-          ".configure(TypeConversionTest.java:", "to java.util.Date",
+          "1) Type mismatch converting 'foo' (bound at ",
+          getClass().getName(),
+          getDeclaringSourcePart(getClass()),
+          asModuleChain(OuterModule.class, InnerModule.class),
+          "to java.util.Date",
           "using CustomConverter which matches only(java.util.Date) ",
-          "(bound at " + getClass().getName(), ".configure(TypeConversionTest.java:",
+          "(bound at " + getClass().getName(),
+          getDeclaringSourcePart(getClass()),
+          asModuleChain(OuterModule.class, InnerModule.class, ConverterCustomModule.class),
           "Converter returned -1.",
-          "at " + DateHolder.class.getName() + ".date(TypeConversionTest.java:");
+          "at " + DateHolder.class.getName() + ".date(TypeConversionTest.java:",
+          asModuleChain(OuterModule.class, InnerModule.class));
     }
   }
 
@@ -340,7 +386,7 @@ public class TypeConversionTest extends TestCase {
     };
 
     Injector injector = Guice.createInjector(new AbstractModule() {
-      protected void configure() {
+      @Override protected void configure() {
         convertToTypes(Matchers.only(TypeLiteral.get(Date.class)), converter);
         bindConstant().annotatedWith(NumericValue.class).to("unused");
       }
@@ -351,25 +397,54 @@ public class TypeConversionTest extends TestCase {
     assertSame(first, second);
   }
 
-  public void testAmbiguousTypeConversion() {
-    Module module = new AbstractModule() {
-      protected void configure() {
-        convertToTypes(Matchers.only(TypeLiteral.get(Date.class)), mockTypeConverter(new Date()));
-        convertToTypes(Matchers.only(TypeLiteral.get(Date.class)), mockTypeConverter(new Date()));
-        bindConstant().annotatedWith(NumericValue.class).to("foo");
-        bind(DateHolder.class);
-      }
-    };
+  class OuterAmbiguousModule extends AbstractModule {
+    @Override protected void configure() {
+      install(new InnerAmbiguousModule());
+    }
+  }
+
+  class InnerAmbiguousModule extends AbstractModule {
+    @Override protected void configure() {
+      install(new Ambiguous1Module());
+      install(new Ambiguous2Module());
+      bindConstant().annotatedWith(NumericValue.class).to("foo");
+      bind(DateHolder.class);
+    }
+  }
 
+  class Ambiguous1Module extends AbstractModule {
+    @Override protected void configure() {
+      convertToTypes(Matchers.only(TypeLiteral.get(Date.class)), mockTypeConverter(new Date()));
+    }
+  }
+
+  class Ambiguous2Module extends AbstractModule {
+    @Override protected void configure() {
+      convertToTypes(Matchers.only(TypeLiteral.get(Date.class)), mockTypeConverter(new Date()));
+    }
+  }
+
+  public void testAmbiguousTypeConversion() {
     try {
-      Guice.createInjector(module);
+      Guice.createInjector(new OuterAmbiguousModule());
       fail();
     } catch (CreationException expected) {
       assertContains(expected.getMessage(),
           "1) Multiple converters can convert 'foo' (bound at ", getClass().getName(),
-          ".configure(TypeConversionTest.java:", "to java.util.Date:",
-          "CustomConverter which matches only(java.util.Date)", "and",
-          "CustomConverter which matches only(java.util.Date)",
+          getDeclaringSourcePart(getClass()),
+          asModuleChain(OuterAmbiguousModule.class, InnerAmbiguousModule.class),
+          "to java.util.Date:",
+          "CustomConverter which matches only(java.util.Date) (bound at "
+              + Ambiguous1Module.class.getName()
+              + getDeclaringSourcePart(getClass()),
+          asModuleChain(
+              OuterAmbiguousModule.class, InnerAmbiguousModule.class, Ambiguous1Module.class),
+          "and",
+          "CustomConverter which matches only(java.util.Date) (bound at "
+              + Ambiguous2Module.class.getName()
+              + getDeclaringSourcePart(getClass()),
+          asModuleChain(
+              OuterAmbiguousModule.class, InnerAmbiguousModule.class, Ambiguous2Module.class),
           "Please adjust your type converter configuration to avoid overlapping matches.",
           "at " + DateHolder.class.getName() + ".date(TypeConversionTest.java:");
     }
@@ -387,7 +462,7 @@ public class TypeConversionTest extends TestCase {
     };
   }
 
-  private TypeConverter failingTypeConverter() {
+  private static TypeConverter failingTypeConverter() {
     return new TypeConverter() {
       public Object convert(String value, TypeLiteral<?> toType) {
         throw new UnsupportedOperationException("Cannot convert");
@@ -402,9 +477,9 @@ public class TypeConversionTest extends TestCase {
     @Inject @NumericValue Date date;
   }
 
-  public void ignoreTestCannotConvertUnannotatedBindings() {
+  public void testCannotConvertUnannotatedBindings() {
     Injector injector = Guice.createInjector(new AbstractModule() {
-      protected void configure() {
+      @Override protected void configure() {
         bind(String.class).toInstance("55");
       }
     });
diff --git a/core/test/com/google/inject/TypeListenerTest.java b/core/test/com/google/inject/TypeListenerTest.java
index 5526d2e..ba7520d 100644
--- a/core/test/com/google/inject/TypeListenerTest.java
+++ b/core/test/com/google/inject/TypeListenerTest.java
@@ -16,7 +16,9 @@
 
 package com.google.inject;
 
+import static com.google.inject.Asserts.asModuleChain;
 import static com.google.inject.Asserts.assertContains;
+import static com.google.inject.Asserts.getDeclaringSourcePart;
 import static com.google.inject.matcher.Matchers.any;
 import static com.google.inject.matcher.Matchers.only;
 import static com.google.inject.name.Names.named;
@@ -82,7 +84,7 @@ public class TypeListenerTest extends TestCase {
     }
   };
 
-  public void testTypeListenersAreFired() throws NoSuchMethodException {
+  public void testTypeListenersAreFired() {
     final AtomicInteger firedCount = new AtomicInteger();
 
     final TypeListener typeListener = new TypeListener() {
@@ -93,7 +95,7 @@ public class TypeListenerTest extends TestCase {
     };
 
     Guice.createInjector(new AbstractModule() {
-      protected void configure() {
+      @Override protected void configure() {
         bindListener(onlyAbcd, typeListener);
         bind(A.class);
       }
@@ -111,7 +113,7 @@ public class TypeListenerTest extends TestCase {
     };
 
     Injector injector = Guice.createInjector(new AbstractModule() {
-      protected void configure() {
+      @Override protected void configure() {
         bindListener(onlyAbcd, new TypeListener() {
           public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
             encounter.register(injectionListener);
@@ -154,7 +156,7 @@ public class TypeListenerTest extends TestCase {
     final Matcher<Object> buzz = only(C.class.getMethod("buzz"));
 
     Injector injector = Guice.createInjector(new AbstractModule() {
-      protected void configure() {
+      @Override protected void configure() {
         bindInterceptor(any(), buzz, prefixInterceptor("ka"));
         bindInterceptor(any(), any(), prefixInterceptor("fe"));
 
@@ -174,30 +176,38 @@ public class TypeListenerTest extends TestCase {
   }
   /*end[AOP]*/
 
+  class OuterThrowsModule extends AbstractModule {
+    @Override protected void configure() {
+      install(new InnerThrowsModule());
+    }
+  }
+  class InnerThrowsModule extends AbstractModule {
+    @Override protected void configure() {
+      bindListener(onlyAbcd, failingTypeListener);
+      bind(B.class);
+      bind(C.class);
+    }
+  }
   public void testTypeListenerThrows() {
     try {
-      Guice.createInjector(new AbstractModule() {
-        protected void configure() {
-          bindListener(onlyAbcd, failingTypeListener);
-          bind(B.class);
-          bind(C.class);
-        }
-      });
+      Guice.createInjector(new OuterThrowsModule());
       fail();
     } catch (CreationException expected) {
       assertContains(expected.getMessage(),
           "1) Error notifying TypeListener clumsy (bound at " + getClass().getName(),
-          ".configure(TypeListenerTest.java:",
+          getDeclaringSourcePart(getClass()),
+          asModuleChain(OuterThrowsModule.class, InnerThrowsModule.class),
           "of " + B.class.getName(), 
           "Reason: java.lang.ClassCastException: whoops, failure #1",
           "2) Error notifying TypeListener clumsy (bound at " + getClass().getName(),
-          ".configure(TypeListenerTest.java:",
+          getDeclaringSourcePart(getClass()),
+          asModuleChain(OuterThrowsModule.class, InnerThrowsModule.class),
           "of " + C.class.getName(),
           "Reason: java.lang.ClassCastException: whoops, failure #2");
     }
     
     Injector injector = Guice.createInjector(new AbstractModule() {
-      protected void configure() {
+      @Override protected void configure() {
         bindListener(onlyAbcd, failingTypeListener);
       }
     });
@@ -207,7 +217,7 @@ public class TypeListenerTest extends TestCase {
     } catch (ConfigurationException expected) {
       assertContains(expected.getMessage(),
           "1) Error notifying TypeListener clumsy (bound at " + getClass().getName(),
-          ".configure(TypeListenerTest.java:",
+          getDeclaringSourcePart(getClass()),
           "of " + B.class.getName(),
           "Reason: java.lang.ClassCastException: whoops, failure #3");
     }
@@ -219,7 +229,7 @@ public class TypeListenerTest extends TestCase {
     } catch (ConfigurationException expected) {
       assertContains(expected.getMessage(),
           "1) Error notifying TypeListener clumsy (bound at " + getClass().getName(),
-          ".configure(TypeListenerTest.java:",
+          getDeclaringSourcePart(getClass()),
           "of " + B.class.getName(),
           "Reason: java.lang.ClassCastException: whoops, failure #3");
     }
@@ -230,7 +240,7 @@ public class TypeListenerTest extends TestCase {
 
   public void testInjectionListenerThrows() {
     Injector injector = Guice.createInjector(new AbstractModule() {
-      protected void configure() {
+      @Override protected void configure() {
         bindListener(onlyAbcd, new TypeListener() {
           public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
             encounter.register(failingInjectionListener);
@@ -277,7 +287,7 @@ public class TypeListenerTest extends TestCase {
   public void testInjectMembersTypeListenerFails() {
     try {
       Guice.createInjector(new AbstractModule() {
-        protected void configure() {
+        @Override protected void configure() {
           getMembersInjector(A.class);
           bindListener(onlyAbcd, failingTypeListener);
         }
@@ -286,7 +296,7 @@ public class TypeListenerTest extends TestCase {
     } catch (CreationException expected) {
       assertContains(expected.getMessage(),
           "1) Error notifying TypeListener clumsy (bound at ",
-          TypeListenerTest.class.getName(), ".configure(TypeListenerTest.java:",
+          TypeListenerTest.class.getName(), getDeclaringSourcePart(getClass()),
           "of " + A.class.getName(),
           " Reason: java.lang.ClassCastException: whoops, failure #1");
     }
@@ -304,8 +314,9 @@ public class TypeListenerTest extends TestCase {
     };
 
     Injector injector = Guice.createInjector(new AbstractModule() {
-      protected void configure() {
+      @Override protected void configure() {
         bindListener(onlyAbcd, new TypeListener() {
+          @SuppressWarnings("unchecked")
           public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
             typeEncounters.incrementAndGet();
             encounter.register((InjectionListener) listener);
@@ -363,7 +374,7 @@ public class TypeListenerTest extends TestCase {
     };
 
     Guice.createInjector(new AbstractModule() {
-      protected void configure() {
+      @Override protected void configure() {
         bindListener(only(TypeLiteral.get(C.class)), new TypeListener() {
           public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
             Provider<B> bProvider = encounter.getProvider(B.class);
@@ -401,7 +412,7 @@ public class TypeListenerTest extends TestCase {
 
   public void testLookupsPostCreate() {
     Injector injector = Guice.createInjector(new AbstractModule() {
-      protected void configure() {
+      @Override protected void configure() {
         bindListener(only(TypeLiteral.get(C.class)), new TypeListener() {
           public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
             assertNotNull(encounter.getProvider(B.class).get());
@@ -434,8 +445,9 @@ public class TypeListenerTest extends TestCase {
     };
 
     Injector injector = Guice.createInjector(new AbstractModule() {
-      protected void configure() {
+      @Override protected void configure() {
         bindListener(onlyAbcd, new TypeListener() {
+          @SuppressWarnings("unchecked")
           public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
             encounter.register((MembersInjector) membersInjector);
             encounter.register((InjectionListener) injectionListener);
@@ -469,7 +481,7 @@ public class TypeListenerTest extends TestCase {
 
   public void testMembersInjectorThrows() {
     Injector injector = Guice.createInjector(new AbstractModule() {
-      protected void configure() {
+      @Override protected void configure() {
         bindListener(onlyAbcd, new TypeListener() {
           public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
             encounter.register(failingMembersInjector);
@@ -522,7 +534,7 @@ public class TypeListenerTest extends TestCase {
     final AtomicInteger notificationCount = new AtomicInteger();
 
     Guice.createInjector(new AbstractModule() {
-      protected void configure() {
+      @Override protected void configure() {
         bindListener(onlyAbcd, new TypeListener() {
           public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
             notificationCount.incrementAndGet();
@@ -540,7 +552,7 @@ public class TypeListenerTest extends TestCase {
     final AtomicReference<TypeEncounter<?>> encounterReference = new AtomicReference<TypeEncounter<?>>();
 
     Guice.createInjector(new AbstractModule() {
-      protected void configure() {
+      @Override protected void configure() {
         bindListener(any(), new TypeListener() {
           public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
             encounterReference.set(encounter);
@@ -595,8 +607,9 @@ public class TypeListenerTest extends TestCase {
   public void testAddErrors() {
     try {
       Guice.createInjector(new AbstractModule() {
-        protected void configure() {
-          bindListener(Matchers.only(new TypeLiteral<Stage>() {}), new TypeListener() {
+        @Override protected void configure() {
+          requestInjection(new Object());
+          bindListener(Matchers.any(), new TypeListener() {
             public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
               encounter.addError("There was an error on %s", type);
               encounter.addError(new IllegalArgumentException("whoops!"));
@@ -609,7 +622,7 @@ public class TypeListenerTest extends TestCase {
       fail();
     } catch (CreationException expected) {
       assertContains(expected.getMessage(),
-          "1) There was an error on com.google.inject.Stage",
+          "1) There was an error on java.lang.Object",
           "2) An exception was caught and reported. Message: whoops!",
           "3) And another problem",
           "4) An exception was caught and reported. Message: null",
@@ -617,6 +630,49 @@ public class TypeListenerTest extends TestCase {
     }
   }
 
+  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 {
diff --git a/core/test/com/google/inject/internal/MoreTypesTest.java b/core/test/com/google/inject/internal/MoreTypesTest.java
index 34026a0..0ce5504 100644
--- a/core/test/com/google/inject/internal/MoreTypesTest.java
+++ b/core/test/com/google/inject/internal/MoreTypesTest.java
@@ -20,6 +20,7 @@ import com.google.inject.TypeLiteral;
 
 import junit.framework.TestCase;
 
+import java.lang.reflect.Type;
 import java.util.Map;
 import java.util.Set;
 
@@ -44,5 +45,10 @@ public class MoreTypesTest extends TestCase {
         MoreTypes.typeToString(mapInnerLongToSetInnerLong.getType()));
   }
 
+  public <T> void testEquals_typeVariable() throws Exception {
+    Type type = getClass().getMethod("testEquals_typeVariable").getTypeParameters()[0];
+    assertTrue(MoreTypes.equals(new TypeLiteral<T>() {}.getType(), type));
+  }
+
   public static class Inner<T> {}
 }
diff --git a/core/test/com/google/inject/internal/RehashableKeysTest.java b/core/test/com/google/inject/internal/RehashableKeysTest.java
new file mode 100644
index 0000000..d1676ac
--- /dev/null
+++ b/core/test/com/google/inject/internal/RehashableKeysTest.java
@@ -0,0 +1,132 @@
+package com.google.inject.internal;
+
+import static com.google.inject.internal.RehashableKeys.Keys.needsRehashing;
+import static com.google.inject.internal.RehashableKeys.Keys.rehash;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import com.google.inject.BindingAnnotation;
+import com.google.inject.Key;
+import com.google.inject.name.Names;
+
+import junit.framework.TestCase;
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.Retention;
+
+/**
+ * @author chrispurcell at google.com (Chris Purcell)
+ */
+public class RehashableKeysTest extends TestCase {
+
+  public void testNeedsRehashing_noAnnotation() {
+    Key<?> key = Key.get(Integer.class);
+    assertFalse(needsRehashing(key));
+  }
+
+  public void testNeedsRehashing_noParametersAnnotation() {
+    Key<?> key = Key.get(Integer.class, NoParametersAnnotation.class);
+    assertFalse(needsRehashing(key));
+  }
+
+  public void testNeedsRehashing_immutableAnnotation() {
+    Key<?> key = Key.get(Integer.class, Names.named("testy"));
+    assertFalse(needsRehashing(key));
+  }
+
+  public void testNeedsRehashing_mutableAnnotation() {
+    MutableTestAnnotation annotation = new MutableTestAnnotation(100);
+    Key<?> key = Key.get(Integer.class, annotation);
+    assertFalse(needsRehashing(key));
+
+    annotation.setValue(101);
+    assertTrue(needsRehashing(key));
+
+    Key<?> key2 = Key.get(Integer.class, annotation);
+    assertTrue(needsRehashing(key));
+    assertFalse(needsRehashing(key2));
+
+    annotation.setValue(102);
+    assertTrue(needsRehashing(key));
+    assertTrue(needsRehashing(key2));
+
+    annotation.setValue(100);
+    assertFalse(needsRehashing(key));
+    assertTrue(needsRehashing(key2));
+  }
+
+  public void testRehash_noParametersAnnotation() {
+    Key<?> key = Key.get(Integer.class, NoParametersAnnotation.class);
+    assertSame(key, rehash(key));
+  }
+
+  public void testRehash_noAnnotation() {
+    Key<?> key = Key.get(Integer.class);
+    assertSame(key, rehash(key));
+  }
+
+  public void testRehash_immutableAnnotation() {
+    Key<?> key = Key.get(Integer.class, Names.named("testy"));
+    Key<?> keyCopy = rehash(key);
+    assertEquals(key, keyCopy);
+    assertEquals(key.hashCode(), keyCopy.hashCode());
+  }
+
+  public void testRehash_mutableAnnotation() {
+    MutableTestAnnotation annotation = new MutableTestAnnotation(100);
+    Key<?> key = Key.get(Integer.class, annotation);
+    Key<?> keyCopy = rehash(key);
+    assertTrue(key.equals(keyCopy));
+    assertTrue(key.hashCode() == keyCopy.hashCode());
+
+    annotation.setValue(101);
+    Key<?> keyCopy2 = rehash(key);
+    assertTrue(key.equals(keyCopy2));
+    assertFalse(key.hashCode() == keyCopy2.hashCode());
+
+    annotation.setValue(100);
+    Key<?> keyCopy3 = rehash(keyCopy2);
+    assertTrue(key.equals(keyCopy3));
+    assertTrue(key.hashCode() == keyCopy3.hashCode());
+    assertTrue(keyCopy2.equals(keyCopy3));
+    assertFalse(keyCopy2.hashCode() == keyCopy3.hashCode());
+  }
+
+  @Retention(RUNTIME) @BindingAnnotation
+  private @interface NoParametersAnnotation { }
+
+  @Retention(RUNTIME) @BindingAnnotation
+  private @interface TestAnnotation {
+    int value();
+  }
+
+  private static class MutableTestAnnotation implements TestAnnotation {
+
+    private int value;
+
+    MutableTestAnnotation(int value) {
+      this.value = value;
+    }
+
+    public Class<? extends Annotation> annotationType() {
+      return TestAnnotation.class;
+    }
+
+    public int value() {
+      return value;
+    }
+
+    void setValue(int value) {
+      this.value = value;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+      return (obj instanceof TestAnnotation) && (((TestAnnotation) obj).value() == value);
+    }
+
+    @Override
+    public int hashCode() {
+      return value;
+    }
+  }
+}
diff --git a/core/test/com/google/inject/internal/util/LineNumbersTest.java b/core/test/com/google/inject/internal/util/LineNumbersTest.java
index 68a2b26..da2824a 100644
--- a/core/test/com/google/inject/internal/util/LineNumbersTest.java
+++ b/core/test/com/google/inject/internal/util/LineNumbersTest.java
@@ -17,6 +17,7 @@
 package com.google.inject.internal.util;
 
 import static com.google.inject.Asserts.assertContains;
+import static com.google.inject.Asserts.getDeclaringSourcePart;
 
 import com.google.inject.AbstractModule;
 import com.google.inject.CreationException;
@@ -46,7 +47,7 @@ public class LineNumbersTest extends TestCase {
       assertContains(expected.getMessage(),
           "1) No implementation for " + B.class.getName() + " was bound.",
           "for parameter 0 at " + A.class.getName() + ".<init>(LineNumbersTest.java:",
-          "at " + LineNumbersTest.class.getName(), ".configure(LineNumbersTest.java:");
+          "at " + LineNumbersTest.class.getName(), getDeclaringSourcePart(getClass()));
     }
   }
 
@@ -75,7 +76,7 @@ public class LineNumbersTest extends TestCase {
       assertContains(expected.getMessage(),
           "1) No implementation for " + B.class.getName() + " was bound.",
           "for parameter 0 at " + A.class.getName() + ".<init>(LineNumbersTest.java:",
-          "at " + LineNumbersTest.class.getName(), ".configure(LineNumbersTest.java:");
+          "at " + LineNumbersTest.class.getName(), getDeclaringSourcePart(getClass()));
     }
   }
 
@@ -125,7 +126,7 @@ public class LineNumbersTest extends TestCase {
       assertContains(expected.getMessage(),
           "1) No implementation for " + B.class.getName() + " was bound.",
           "for parameter 0 at " + GeneratingClassLoader.name + ".<init>(Unknown Source)",
-          "at " + LineNumbersTest.class.getName(), ".configure(LineNumbersTest.java:");
+          "at " + LineNumbersTest.class.getName(), getDeclaringSourcePart(getClass()));
     }
   }
   
diff --git a/core/test/com/google/inject/spi/ElementApplyToTest.java b/core/test/com/google/inject/spi/ElementApplyToTest.java
index baa8ca4..d483ba4 100644
--- a/core/test/com/google/inject/spi/ElementApplyToTest.java
+++ b/core/test/com/google/inject/spi/ElementApplyToTest.java
@@ -23,6 +23,7 @@ import com.google.inject.Module;
  */
 public class ElementApplyToTest extends ElementsTest {
 
+  @Override
   protected void checkModule(Module module, ElementVisitor<?>... visitors) {
     // convert from module to elements and back
     super.checkModule(Elements.getModule(Elements.getElements(module)), visitors);
diff --git a/core/test/com/google/inject/spi/ElementSourceTest.java b/core/test/com/google/inject/spi/ElementSourceTest.java
new file mode 100644
index 0000000..93fbeac
--- /dev/null
+++ b/core/test/com/google/inject/spi/ElementSourceTest.java
@@ -0,0 +1,175 @@
+package com.google.inject.spi;
+
+import static com.google.inject.internal.InternalFlags.getIncludeStackTraceOption;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.Binder;
+import com.google.inject.Binding;
+import com.google.inject.BindingAnnotation;
+import com.google.inject.Module;
+
+import junit.framework.TestCase;
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+import java.util.List;
+
+/**
+ * Tests for {@link ElementSource}.
+ */
+public class ElementSourceTest extends TestCase {
+
+  private static final StackTraceElement BINDER_INSTALL = 
+      new StackTraceElement("com.google.inject.spi.Elements$RecordingBinder", "install", 
+          "Unknown Source", 234 /* line number*/);
+  
+  public void testCallStackSize() {
+    ModuleSource moduleSource = createModuleSource();
+    StackTraceElement[] bindingCallStack = new StackTraceElement[3];
+    bindingCallStack[0] = new StackTraceElement(
+        "com.google.inject.spi.Elements$RecordingBinder", "bind", "Unknown Source", 200);
+    bindingCallStack[1] = new StackTraceElement(
+        "com.google.inject.spi.Elements$RecordingBinder", "bind", "Unknown Source", 100);
+    bindingCallStack[2] = new StackTraceElement(
+        "com.google.inject.spi.moduleSourceTest$C", "configure", "Unknown Source", 100);
+    ElementSource elementSource = new ElementSource(
+        null /* No original element source */, "" /* Don't care */, moduleSource, bindingCallStack);
+    assertEquals(10 /* call stack size */, elementSource.getStackTrace().length);
+  }  
+
+  public void testGetCallStack_IntegrationTest() throws Exception {
+    List<Element> elements = Elements.getElements(new A());
+    for (Element element : elements) {
+      if (element instanceof Binding) {
+        Binding<?> binding = (Binding<?>) element;
+        Class<? extends Annotation> annotationType = binding.getKey().getAnnotationType();
+        if (annotationType != null && annotationType.equals(SampleAnnotation.class)) {
+          ElementSource elementSource = (ElementSource) binding.getSource();
+          List<String> moduleClassNames = elementSource.getModuleClassNames();
+          // Check module class names
+          // Module C
+          assertEquals("com.google.inject.spi.ElementSourceTest$C", moduleClassNames.get(0));
+          // Module B
+          assertEquals("com.google.inject.spi.ElementSourceTest$B", moduleClassNames.get(1));
+          // Module A
+          assertEquals("com.google.inject.spi.ElementSourceTest$A", moduleClassNames.get(2));
+          StackTraceElement[] callStack = elementSource.getStackTrace();
+          switch(getIncludeStackTraceOption()) {
+            case OFF:
+              // Check declaring source
+              StackTraceElement stackTraceElement = 
+                  (StackTraceElement) elementSource.getDeclaringSource();
+              assertEquals(new StackTraceElement(
+                  "com.google.inject.spi.ElementSourceTest$C", "configure", null, -1), 
+                  stackTraceElement);
+              // Check call stack
+              assertEquals(0, callStack.length);
+              return;
+            case ONLY_FOR_DECLARING_SOURCE:
+                // Check call stack
+                assertEquals(0, callStack.length);
+                return;
+            case COMPLETE:
+              // Check call stack
+              int skippedCallStackSize = new Throwable().getStackTrace().length - 1;
+              assertEquals(skippedCallStackSize + 15, elementSource.getStackTrace().length);
+              assertEquals("com.google.inject.spi.Elements$RecordingBinder",
+                  callStack[0].getClassName());
+              assertEquals("com.google.inject.spi.Elements$RecordingBinder",
+                  callStack[1].getClassName());
+              assertEquals("com.google.inject.AbstractModule",
+                  callStack[2].getClassName());
+              // Module C
+              assertEquals("com.google.inject.spi.ElementSourceTest$C",
+                  callStack[3].getClassName());
+              assertEquals("configure",
+                  callStack[3].getMethodName());
+              assertEquals("Unknown Source",
+                  callStack[3].getFileName());
+              assertEquals("com.google.inject.AbstractModule",
+                  callStack[4].getClassName());
+              assertEquals("com.google.inject.spi.Elements$RecordingBinder",
+                  callStack[5].getClassName());
+              // Module B
+              assertEquals("com.google.inject.spi.ElementSourceTest$B",
+                  callStack[6].getClassName());
+              assertEquals("com.google.inject.spi.Elements$RecordingBinder",
+                  callStack[7].getClassName());
+              // Module A
+              assertEquals("com.google.inject.AbstractModule",
+                  callStack[8].getClassName());
+              assertEquals("com.google.inject.spi.ElementSourceTest$A",
+                  callStack[9].getClassName());
+              assertEquals("com.google.inject.AbstractModule",
+                  callStack[10].getClassName());
+              assertEquals("com.google.inject.spi.Elements$RecordingBinder",
+                  callStack[11].getClassName());
+              assertEquals("com.google.inject.spi.Elements",
+                  callStack[12].getClassName());
+              assertEquals("com.google.inject.spi.Elements",
+                  callStack[13].getClassName());
+              assertEquals("com.google.inject.spi.ElementSourceTest",
+                  callStack[14].getClassName());
+              // Check modules index
+              List<Integer> indexes = elementSource.getModuleConfigurePositionsInStackTrace();
+              assertEquals((int) indexes.get(0), 4);
+              assertEquals((int) indexes.get(1), 6);
+              assertEquals((int) indexes.get(2), 10);
+              return;
+          }
+        }
+      }
+    }
+    fail("The test should not reach this line.");
+  }  
+
+  private ModuleSource createModuleSource() {
+    // First module
+    StackTraceElement[] partialCallStack = new StackTraceElement[1];
+    partialCallStack[0] = BINDER_INSTALL;    
+    ModuleSource moduleSource = new ModuleSource(new A(), partialCallStack);
+    // Second module 
+    partialCallStack = new StackTraceElement[2];
+    partialCallStack[0] = BINDER_INSTALL;
+    partialCallStack[1] = new StackTraceElement(
+        "com.google.inject.spi.moduleSourceTest$A", "configure", "Unknown Source", 100);
+    moduleSource = moduleSource.createChild(new B(), partialCallStack);    
+    // Third module
+    partialCallStack = new StackTraceElement[4];
+    partialCallStack[0] = BINDER_INSTALL;
+    partialCallStack[1] = new StackTraceElement("class1", "method1", "Class1.java", 1);
+    partialCallStack[2] = new StackTraceElement("class2", "method2", "Class2.java", 2);
+    partialCallStack[3] = new StackTraceElement(
+        "com.google.inject.spi.moduleSourceTest$B", "configure", "Unknown Source", 200);
+    return moduleSource.createChild(new C(), partialCallStack);
+  }
+
+  private static class A extends AbstractModule {
+    @Override
+    public void configure() {
+      install(new B());
+    }
+  }
+  
+  private static class B implements Module {
+    @Override
+    public void configure(Binder binder) {
+      binder.install(new C());
+    }
+  }
+  
+  @Retention(RUNTIME)
+  @Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
+  @BindingAnnotation
+  @interface SampleAnnotation { }
+
+  private static class C extends AbstractModule {
+    @Override
+    public void configure() {
+      bind(String.class).annotatedWith(SampleAnnotation.class).toInstance("the value");
+    }
+  }  
+}
diff --git a/core/test/com/google/inject/spi/ElementsTest.java b/core/test/com/google/inject/spi/ElementsTest.java
index 57b844c..e2fcff9 100644
--- a/core/test/com/google/inject/spi/ElementsTest.java
+++ b/core/test/com/google/inject/spi/ElementsTest.java
@@ -18,6 +18,8 @@ package com.google.inject.spi;
 
 import static com.google.common.collect.Iterables.getOnlyElement;
 import static com.google.inject.Asserts.assertContains;
+import static com.google.inject.Asserts.getDeclaringSourcePart;
+import static com.google.inject.Asserts.isIncludeStackTraceComplete;
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
 import com.google.common.collect.ImmutableMap;
@@ -76,7 +78,7 @@ public class ElementsTest extends TestCase {
   public void testAddMessageErrorCommand() {
     checkModule(
         new AbstractModule() {
-          protected void configure() {
+          @Override protected void configure() {
             addError("Message %s %d %s", "A", 5, "C");
           }
         },
@@ -86,8 +88,9 @@ public class ElementsTest extends TestCase {
             assertEquals("Message A 5 C", command.getMessage());
             assertNull(command.getCause());
             assertContains(command.getSources().toString(),
-                ElementsTest.class.getName(), ".configure(ElementsTest.java:");
-            assertContains(command.getSource(), "ElementsTest.java");
+                ElementsTest.class.getName(),
+                getDeclaringSourcePart(ElementsTest.class));
+            assertContains(command.getSource(), getDeclaringSourcePart(ElementsTest.class));
             return null;
           }
         }
@@ -107,7 +110,7 @@ public class ElementsTest extends TestCase {
             assertEquals("A", command.getCause().getMessage());
             assertEquals(command.getMessage(),
                 "An exception was caught and reported. Message: A");
-            assertContains(command.getSource(), "ElementsTest.java");
+            assertContains(command.getSource(), getDeclaringSourcePart(ElementsTest.class));
             return null;
           }
         }
@@ -596,7 +599,7 @@ public class ElementsTest extends TestCase {
             assertEquals("Setting the scope is not permitted when binding to a single instance.",
                 command.getMessage());
             assertNull(command.getCause());
-            assertContains(command.getSource(), "ElementsTest.java");
+            assertContains(command.getSource(), getDeclaringSourcePart(ElementsTest.class));
             return null;
           }
         }
@@ -913,8 +916,8 @@ public class ElementsTest extends TestCase {
             one.expose(Collection.class).annotatedWith(SampleAnnotation.class);
             one.bind(List.class).to(ArrayList.class);
 
-            PrivateBinder two = binder().withSource("1 ElementsTest.java")
-                .newPrivateBinder().withSource("2 ElementsTest.java");
+            PrivateBinder two = binder().withSource("1 FooBar")
+                .newPrivateBinder().withSource("2 FooBar");
             two.expose(String.class).annotatedWith(Names.named("a"));
             two.expose(b);
             two.bind(List.class).to(ArrayList.class);
@@ -936,14 +939,14 @@ public class ElementsTest extends TestCase {
           }
         },
 
-        new FailingElementVisitor() {
+        new ExternalFailureVisitor() {
           @Override public Void visit(PrivateElements two) {
             assertEquals(ab, two.getExposedKeys());
-            assertEquals("1 ElementsTest.java", two.getSource());
+            assertEquals("1 FooBar", two.getSource().toString());
             checkElements(two.getElements(),
-                new FailingElementVisitor() {
+                new ExternalFailureVisitor() {
                   @Override public <T> Void visit(Binding<T> binding) {
-                    assertEquals("2 ElementsTest.java", binding.getSource());
+                    assertEquals("2 FooBar", binding.getSource().toString());
                     assertEquals(Key.get(List.class), binding.getKey());
                     return null;
                   }
@@ -976,7 +979,7 @@ public class ElementsTest extends TestCase {
             assertEquals("More than one annotation is specified for this binding.",
                 command.getMessage());
             assertNull(command.getCause());
-            assertContains(command.getSource(), "ElementsTest.java");
+            assertContains(command.getSource(), getDeclaringSourcePart(ElementsTest.class));
             return null;
           }
         }
@@ -1003,7 +1006,7 @@ public class ElementsTest extends TestCase {
           @Override public Void visit(Message command) {
             assertEquals("Implementation is set more than once.", command.getMessage());
             assertNull(command.getCause());
-            assertContains(command.getSource(), "ElementsTest.java");
+            assertContains(command.getSource(), getDeclaringSourcePart(ElementsTest.class));
             return null;
           }
         }
@@ -1030,7 +1033,7 @@ public class ElementsTest extends TestCase {
           @Override public Void visit(Message command) {
             assertEquals("Scope is set more than once.", command.getMessage());
             assertNull(command.getCause());
-            assertContains(command.getSource(), "ElementsTest.java");
+            assertContains(command.getSource(), getDeclaringSourcePart(ElementsTest.class));
             return null;
           }
         }
@@ -1058,7 +1061,7 @@ public class ElementsTest extends TestCase {
             assertEquals("More than one annotation is specified for this binding.",
                 command.getMessage());
             assertNull(command.getCause());
-            assertContains(command.getSource(), "ElementsTest.java");
+            assertContains(command.getSource(), getDeclaringSourcePart(ElementsTest.class));
             return null;
           }
         }
@@ -1085,7 +1088,7 @@ public class ElementsTest extends TestCase {
           @Override public Void visit(Message message) {
             assertEquals("Constant value is set more than once.", message.getMessage());
             assertNull(message.getCause());
-            assertContains(message.getSource(), "ElementsTest.java");
+            assertContains(message.getSource(), getDeclaringSourcePart(ElementsTest.class));
             return null;
           }
         }
@@ -1214,7 +1217,6 @@ public class ElementsTest extends TestCase {
     assertEquals(1, aConfigureCount.get());
   }
 
-
   /**
    * Ensures the module performs the commands consistent with {@code visitors}.
    */
@@ -1228,8 +1230,17 @@ public class ElementsTest extends TestCase {
     for (int i = 0; i < visitors.length; i++) {
       ElementVisitor<?> visitor = visitors[i];
       Element element = elements.get(i);
+      if (!(element instanceof Message)) {
+          ElementSource source = (ElementSource) element.getSource();
+          assertTrue(source.getModuleClassNames().size() > 0);
+          if (isIncludeStackTraceComplete()) {
+            assertTrue(source.getStackTrace().length > 0);
+          } else {
+            assertEquals(0, source.getStackTrace().length);
+          }
+      }
       if (!(visitor instanceof ExternalFailureVisitor)) {
-        assertContains(element.getSource().toString(), "ElementsTest.java");
+        assertContains(element.getSource().toString(), getDeclaringSourcePart(ElementsTest.class));
       }
       element.acceptVisitor(visitor);
     }
diff --git a/core/test/com/google/inject/spi/ModuleSourceTest.java b/core/test/com/google/inject/spi/ModuleSourceTest.java
new file mode 100644
index 0000000..b72d976
--- /dev/null
+++ b/core/test/com/google/inject/spi/ModuleSourceTest.java
@@ -0,0 +1,125 @@
+package com.google.inject.spi;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.Binder;
+import com.google.inject.Module;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests for {@link ModuleSource}.
+ */
+public class ModuleSourceTest extends TestCase {
+
+  private static final StackTraceElement BINDER_INSTALL = 
+      new StackTraceElement("com.google.inject.spi.Elements$RecordingBinder", "install", 
+          "Unknown Source", 235 /* line number*/);
+  
+  public void testOneModule() {
+    ModuleSource moduleSource = createWithSizeOne();
+    checkSizeOne(moduleSource);    
+  }
+  
+  public void testTwoModules() {
+    ModuleSource moduleSource = createWithSizeTwo();
+    checkSizeTwo(moduleSource);
+    moduleSource = moduleSource.getParent();
+    checkSizeOne(moduleSource);
+  }
+  
+  public void testThreeModules() {
+    ModuleSource moduleSource = createWithSizeThree();
+    checkSizeThree(moduleSource);
+    moduleSource = moduleSource.getParent();
+    checkSizeTwo(moduleSource);
+    moduleSource = moduleSource.getParent();
+    checkSizeOne(moduleSource);
+  }
+
+  private void checkSizeOne(ModuleSource moduleSource) {
+    assertEquals(1, moduleSource.size());
+    assertEquals(1, moduleSource.getStackTraceSize());
+    // Check call stack
+    StackTraceElement[] callStack = moduleSource.getStackTrace();
+    assertEquals(BINDER_INSTALL, callStack[0]);    
+  }
+  
+  private void checkSizeTwo(ModuleSource moduleSource) {
+    assertEquals(2, moduleSource.size());
+    assertEquals(3, moduleSource.getStackTraceSize());
+    // Check call stack
+    StackTraceElement[] callStack = moduleSource.getStackTrace();
+    assertEquals(BINDER_INSTALL, callStack[0]);    
+    assertEquals(
+        new StackTraceElement(
+            "com.google.inject.spi.moduleSourceTest$A", "configure", "Unknown Source", 100),
+        callStack[1]);
+    assertEquals(BINDER_INSTALL, callStack[2]);    
+  }
+  
+  private void checkSizeThree(ModuleSource moduleSource) {
+    assertEquals(3, moduleSource.size());
+    assertEquals(7, moduleSource.getStackTraceSize());
+    // Check call stack
+    StackTraceElement[] callStack = moduleSource.getStackTrace();
+    assertEquals(BINDER_INSTALL, callStack[0]);    
+    assertEquals(new StackTraceElement("class1", "method1", "Unknown Source", 1), callStack[1]);
+    assertEquals(new StackTraceElement("class2", "method2", "Unknown Source", 2), callStack[2]);   
+    assertEquals(
+        new StackTraceElement(
+            "com.google.inject.spi.moduleSourceTest$B", "configure", "Unknown Source", 200),
+            callStack[3]); 
+    assertEquals(BINDER_INSTALL, callStack[4]);    
+
+    assertEquals(
+        new StackTraceElement(
+            "com.google.inject.spi.moduleSourceTest$A", "configure", "Unknown Source", 100),
+        callStack[5]);
+    assertEquals(BINDER_INSTALL, callStack[6]);    
+  }
+  
+  private ModuleSource createWithSizeOne() {
+    StackTraceElement[] partialCallStack = new StackTraceElement[1];
+    partialCallStack[0] = BINDER_INSTALL;    
+    return new ModuleSource(new A(), partialCallStack);
+  }
+  
+  private ModuleSource createWithSizeTwo() {
+    ModuleSource moduleSource = createWithSizeOne();
+    StackTraceElement[] partialCallStack = new StackTraceElement[2];
+    partialCallStack[0] = BINDER_INSTALL;
+    partialCallStack[1] = new StackTraceElement(
+        "com.google.inject.spi.moduleSourceTest$A", "configure", "moduleSourceTest.java", 100);
+    return moduleSource.createChild(new B(), partialCallStack);
+  }
+    
+  private ModuleSource createWithSizeThree() {
+    ModuleSource moduleSource = createWithSizeTwo();
+    StackTraceElement[] partialCallStack = new StackTraceElement[4];
+    partialCallStack[0] = BINDER_INSTALL;
+    partialCallStack[1] = new StackTraceElement("class1", "method1", "Class1.java", 1);
+    partialCallStack[2] = new StackTraceElement("class2", "method2", "Class2.java", 2);
+    partialCallStack[3] = new StackTraceElement(
+        "com.google.inject.spi.moduleSourceTest$B", "configure", "moduleSourceTest.java", 200);
+    return moduleSource.createChild(new C(), partialCallStack);
+  }
+
+  private static class A extends AbstractModule {
+    @Override
+    public void configure() {
+      install(new B());
+    }
+  }
+  
+  private static class B implements Module {
+    @Override
+    public void configure(Binder binder) {
+      binder.install(new C());
+    }
+  }
+  
+  private static class C extends AbstractModule {
+    @Override
+    public void configure() {}
+  }
+}
diff --git a/core/test/com/google/inject/spi/ProviderMethodsTest.java b/core/test/com/google/inject/spi/ProviderMethodsTest.java
index 3c60829..8bbb8de 100644
--- a/core/test/com/google/inject/spi/ProviderMethodsTest.java
+++ b/core/test/com/google/inject/spi/ProviderMethodsTest.java
@@ -186,7 +186,7 @@ public class ProviderMethodsTest extends TestCase implements Module {
   public void testMultipleBindingAnnotations() {
     try {
       Guice.createInjector(new AbstractModule() {
-        protected void configure() {}
+        @Override protected void configure() {}
 
         @Provides @Named("A") @Blue
         public String provideString() {
@@ -229,7 +229,7 @@ public class ProviderMethodsTest extends TestCase implements Module {
       this.second = second;
     }
 
-    protected void configure() {}
+    @Override protected void configure() {}
 
     @Named("First") @Provides T provideFirst() {
       return first;
@@ -246,7 +246,7 @@ public class ProviderMethodsTest extends TestCase implements Module {
   
   public void testAutomaticProviderMethods() {
     Injector injector = Guice.createInjector((Module) new AbstractModule() {
-      protected void configure() { }
+      @Override protected void configure() { }
       private int next = 1;
 
       @Provides @Named("count")
@@ -266,7 +266,7 @@ public class ProviderMethodsTest extends TestCase implements Module {
    */
   public void testAutomaticProviderMethodsDoNotCauseDoubleBinding() {
     Module installsSelf = new AbstractModule() {
-      protected void configure() {
+      @Override protected void configure() {
         install(this);
         bind(Integer.class).toInstance(5);
       }
@@ -284,7 +284,7 @@ public class ProviderMethodsTest extends TestCase implements Module {
     final List<Number> numbers = ImmutableList.<Number>of(1, 2, 3);
 
     Injector injector = Guice.createInjector(new AbstractModule() {
-      protected void configure() {
+      @Override protected void configure() {
         @SuppressWarnings("unchecked")
         Key<List<? super Integer>> listOfSupertypesOfInteger = (Key<List<? super Integer>>)
             Key.get(Types.listOf(Types.supertypeOf(Integer.class)));
@@ -311,7 +311,7 @@ public class ProviderMethodsTest extends TestCase implements Module {
 
   public void testProviderMethodDependenciesAreExposed() {
     Injector injector = Guice.createInjector(new AbstractModule() {
-      protected void configure() {
+      @Override protected void configure() {
         bind(Integer.class).toInstance(50);
         bindConstant().annotatedWith(Names.named("units")).to("Kg");
       }
@@ -362,7 +362,7 @@ public class ProviderMethodsTest extends TestCase implements Module {
   public void testVoidProviderMethods() {
     try {
       Guice.createInjector(new AbstractModule() {
-        protected void configure() {}
+        @Override protected void configure() {}
 
         @Provides void provideFoo() {}
       });
diff --git a/core/test/com/google/inject/spi/SpiBindingsTest.java b/core/test/com/google/inject/spi/SpiBindingsTest.java
index a5fb45a..870c911 100644
--- a/core/test/com/google/inject/spi/SpiBindingsTest.java
+++ b/core/test/com/google/inject/spi/SpiBindingsTest.java
@@ -17,6 +17,8 @@
 package com.google.inject.spi;
 
 import static com.google.inject.Asserts.assertContains;
+import static com.google.inject.Asserts.getDeclaringSourcePart;
+import static com.google.inject.Asserts.isIncludeStackTraceComplete;
 
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
@@ -79,7 +81,7 @@ public class SpiBindingsTest extends TestCase {
         new FailingElementVisitor() {
           @Override public <T> Void visit(Binding<T> binding) {
             assertTrue(binding instanceof InstanceBinding);
-            assertContains(binding.getSource().toString(), "SpiBindingsTest.java");
+            checkBindingSource(binding);
             assertEquals(Key.get(String.class), binding.getKey());
             binding.acceptTargetVisitor(new FailingTargetVisitor<T>() {
               @Override public Void visit(InstanceBinding<? extends T> binding) {
@@ -111,7 +113,7 @@ public class SpiBindingsTest extends TestCase {
         new FailingElementVisitor() {
           @Override public <T> Void visit(Binding<T> binding) {
             assertTrue(binding instanceof ProviderInstanceBinding);
-            assertContains(binding.getSource().toString(), "SpiBindingsTest.java");
+            checkBindingSource(binding);
             assertEquals(Key.get(String.class), binding.getKey());
             binding.acceptTargetVisitor(new FailingTargetVisitor<T>() {
               @Override public Void visit(
@@ -137,7 +139,7 @@ public class SpiBindingsTest extends TestCase {
         new FailingElementVisitor() {
           @Override public <T> Void visit(Binding<T> binding) {
             assertTrue(binding instanceof ProviderKeyBinding);
-            assertContains(binding.getSource().toString(), "SpiBindingsTest.java");
+            checkBindingSource(binding);
             assertEquals(Key.get(String.class), binding.getKey());
             binding.acceptTargetVisitor(new FailingTargetVisitor<T>() {
               @Override public Void visit(ProviderKeyBinding<? extends T> binding) {
@@ -166,7 +168,7 @@ public class SpiBindingsTest extends TestCase {
         new FailingElementVisitor() {
           @Override public <T> Void visit(Binding<T> binding) {
             assertTrue(binding instanceof LinkedKeyBinding);
-            assertContains(binding.getSource().toString(), "SpiBindingsTest.java");
+            checkBindingSource(binding);
             assertEquals(aKey, binding.getKey());
             binding.acceptTargetVisitor(new FailingTargetVisitor<T>() {
               @Override public Void visit(LinkedKeyBinding<? extends T> binding) {
@@ -198,7 +200,7 @@ public class SpiBindingsTest extends TestCase {
         new FailingElementVisitor() {
           @Override public <T> Void visit(Binding<T> binding) {
             assertTrue(binding instanceof ConstructorBinding);
-            assertContains(binding.getSource().toString(), "SpiBindingsTest.java");
+            checkBindingSource(binding);
             assertEquals(Key.get(D.class), binding.getKey());
             binding.acceptTargetVisitor(new FailingTargetVisitor<T>() {
               @Override public Void visit(ConstructorBinding<? extends T> binding) {
@@ -225,7 +227,7 @@ public class SpiBindingsTest extends TestCase {
         new FailingElementVisitor() {
           @Override public <T> Void visit(Binding<T> binding) {
             assertTrue(binding instanceof InstanceBinding);
-            assertContains(binding.getSource().toString(), "SpiBindingsTest.java");
+            checkBindingSource(binding);
             assertEquals(Key.get(Integer.class, Names.named("one")), binding.getKey());
             binding.acceptTargetVisitor(new FailingTargetVisitor<T>() {
               @Override public Void visit(InstanceBinding<? extends T> binding) {
@@ -248,7 +250,7 @@ public class SpiBindingsTest extends TestCase {
 
     Binding<Integer> binding = injector.getBinding(Key.get(Integer.class, Names.named("one")));
     assertEquals(Key.get(Integer.class, Names.named("one")), binding.getKey());
-    assertContains(binding.getSource().toString(), "SpiBindingsTest.java");
+    checkBindingSource(binding);
     assertTrue(binding instanceof ConvertedConstantBinding);
     binding.acceptTargetVisitor(new FailingTargetVisitor<Integer>() {
       @Override public Void visit(
@@ -270,7 +272,7 @@ public class SpiBindingsTest extends TestCase {
     Key<Provider<String>> providerOfStringKey = new Key<Provider<String>>() {};
     Binding<Provider<String>> binding = injector.getBinding(providerOfStringKey);
     assertEquals(providerOfStringKey, binding.getKey());
-    assertContains(binding.getSource().toString(), "SpiBindingsTest.java");
+    checkBindingSource(binding);
     assertTrue(binding instanceof ProviderBinding);
     binding.acceptTargetVisitor(new FailingTargetVisitor<Provider<String>>() {
       @Override public Void visit(
@@ -382,7 +384,7 @@ public class SpiBindingsTest extends TestCase {
     Key<Provider<String>> providerOfStringKey = new Key<Provider<String>>() {};
     Binding<Provider<String>> providerBinding = injector.getBinding(providerOfStringKey);
     assertEquals(providerOfStringKey, providerBinding.getKey());
-    assertContains(providerBinding.getSource().toString(), "SpiBindingsTest.java");
+    checkBindingSource(providerBinding);
     assertTrue("binding: " + providerBinding, providerBinding instanceof ProviderBinding);
     providerBinding.acceptTargetVisitor(new FailingTargetVisitor<Provider<String>>() {
       @Override public Void visit(ProviderBinding<? extends Provider<String>> binding) {
@@ -394,7 +396,7 @@ public class SpiBindingsTest extends TestCase {
     // Check for String binding -- that one is ProviderInstanceBinding, and gets hooked
     Binding<String> binding = injector.getBinding(String.class);
     assertEquals(Key.get(String.class), binding.getKey());
-    assertContains(binding.getSource().toString(), "SpiBindingsTest.java");
+    checkBindingSource(binding);
     assertTrue(binding instanceof ProviderInstanceBinding);
     assertEquals("visited", binding.acceptTargetVisitor(new FailingSpiTargetVisitor<String>()));
   }
@@ -406,6 +408,17 @@ public class SpiBindingsTest extends TestCase {
     }
   }
 
+  public void checkBindingSource(Binding binding) {
+    assertContains(binding.getSource().toString(), getDeclaringSourcePart(getClass()));
+    ElementSource source = (ElementSource) binding.getSource();
+    assertTrue(source.getModuleClassNames().size() > 0);
+    if (isIncludeStackTraceComplete()) {
+      assertTrue(source.getStackTrace().length > 0);
+    } else {
+      assertEquals(0, source.getStackTrace().length);
+    }
+  }
+  
   public void checkInjector(Module module, ElementVisitor<?>... visitors) {
     Injector injector = Guice.createInjector(module);
 
diff --git a/core/test/com/google/inject/OverrideModuleTest.java b/core/test/com/google/inject/util/OverrideModuleTest.java
similarity index 80%
rename from core/test/com/google/inject/OverrideModuleTest.java
rename to core/test/com/google/inject/util/OverrideModuleTest.java
index 0f211c2..3b8e05b 100644
--- a/core/test/com/google/inject/OverrideModuleTest.java
+++ b/core/test/com/google/inject/util/OverrideModuleTest.java
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package com.google.inject;
+package com.google.inject.util;
 
+import static com.google.inject.Asserts.asModuleChain;
 import static com.google.inject.Asserts.assertContains;
 import static com.google.inject.Guice.createInjector;
 import static com.google.inject.name.Names.named;
@@ -23,6 +24,20 @@ import static java.lang.annotation.ElementType.TYPE;
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
 import com.google.common.base.Objects;
+import com.google.inject.AbstractModule;
+import com.google.inject.Binder;
+import com.google.inject.CreationException;
+import com.google.inject.Exposed;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Key;
+import com.google.inject.Module;
+import com.google.inject.PrivateModule;
+import com.google.inject.Provider;
+import com.google.inject.Provides;
+import com.google.inject.Scope;
+import com.google.inject.ScopeAnnotation;
+import com.google.inject.Stage;
 import com.google.inject.name.Named;
 import com.google.inject.util.Modules;
 
@@ -160,24 +175,33 @@ public class OverrideModuleTest extends TestCase {
     assertEquals("C3", injector.getInstance(key3));
   }
 
+  static class OuterReplacementsModule extends AbstractModule {
+    @Override protected void configure() {
+      install(new InnerReplacementsModule());
+    }
+  }
+  static class InnerReplacementsModule extends AbstractModule {
+    @Override protected void configure() {
+      bind(String.class).toInstance("B");
+      bind(String.class).toInstance("C");
+    }
+  }
   public void testOverridesTwiceFails() {
     Module original = newModule("A");
-
-    Module replacements = new AbstractModule() {
-      @Override protected void configure() {
-        bind(String.class).toInstance("B");
-        bind(String.class).toInstance("C");
-      }
-    };
-
+    Module replacements = new OuterReplacementsModule();
     Module module = Modules.override(original).with(replacements);
     try {
       createInjector(module);
       fail();
     } catch (CreationException expected) {
-      assertContains(expected.getMessage(), "A binding to java.lang.String "
-              + "was already configured at " + replacements.getClass().getName(),
-          "at " + replacements.getClass().getName());
+      assertContains(expected.getMessage(),
+          "A binding to java.lang.String was already configured at "
+              + InnerReplacementsModule.class.getName(),
+          asModuleChain(Modules.OverrideModule.class,
+              OuterReplacementsModule.class, InnerReplacementsModule.class),
+          "at " + InnerReplacementsModule.class.getName(),
+          asModuleChain(Modules.OverrideModule.class,
+              OuterReplacementsModule.class, InnerReplacementsModule.class));
     }
   }
 
@@ -200,9 +224,14 @@ public class OverrideModuleTest extends TestCase {
       createInjector(module);
       fail();
     } catch (CreationException expected) {
-      assertContains(expected.getMessage(), "1) A binding to java.lang.String "
-          + "was already configured at " + replacements.getClass().getName(),
-          "at " + original.getClass().getName());
+      // The replacement comes first because we replace A with C,
+      // then we encounter B and freak out.
+      assertContains(expected.getMessage(),
+          "1) A binding to java.lang.String was already configured at "
+              + replacements.getClass().getName(),
+          asModuleChain(Modules.OverrideModule.class, replacements.getClass()),
+          "at " + original.getClass().getName(),
+          asModuleChain(Modules.OverrideModule.class, original.getClass()));
     }
   }
 
@@ -210,7 +239,7 @@ public class OverrideModuleTest extends TestCase {
     final SingleUseScope scope = new SingleUseScope();
 
     Module module = new AbstractModule() {
-      protected void configure() {
+      @Override protected void configure() {
         bindScope(TestScopeAnnotation.class, scope);
         bind(String.class).in(TestScopeAnnotation.class);
       }
@@ -276,10 +305,16 @@ public class OverrideModuleTest extends TestCase {
       }
     };
 
-    Module original = new AbstractModule() {
+    final Module original = new AbstractModule() {
       @Override protected void configure() {
         bindScope(TestScopeAnnotation.class, scope);
         bind(Date.class).in(scope);
+        bind(String.class).in(scope);
+      }
+    };
+    Module originalWrapper = new AbstractModule() {
+      @Override protected void configure() {
+        install(original);
       }
     };
 
@@ -290,23 +325,30 @@ public class OverrideModuleTest extends TestCase {
     };
 
     try {
-      createInjector(Modules.override(original).with(replacements));
+      createInjector(Modules.override(originalWrapper).with(replacements));
       fail("Exception expected");
     } catch (CreationException e) {
       assertContains(e.getMessage(),
           "1) The scope for @TestScopeAnnotation is bound directly and cannot be overridden.",
-          "at ", getClass().getName(), ".configure(");
+          "original binding at " + original.getClass().getName() + ".configure(",
+          asModuleChain(originalWrapper.getClass(), original.getClass()),
+          "bound directly at " + original.getClass().getName() + ".configure(",
+          asModuleChain(originalWrapper.getClass(), original.getClass()),
+          "bound directly at " + original.getClass().getName() + ".configure(",
+          asModuleChain(originalWrapper.getClass(), original.getClass()),          
+          "at ", replacements.getClass().getName() + ".configure(",
+          asModuleChain(Modules.OverrideModule.class, replacements.getClass()));
     }
   }
 
   public void testOverrideIsLazy() {
     final AtomicReference<String> value = new AtomicReference<String>("A");
     Module overridden = Modules.override(new AbstractModule() {
-      protected void configure() {
+      @Override protected void configure() {
         bind(String.class).annotatedWith(named("original")).toInstance(value.get());
       }
     }).with(new AbstractModule() {
-      protected void configure() {
+      @Override protected void configure() {
         bind(String.class).annotatedWith(named("override")).toInstance(value.get());
       }
     });
@@ -320,9 +362,9 @@ public class OverrideModuleTest extends TestCase {
 
   public void testOverridePrivateModuleOverPrivateModule() {
     Module exposes5and6 = new AbstractModule() {
-      protected void configure() {
+      @Override protected void configure() {
         install(new PrivateModule() {
-          protected void configure() {
+          @Override protected void configure() {
             bind(Integer.class).toInstance(5);
             expose(Integer.class);
 
@@ -331,7 +373,7 @@ public class OverrideModuleTest extends TestCase {
         });
 
         install(new PrivateModule() {
-          protected void configure() {
+          @Override protected void configure() {
             bind(Long.class).toInstance(6L);
             expose(Long.class);
 
@@ -342,9 +384,9 @@ public class OverrideModuleTest extends TestCase {
     };
 
     AbstractModule exposes15 = new AbstractModule() {
-      protected void configure() {
+      @Override protected void configure() {
         install(new PrivateModule() {
-          protected void configure() {
+          @Override protected void configure() {
             bind(Integer.class).toInstance(15);
             expose(Integer.class);
 
@@ -353,7 +395,7 @@ public class OverrideModuleTest extends TestCase {
         });
 
         install(new PrivateModule() {
-          protected void configure() {
+          @Override protected void configure() {
             bind(Character.class).toInstance('H');
           }
         });
@@ -373,14 +415,14 @@ public class OverrideModuleTest extends TestCase {
 
   public void testOverrideModuleAndPrivateModule() {
     Module exposes5 = new PrivateModule() {
-      protected void configure() {
+      @Override protected void configure() {
         bind(Integer.class).toInstance(5);
         expose(Integer.class);
       }
     };
 
     Module binds15 = new AbstractModule() {
-      protected void configure() {
+      @Override protected void configure() {
         bind(Integer.class).toInstance(15);
       }
     };
@@ -397,9 +439,9 @@ public class OverrideModuleTest extends TestCase {
         = new AtomicReference<Provider<Character>>();
 
     Module exposes5 = new PrivateModule() {
-      protected void configure() {
+      @Override protected void configure() {
         install(new PrivateModule() {
-          protected void configure() {
+          @Override protected void configure() {
             bind(Integer.class).toInstance(5);
             expose(Integer.class);
             charAProvider.set(getProvider(Character.class));
@@ -422,11 +464,11 @@ public class OverrideModuleTest extends TestCase {
         = new AtomicReference<Provider<Character>>();
 
     Module binds15 = new AbstractModule() {
-      protected void configure() {
+      @Override protected void configure() {
         bind(Integer.class).toInstance(15);
 
         install(new PrivateModule() {
-          protected void configure() {
+          @Override protected void configure() {
             charBProvider.set(getProvider(Character.class));
             bind(Character.class).toInstance('B');
           }
@@ -458,15 +500,21 @@ public class OverrideModuleTest extends TestCase {
       return unscoped;
     }
   }
+  
+  static class NewModule<T> extends AbstractModule {
+    private final T bound;
+    NewModule(T bound) {
+      this.bound = bound;
+    }
+    @Override protected void configure() {
+      @SuppressWarnings("unchecked")
+      Class<T> type = (Class<T>)bound.getClass();
+      bind(type).toInstance(bound);
+    }
+  }
 
   private static <T> Module newModule(final T bound) {
-    return new AbstractModule() {
-      @Override protected void configure() {
-        @SuppressWarnings("unchecked")
-        Class<T> type = (Class<T>) bound.getClass();
-        bind(type).toInstance(bound);
-      }
-    };
+    return new NewModule<T>(bound);
   }
   
   private static final String RESULT = "RESULT";
@@ -610,5 +658,24 @@ public class OverrideModuleTest extends TestCase {
       return new Object();
     }
   }
-    
+
+  public void testCorrectStage() {
+    final Stage stage = Stage.PRODUCTION;
+    Module module = Modules.override(new AbstractModule() {
+      @Override
+      protected void configure() {
+        if (currentStage() != Stage.PRODUCTION) {
+          addError("Wronge stage in overridden module:" + currentStage());
+        }
+      }
+    }).with(new AbstractModule() {
+      @Override
+      protected void configure() {
+        if (currentStage() != Stage.PRODUCTION) {
+          addError("Wronge stage in overriding module:" + currentStage());
+        }
+      }
+    });
+    Guice.createInjector(stage, module);
+  }
 }
diff --git a/core/test/com/googlecode/guice/BytecodeGenTest.java b/core/test/com/googlecode/guice/BytecodeGenTest.java
index a62aacc..de4d560 100644
--- a/core/test/com/googlecode/guice/BytecodeGenTest.java
+++ b/core/test/com/googlecode/guice/BytecodeGenTest.java
@@ -38,6 +38,7 @@ import java.lang.reflect.Method;
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.net.URLClassLoader;
+import java.util.concurrent.TimeoutException;
 
 /**
  * This test is in a separate package so we can test package-level visibility
@@ -80,14 +81,14 @@ public class BytecodeGenTest extends TestCase {
     Injector injector = Guice.createInjector(interceptorModule, new PackageVisibilityTestModule());
     injector.getInstance(PublicUserOfPackagePrivate.class); // This must pass.
   }
-  
+
   public void testEnhancerNaming() {
     Injector injector = Guice.createInjector(interceptorModule, new PackageVisibilityTestModule());
     PublicUserOfPackagePrivate pupp = injector.getInstance(PublicUserOfPackagePrivate.class);
     assertTrue(pupp.getClass().getName().startsWith(
         PublicUserOfPackagePrivate.class.getName() + "$$EnhancerByGuice$$"));
   }
-  
+
   // TODO(sameb): Figure out how to test FastClass naming tests.
 
   /**
@@ -228,7 +229,7 @@ public class BytecodeGenTest extends TestCase {
       assertNotSame(testProxy.getClass().getClassLoader(), systemClassLoader);
     }
   }
-  
+
   public void testProxyClassUnloading() {
     Object testObject = Guice.createInjector(interceptorModule, testModule)
         .getInstance(proxyTestClass);
@@ -247,24 +248,21 @@ public class BytecodeGenTest extends TestCase {
      * this should be enough to queue the weak reference
      * unless something is holding onto it accidentally.
      */
+    final int MAX_COUNT = 100;
     String[] buf;
     System.gc();
-    buf = new String[8 * 1024 * 1024];
-    buf = null;
-    System.gc();
-    buf = new String[8 * 1024 * 1024];
-    buf = null;
-    System.gc();
-    buf = new String[8 * 1024 * 1024];
-    buf = null;
-    System.gc();
-    buf = new String[8 * 1024 * 1024];
-    buf = null;
-    System.gc();
+    //TODO(cgruber): Use com.google.common.testing.GcFinalization and a countdown latch to un-flake.
+    for (int count = 0 ; clazzRef.get() != null ; count++) {
+      buf = new String[8 * 1024 * 1024];
+      buf = null;
+      System.gc();
+      assertTrue("Timeout waiting for class to be unloaded.  This may be a flaky result.",
+          count <= MAX_COUNT);
+    }
 
     // This test could be somewhat flaky when the GC isn't working.
     // If it fails, run the test again to make sure it's failing reliably.
-    assertNull(clazzRef.get());
+    assertNull("Proxy class was not unloaded.", clazzRef.get());
   }
 
   public void testProxyingPackagePrivateMethods() {
diff --git a/core/test/com/googlecode/guice/Jsr330Test.java b/core/test/com/googlecode/guice/Jsr330Test.java
index 32e331f..57cd6e0 100644
--- a/core/test/com/googlecode/guice/Jsr330Test.java
+++ b/core/test/com/googlecode/guice/Jsr330Test.java
@@ -442,11 +442,15 @@ public class Jsr330Test extends TestCase {
   }
 
   static class L {
-    @Inject final B b = null;
+    @SuppressWarnings("InjectJavaxInjectOnFinalField")
+    @Inject
+    final B b = null;
   }
 
   static abstract class AbstractM {
-    @Inject abstract void setB(B b);
+    @SuppressWarnings("InjectJavaxInjectOnAbstractMethod")
+    @Inject
+    abstract void setB(B b);
   }
 
   static class M extends AbstractM {
diff --git a/core/test/com/googlecode/guice/OSGiContainerTest.java b/core/test/com/googlecode/guice/OSGiContainerTest.java
index 068a541..6cb2b1b 100644
--- a/core/test/com/googlecode/guice/OSGiContainerTest.java
+++ b/core/test/com/googlecode/guice/OSGiContainerTest.java
@@ -33,7 +33,6 @@ import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.util.Iterator;
-import java.util.Map;
 import java.util.Properties;
 
 import javax.imageio.spi.ServiceRegistry;
@@ -59,6 +58,7 @@ public class OSGiContainerTest
   static final String AOPALLIANCE_JAR = System.getProperty("aopalliance.jar", "lib/aopalliance.jar");
 /*end[AOP]*/
   static final String JAVAX_INJECT_JAR = System.getProperty("javax.inject.jar", "lib/javax.inject.jar");
+  static final String GUAVA_JAR = System.getProperty("guava.jar", "lib/guava-11.0.2.jar");
 
   // dynamically build test bundles
   @Override protected void setUp()
@@ -72,6 +72,7 @@ public class OSGiContainerTest
     assertTrue(failMsg(), new File(AOPALLIANCE_JAR).isFile());
 /*end[AOP]*/
     assertTrue(failMsg(), new File(JAVAX_INJECT_JAR).isFile());
+    assertTrue(failMsg(), new File(GUAVA_JAR).isFile());
 
     Properties instructions = new Properties();
 
@@ -87,6 +88,12 @@ public class OSGiContainerTest
     buildBundle("javax.inject", instructions, JAVAX_INJECT_JAR);
     instructions.clear();
 
+    // early versions of guava did not ship with OSGi metadata
+    instructions.setProperty("Export-Package", "com.google.common.*");
+    instructions.setProperty("Import-Package", "*;resolution:=optional");
+    buildBundle("guava", instructions, GUAVA_JAR);
+    instructions.clear();
+
     // strict imports to make sure test bundle only has access to these packages
     instructions.setProperty("Import-Package", "org.osgi.framework,"
 /*if[AOP]*/
@@ -135,7 +142,7 @@ public class OSGiContainerTest
     // test each available OSGi framework in turn
     Iterator<FrameworkFactory> f = ServiceRegistry.lookupProviders(FrameworkFactory.class);
     while (f.hasNext()) {
-      Framework framework = f.next().newFramework((Map)properties);
+      Framework framework = f.next().newFramework(properties);
 
       framework.start();
       BundleContext systemContext = framework.getBundleContext();
@@ -145,6 +152,7 @@ public class OSGiContainerTest
       systemContext.installBundle("reference:file:" + BUILD_TEST_DIR + "/aopalliance.jar");
 /*end[AOP]*/
       systemContext.installBundle("reference:file:" + BUILD_TEST_DIR + "/javax.inject.jar");
+      systemContext.installBundle("reference:file:" + BUILD_TEST_DIR + "/guava.jar");
       systemContext.installBundle("reference:file:" + GUICE_JAR);
       systemContext.installBundle("reference:file:" + BUILD_TEST_DIR + "/osgitests.jar").start();
 
diff --git a/debian/changelog b/debian/changelog
index 7e715c4..7fe2c0c 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,6 +1,6 @@
-sisu-guice (3.1.1+dfsg-2) unstable; urgency=low
+sisu-guice (3.1.9+dfsg-1) unstable; urgency=low
 
-  * Team upload.
+  * New upstream release.
   * jh_repack in d/watch replaced with d/orig-tar.sh to exclude folders
     javadoc, latest-javadoc, latest-api-diffs (Closes: #737464).
 
diff --git a/debian/control b/debian/control
index 5cff128..6e8ae3e 100644
--- a/debian/control
+++ b/debian/control
@@ -2,7 +2,7 @@ Source: sisu-guice
 Section: java
 Priority: optional
 Maintainer: Debian Java Maintainers <pkg-java-maintainers at lists.alioth.debian.org>
-Uploaders: Damien Raude-Morvan <drazzib at debian.org>
+Uploaders: Damien Raude-Morvan <drazzib at debian.org>, Eugene Zhukov <jevgeni.zh at gmail.com>
 Build-Depends: cdbs, debhelper (>= 7), default-jdk, maven-debian-helper (>= 1.4)
 Build-Depends-Indep: default-jdk-doc,
                      glassfish-javaee,
diff --git a/debian/maven.rules b/debian/maven.rules
index b143fc7..2594d7b 100644
--- a/debian/maven.rules
+++ b/debian/maven.rules
@@ -2,6 +2,7 @@
 com.google.guava guava bundle s/.*/debian/ * *
 com.google.guava guava s/jar/bundle/ s/.*/debian/ * *
 javax.servlet servlet-api jar s/.*/2.5/ * *
+org.ow2.asm asm jar s/.*/4.1/ * *
 org.sonatype.sisu sisu-guice jar s/.*/debian/ * *
 org.sonatype.sisu.inject guice-assistedinject jar s/.*/debian/ * *
 org.sonatype.sisu.inject guice-extensions pom s/.*/debian/ * *
@@ -16,6 +17,7 @@ org.sonatype.sisu.inject guice-spring jar s/.*/debian/ * *
 org.sonatype.sisu.inject guice-struts2 jar s/.*/debian/ * *
 org.sonatype.sisu.inject guice-throwingproviders jar s/.*/debian/ * *
 s/easymock/org.easymock/ easymock * s/.*/debian/ * *
+org.ow2.asm * * s/4\..*/4.x/ * *
 org.springframework * * s/3\..*/3.x/ * *
 s/com.google.code.google-collections/com.google.guava/ s/google-collect/guava/ s/jar/bundle/ s/.*/debian/ * *
 s/com.google.collections/com.google.guava/ s/google-collections/guava/ s/jar/bundle/ s/.*/debian/ * *
diff --git a/extensions/assistedinject/build.xml b/extensions/assistedinject/build.xml
index 16557de..459ff9e 100644
--- a/extensions/assistedinject/build.xml
+++ b/extensions/assistedinject/build.xml
@@ -10,11 +10,10 @@
     <pathelement path="../../build/classes"/>
   </path>
 
-  <target name="jar" depends="jar.withrenameddeps, manifest" description="Build jar.">
+  <target name="jar" depends="compile, manifest" description="Build jar.">
     <jar destfile="${build.dir}/${ant.project.name}-${version}.jar"
         manifest="${build.dir}/META-INF/MANIFEST.MF">
-      <zipfileset src="${build.dir}/${ant.project.name}-with-deps.jar"
-          excludes="com/google/inject/internal/**"/>
+      <fileset dir="${build.dir}/classes"/>
     </jar>
   </target>
 
diff --git a/extensions/assistedinject/pom.xml b/extensions/assistedinject/pom.xml
index d0d7371..a49e752 100644
--- a/extensions/assistedinject/pom.xml
+++ b/extensions/assistedinject/pom.xml
@@ -5,8 +5,8 @@
 
   <parent>
     <groupId>org.sonatype.sisu.inject</groupId>
-    <artifactId>guice-extensions</artifactId>
-    <version>3.1.1</version>
+    <artifactId>extensions-parent</artifactId>
+    <version>3.1.9</version>
   </parent>
 
   <artifactId>guice-assistedinject</artifactId>
diff --git a/extensions/assistedinject/src/com/google/inject/assistedinject/FactoryProvider2.java b/extensions/assistedinject/src/com/google/inject/assistedinject/FactoryProvider2.java
index 0485a96..a77f1f3 100644
--- a/extensions/assistedinject/src/com/google/inject/assistedinject/FactoryProvider2.java
+++ b/extensions/assistedinject/src/com/google/inject/assistedinject/FactoryProvider2.java
@@ -25,6 +25,7 @@ import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
 import com.google.inject.AbstractModule;
 import com.google.inject.Binder;
 import com.google.inject.Binding;
@@ -41,6 +42,7 @@ import com.google.inject.internal.Annotations;
 import com.google.inject.internal.BytecodeGen;
 import com.google.inject.internal.Errors;
 import com.google.inject.internal.ErrorsException;
+import com.google.inject.internal.UniqueAnnotations;
 import com.google.inject.internal.util.Classes;
 import com.google.inject.spi.BindingTargetVisitor;
 import com.google.inject.spi.Dependency;
@@ -65,6 +67,8 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
 /**
  * The newer implementation of factory provider. This implementation uses a child injector to
@@ -78,6 +82,12 @@ import java.util.Set;
 final class FactoryProvider2 <F> implements InvocationHandler,
     ProviderWithExtensionVisitor<F>, HasDependencies, AssistedInjectBinding<F> {
 
+  /** A constant annotation to denote the return value, instead of creating a new one each time. */
+  static final Annotation RETURN_ANNOTATION = UniqueAnnotations.create();
+
+  // use the logger under a well-known name, not FactoryProvider2
+  static final Logger logger = Logger.getLogger(AssistedInject.class.getName());
+
   /** if a factory method parameter isn't annotated, it gets this annotation. */
   static final Assisted DEFAULT_ANNOTATION = new Assisted() {
     public String value() {
@@ -190,7 +200,7 @@ final class FactoryProvider2 <F> implements InvocationHandler,
    */
   FactoryProvider2(Key<F> factoryKey, BindingCollector collector) {
     this.factoryKey = factoryKey;
-    
+
     TypeLiteral<F> factoryType = factoryKey.getTypeLiteral();
     Errors errors = new Errors();
 
@@ -241,6 +251,15 @@ final class FactoryProvider2 <F> implements InvocationHandler,
         if(implementation == null) {
           implementation = returnType.getTypeLiteral();
         }
+        Class<? extends Annotation> scope =
+            Annotations.findScopeAnnotation(errors, implementation.getRawType());
+        if (scope != null) {
+          errors.addMessage("Found scope annotation [%s] on implementation class "
+              + "[%s] of AssistedInject factory [%s].\nThis is not allowed, please"
+              + " remove the scope annotation.",
+              scope, implementation.getRawType(), factoryType);
+        }
+        
         InjectionPoint ctorInjectionPoint;
         try {
           ctorInjectionPoint =
@@ -259,7 +278,7 @@ final class FactoryProvider2 <F> implements InvocationHandler,
         // all injections directly inject the object itself (and not a Provider of the object,
         // or an Injector), because it caches a single child injector and mutates the Provider
         // of the arguments in a ThreadLocal.
-        if(isValidForOptimizedAssistedInject(deps)) {
+        if(isValidForOptimizedAssistedInject(deps, implementation.getRawType(), factoryType)) {
           ImmutableList.Builder<ThreadLocalProvider> providerListBuilder = ImmutableList.builder();
           for(int i = 0; i < params.size(); i++) {
             providerListBuilder.add(new ThreadLocalProvider());
@@ -316,7 +335,7 @@ final class FactoryProvider2 <F> implements InvocationHandler,
     }
     return visitor.visit(binding);
   }
-  
+
   private void validateFactoryReturnType(Errors errors, Class<?> returnType, Class<?> factoryType) {
     if (Modifier.isPublic(factoryType.getModifiers())
         && !Modifier.isPublic(returnType.getModifiers())) {
@@ -487,12 +506,26 @@ final class FactoryProvider2 <F> implements InvocationHandler,
    * the assisted bindings are immediately provided. This looks for hints that the values may be
    * lazily retrieved, by looking for injections of Injector or a Provider for the assisted values.
    */
-  private boolean isValidForOptimizedAssistedInject(Set<Dependency<?>> dependencies) {
+  private boolean isValidForOptimizedAssistedInject(Set<Dependency<?>> dependencies,
+      Class<?> implementation, TypeLiteral<?> factoryType) {
+    Set<Dependency<?>> badDeps = null; // optimization: create lazily
     for (Dependency<?> dep : dependencies) {
       if (isInjectorOrAssistedProvider(dep)) {
-        return false;
+        if (badDeps == null) {
+          badDeps = Sets.newHashSet();
+        }
+        badDeps.add(dep);
       }
     }
+    if (badDeps != null && !badDeps.isEmpty()) {
+      logger.log(Level.WARNING, "AssistedInject factory {0} will be slow "
+          + "because {1} has assisted Provider dependencies or injects the Injector. "
+          + "Stop injecting @Assisted Provider<T> (instead use @Assisted T) "
+          + "or Injector to speed things up. (It will be a ~6500% speed bump!)  "
+          + "The exact offending deps are: {2}",
+          new Object[] {factoryType, implementation, badDeps} );
+      return false;
+    }
     return true;
   }
 
@@ -567,7 +600,7 @@ final class FactoryProvider2 <F> implements InvocationHandler,
     final Key<?> returnType = data.returnType;
 
     // We ignore any pre-existing binding annotation.
-    final Key<?> assistedReturnType = Key.get(returnType.getTypeLiteral(), Assisted.class);
+    final Key<?> returnKey = Key.get(returnType.getTypeLiteral(), RETURN_ANNOTATION);
 
     Module assistedModule = new AbstractModule() {
       @Override @SuppressWarnings("unchecked") // raw keys are necessary for the args array and return value
@@ -592,7 +625,7 @@ final class FactoryProvider2 <F> implements InvocationHandler,
         // but if it isn't, we'll end up throwing a fairly good error
         // message for the user.
         if(constructor != null) {
-          binder.bind(assistedReturnType)
+          binder.bind(returnKey)
               .toConstructor(constructor, (TypeLiteral)data.implementationType)
               .in(Scopes.NO_SCOPE); // make sure we erase any scope on the implementation type
         }
@@ -600,7 +633,7 @@ final class FactoryProvider2 <F> implements InvocationHandler,
     };
 
     Injector forCreate = injector.createChildInjector(assistedModule);
-    Binding binding = forCreate.getBinding(assistedReturnType);
+    Binding binding = forCreate.getBinding(returnKey);
     // If we have providers cached in data, cache the binding for future optimizations.
     if(data.optimized) {
       data.cachedBinding = binding;
diff --git a/extensions/assistedinject/test/com/google/inject/assistedinject/FactoryModuleBuilderTest.java b/extensions/assistedinject/test/com/google/inject/assistedinject/FactoryModuleBuilderTest.java
index 39f039c..0a2836f 100644
--- a/extensions/assistedinject/test/com/google/inject/assistedinject/FactoryModuleBuilderTest.java
+++ b/extensions/assistedinject/test/com/google/inject/assistedinject/FactoryModuleBuilderTest.java
@@ -462,22 +462,29 @@ public class FactoryModuleBuilderTest extends TestCase {
   }
   
   public void testSingletonScopeOnAssistedClassIsIgnored() {
-    // production stage is important, because it will trigger eager singleton creation
-    Injector injector = Guice.createInjector(Stage.PRODUCTION, new AbstractModule() {
-      @Override
-      protected void configure() {
-        install(new FactoryModuleBuilder().build(SingletonFactory.class));
-      }
-    });
-    
-    SingletonFactory factory = injector.getInstance(SingletonFactory.class);
-    assertNotSame(factory.create("foo"), factory.create("bar"));
+    try {
+      Guice.createInjector(new AbstractModule() {
+        @Override
+        protected void configure() {
+          install(new FactoryModuleBuilder().build(SingletonFactory.class));
+        }
+      });
+      fail();
+    } catch (CreationException ce) {
+      assertEquals(1, ce.getErrorMessages().size());
+      assertEquals("Found scope annotation [" + Singleton.class.getName() + "]"
+          + " on implementation class [" + AssistedSingleton.class.getName() + "]"
+          + " of AssistedInject factory [" + SingletonFactory.class.getName() + "]."
+          + "\nThis is not allowed, please remove the scope annotation.",
+          Iterables.getOnlyElement(ce.getErrorMessages()).getMessage());
+    }
   }
   
   interface SingletonFactory {
     AssistedSingleton create(String string);
   }
-  
+
+  @SuppressWarnings("GuiceAssistedInjectScoping")
   @Singleton
   static class AssistedSingleton {
     @Inject
diff --git a/extensions/assistedinject/test/com/google/inject/assistedinject/FactoryProvider2Test.java b/extensions/assistedinject/test/com/google/inject/assistedinject/FactoryProvider2Test.java
index 4bce4a1..c7fccc3 100644
--- a/extensions/assistedinject/test/com/google/inject/assistedinject/FactoryProvider2Test.java
+++ b/extensions/assistedinject/test/com/google/inject/assistedinject/FactoryProvider2Test.java
@@ -575,22 +575,28 @@ public class FactoryProvider2Test extends TestCase {
       });
       fail();
     } catch (CreationException expected) {
+      assertEquals(expected.getMessage(), 4, expected.getErrorMessages().size());
+      // Assert each method individually, because JDK7 doesn't guarantee method ordering.
       assertContains(expected.getMessage(),
-          "1) A Provider may not be a type in a factory method of an AssistedInject."
+          ") A Provider may not be a type in a factory method of an AssistedInject."
             + "\n  Offending instance is parameter [1] with key"
             + " [com.google.inject.Provider<" + Color.class.getName() + ">] on method ["
-            + ProviderBasedColoredCarFactory.class.getName() + ".createCar()]",
-          "2) A Provider may not be a type in a factory method of an AssistedInject."
+            + ProviderBasedColoredCarFactory.class.getName() + ".createCar()]");
+      assertContains(expected.getMessage(),
+          ") A Provider may not be a type in a factory method of an AssistedInject."
             + "\n  Offending instance is parameter [2] with key"
             + " [com.google.inject.Provider<java.lang.String>] on method ["
-            + ProviderBasedColoredCarFactory.class.getName() + ".createCar()]",
-          "3) A Provider may not be a type in a factory method of an AssistedInject."
+            + ProviderBasedColoredCarFactory.class.getName() + ".createCar()]");
+      assertContains(expected.getMessage(),
+          ") A Provider may not be a type in a factory method of an AssistedInject."
             + "\n  Offending instance is parameter [1] with key"
             + " [com.google.inject.Provider<" + Color.class.getName() + ">"
             + " annotated with @com.google.inject.assistedinject.Assisted(value=color)]"
             + " on method [" + ProviderBasedColoredCarFactory.class.getName() + ".createMustang()]"
       );
-      
+      assertContains(expected.getMessage(),
+          ") No implementation for com.google.inject.assistedinject."
+            + "FactoryProvider2Test$ProviderBasedColoredCarFactory was bound.");
     }
   }
 
@@ -609,21 +615,27 @@ public class FactoryProvider2Test extends TestCase {
       });
       fail();
     } catch (CreationException expected) {
+      assertEquals(expected.getMessage(), 4, expected.getErrorMessages().size());
       assertContains(expected.getMessage(),
-          "1) A Provider may not be a type in a factory method of an AssistedInject."
+          ") A Provider may not be a type in a factory method of an AssistedInject."
             + "\n  Offending instance is parameter [1] with key"
             + " [com.google.inject.Provider<" + Color.class.getName() + ">] on method ["
-            + JavaxProviderBasedColoredCarFactory.class.getName() + ".createCar()]",
-          "2) A Provider may not be a type in a factory method of an AssistedInject."
+            + JavaxProviderBasedColoredCarFactory.class.getName() + ".createCar()]");
+      assertContains(expected.getMessage(),
+          ") A Provider may not be a type in a factory method of an AssistedInject."
             + "\n  Offending instance is parameter [2] with key"
             + " [com.google.inject.Provider<java.lang.String>] on method ["
-            + JavaxProviderBasedColoredCarFactory.class.getName() + ".createCar()]",
-          "3) A Provider may not be a type in a factory method of an AssistedInject."
+            + JavaxProviderBasedColoredCarFactory.class.getName() + ".createCar()]");
+      assertContains(expected.getMessage(),
+          ") A Provider may not be a type in a factory method of an AssistedInject."
             + "\n  Offending instance is parameter [1] with key"
             + " [com.google.inject.Provider<" + Color.class.getName() + ">"
             + " annotated with @com.google.inject.assistedinject.Assisted(value=color)]"
             + " on method [" + JavaxProviderBasedColoredCarFactory.class.getName() + ".createMustang()]"
       );
+      assertContains(expected.getMessage(),
+          ") No implementation for com.google.inject.assistedinject."
+            + "FactoryProvider2Test$JavaxProviderBasedColoredCarFactory was bound.");
     }
   }
 
@@ -1024,9 +1036,35 @@ public class FactoryProvider2Test extends TestCase {
   
   static class Segway implements Car {
     @Inject Injector injector;
-    
+
     Color getColor() { return injector.getInstance(Key.get(Color.class, FactoryProvider2.DEFAULT_ANNOTATION)); }
   }
-  
-  
+
+  public void testReturnValueMatchesParamValue() {
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      @Override
+      public void configure() {
+        install(new FactoryModuleBuilder().build(Delegater.Factory.class));
+      }
+    });
+    Delegater delegate = new Delegater();
+    Delegater user = injector.getInstance(Delegater.Factory.class).create(delegate);
+    assertSame(delegate, user.delegate);
+  }
+
+  static class Delegater {
+    interface Factory {
+      Delegater create(Delegater delegate);
+    }
+
+    private final Delegater delegate;
+
+    @Inject Delegater(@Assisted Delegater delegater) {
+      this.delegate = delegater;
+    }
+
+    Delegater() {
+      this.delegate = null;
+    }
+  }
 }
diff --git a/extensions/grapher/build.xml b/extensions/grapher/build.xml
index d36fdcd..d5524dd 100644
--- a/extensions/grapher/build.xml
+++ b/extensions/grapher/build.xml
@@ -10,11 +10,10 @@
     <pathelement path="../../build/classes"/>
   </path>
 
-  <target name="jar" depends="jar.withrenameddeps, manifest" description="Build jar.">
+  <target name="jar" depends="compile, manifest" description="Build jar.">
     <jar destfile="${build.dir}/${ant.project.name}-${version}.jar"
         manifest="${build.dir}/META-INF/MANIFEST.MF">
-      <zipfileset src="${build.dir}/${ant.project.name}-with-deps.jar"
-          excludes="com/google/inject/internal/**"/>
+      <fileset dir="${build.dir}/classes"/>
     </jar>
   </target>
 
diff --git a/extensions/grapher/pom.xml b/extensions/grapher/pom.xml
index 9dfa48d..d0221af 100644
--- a/extensions/grapher/pom.xml
+++ b/extensions/grapher/pom.xml
@@ -5,8 +5,8 @@
 
   <parent>
     <groupId>org.sonatype.sisu.inject</groupId>
-    <artifactId>guice-extensions</artifactId>
-    <version>3.1.1</version>
+    <artifactId>extensions-parent</artifactId>
+    <version>3.1.9</version>
   </parent>
 
   <artifactId>guice-grapher</artifactId>
diff --git a/extensions/grapher/src/com/google/inject/grapher/ShortNameFactory.java b/extensions/grapher/src/com/google/inject/grapher/ShortNameFactory.java
index 1bf6b3e..e8c6d57 100644
--- a/extensions/grapher/src/com/google/inject/grapher/ShortNameFactory.java
+++ b/extensions/grapher/src/com/google/inject/grapher/ShortNameFactory.java
@@ -22,6 +22,7 @@ import com.google.inject.Key;
 import com.google.inject.TypeLiteral;
 import com.google.inject.internal.ProviderMethod;
 import com.google.inject.internal.util.StackTraceElements;
+import com.google.inject.spi.ElementSource;
 
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Constructor;
@@ -98,6 +99,9 @@ public class ShortNameFactory implements NameFactory {
    * or a {@link Method} when a provider method is used.
    */
   public String getSourceName(Object source) {
+    if (source instanceof ElementSource) {
+      source = ((ElementSource) source).getDeclaringSource();
+    }
     if (source instanceof Method) {
       source = StackTraceElements.forMember((Method) source);
     }
diff --git a/extensions/grapher/src/com/google/inject/grapher/graphviz/GraphvizGrapher.java b/extensions/grapher/src/com/google/inject/grapher/graphviz/GraphvizGrapher.java
index c9daaea..8614a2a 100644
--- a/extensions/grapher/src/com/google/inject/grapher/graphviz/GraphvizGrapher.java
+++ b/extensions/grapher/src/com/google/inject/grapher/graphviz/GraphvizGrapher.java
@@ -249,7 +249,7 @@ public class GraphvizGrapher extends AbstractInjectorGrapher {
   @Override protected void newImplementationNode(ImplementationNode node) {
     NodeId nodeId = node.getId();
     GraphvizNode gnode = new GraphvizNode(nodeId);
-    gnode.setStyle(NodeStyle.INVISIBLE);
+    gnode.setStyle(NodeStyle.SOLID);
 
     gnode.setHeaderBackgroundColor("#000000");
     gnode.setHeaderTextColor("#ffffff");
@@ -265,7 +265,7 @@ public class GraphvizGrapher extends AbstractInjectorGrapher {
   @Override protected void newInstanceNode(InstanceNode node) {
     NodeId nodeId = node.getId();
     GraphvizNode gnode = new GraphvizNode(nodeId);
-    gnode.setStyle(NodeStyle.INVISIBLE);
+    gnode.setStyle(NodeStyle.SOLID);
 
     gnode.setHeaderBackgroundColor("#000000");
     gnode.setHeaderTextColor("#ffffff");
diff --git a/extensions/jmx/build.xml b/extensions/jmx/build.xml
index 1bdcb97..8a99b77 100644
--- a/extensions/jmx/build.xml
+++ b/extensions/jmx/build.xml
@@ -10,11 +10,10 @@
     <pathelement path="../../build/classes"/>
   </path>
 
-  <target name="jar" depends="jar.withrenameddeps, manifest" description="Build jar.">
+  <target name="jar" depends="compile, manifest" description="Build jar.">
     <jar destfile="${build.dir}/${ant.project.name}-${version}.jar"
         manifest="${build.dir}/META-INF/MANIFEST.MF">
-      <zipfileset src="${build.dir}/${ant.project.name}-with-deps.jar"
-          excludes="com/google/inject/internal/**"/>
+      <fileset dir="${build.dir}/classes"/>
     </jar>
   </target>
 
diff --git a/extensions/jmx/pom.xml b/extensions/jmx/pom.xml
index 2f26eeb..27a4ca8 100644
--- a/extensions/jmx/pom.xml
+++ b/extensions/jmx/pom.xml
@@ -5,8 +5,8 @@
 
   <parent>
     <groupId>org.sonatype.sisu.inject</groupId>
-    <artifactId>guice-extensions</artifactId>
-    <version>3.1.1</version>
+    <artifactId>extensions-parent</artifactId>
+    <version>3.1.9</version>
   </parent>
 
   <artifactId>guice-jmx</artifactId>
diff --git a/extensions/jndi/build.xml b/extensions/jndi/build.xml
index 14eaa8b..5c366a2 100644
--- a/extensions/jndi/build.xml
+++ b/extensions/jndi/build.xml
@@ -10,11 +10,10 @@
     <pathelement path="../../build/classes"/>
   </path>
 
-  <target name="jar" depends="jar.withrenameddeps, manifest" description="Build jar.">
+  <target name="jar" depends="compile, manifest" description="Build jar.">
     <jar destfile="${build.dir}/${ant.project.name}-${version}.jar"
         manifest="${build.dir}/META-INF/MANIFEST.MF">
-      <zipfileset src="${build.dir}/${ant.project.name}-with-deps.jar"
-          excludes="com/google/inject/internal/**"/>
+      <fileset dir="${build.dir}/classes"/>
     </jar>
   </target>
 
diff --git a/extensions/jndi/pom.xml b/extensions/jndi/pom.xml
index 631959b..a04ff5f 100644
--- a/extensions/jndi/pom.xml
+++ b/extensions/jndi/pom.xml
@@ -5,8 +5,8 @@
 
   <parent>
     <groupId>org.sonatype.sisu.inject</groupId>
-    <artifactId>guice-extensions</artifactId>
-    <version>3.1.1</version>
+    <artifactId>extensions-parent</artifactId>
+    <version>3.1.9</version>
   </parent>
 
   <artifactId>guice-jndi</artifactId>
diff --git a/extensions/jndi/test/placeholder.txt b/extensions/jndi/test/placeholder.txt
index e69de29..8d1c8b6 100644
--- a/extensions/jndi/test/placeholder.txt
+++ b/extensions/jndi/test/placeholder.txt
@@ -0,0 +1 @@
+ 
diff --git a/extensions/mini/pom.xml b/extensions/mini/pom.xml
index 69e979b..213a9ee 100644
--- a/extensions/mini/pom.xml
+++ b/extensions/mini/pom.xml
@@ -5,8 +5,8 @@
 
   <parent>
     <groupId>org.sonatype.sisu.inject</groupId>
-    <artifactId>guice-extensions</artifactId>
-    <version>3.0-SNAPSHOT</version>
+    <artifactId>extensions-parent</artifactId>
+    <version>3.2.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>guice-mini</artifactId>
diff --git a/extensions/multibindings/build.xml b/extensions/multibindings/build.xml
index 7a23462..2f72159 100644
--- a/extensions/multibindings/build.xml
+++ b/extensions/multibindings/build.xml
@@ -10,11 +10,10 @@
     <pathelement path="../../build/classes"/>
   </path>
 
-  <target name="jar" depends="jar.withrenameddeps, manifest" description="Build jar.">
+  <target name="jar" depends="compile, manifest" description="Build jar.">
     <jar destfile="${build.dir}/${ant.project.name}-${version}.jar"
         manifest="${build.dir}/META-INF/MANIFEST.MF">
-      <zipfileset src="${build.dir}/${ant.project.name}-with-deps.jar"
-          excludes="com/google/inject/internal/**"/>
+      <fileset dir="${build.dir}/classes"/>
     </jar>
   </target>
 
diff --git a/extensions/multibindings/pom.xml b/extensions/multibindings/pom.xml
index b555ea2..6b61b69 100644
--- a/extensions/multibindings/pom.xml
+++ b/extensions/multibindings/pom.xml
@@ -5,8 +5,8 @@
 
   <parent>
     <groupId>org.sonatype.sisu.inject</groupId>
-    <artifactId>guice-extensions</artifactId>
-    <version>3.1.1</version>
+    <artifactId>extensions-parent</artifactId>
+    <version>3.1.9</version>
   </parent>
 
   <artifactId>guice-multibindings</artifactId>
diff --git a/extensions/multibindings/src/com/google/inject/multibindings/Element.java b/extensions/multibindings/src/com/google/inject/multibindings/Element.java
index 3149865..3f68068 100644
--- a/extensions/multibindings/src/com/google/inject/multibindings/Element.java
+++ b/extensions/multibindings/src/com/google/inject/multibindings/Element.java
@@ -25,13 +25,23 @@ import java.lang.annotation.Retention;
 
 /**
  * An internal binding annotation applied to each element in a multibinding.
- * All elements are assigned a globally-unique id to allow different modules
- * to contribute multibindings independently.
+ * Breaks the Java annotation rules for equals and hashCode, instead defining
+ * equality based on whether the associated bindings match. This allows
+ * different modules to contribute multibindings independently, while still
+ * supporting SPI-based module manipulations like
+ * {@link com.google.inject.util.Modules#override Modules.override}.
  *
  * @author jessewilson at google.com (Jesse Wilson)
  */
 @Retention(RUNTIME) @BindingAnnotation
 @interface Element {
+
+  enum Type {
+    MAPBINDER,
+    MULTIBINDER;
+  }
+
   String setName();
   int uniqueId();
+  Type type();
 }
diff --git a/extensions/multibindings/src/com/google/inject/multibindings/MapBinder.java b/extensions/multibindings/src/com/google/inject/multibindings/MapBinder.java
index 9a15efd..d423597 100644
--- a/extensions/multibindings/src/com/google/inject/multibindings/MapBinder.java
+++ b/extensions/multibindings/src/com/google/inject/multibindings/MapBinder.java
@@ -16,15 +16,20 @@
 
 package com.google.inject.multibindings;
 
+import static com.google.inject.internal.RehashableKeys.Keys.needsRehashing;
+import static com.google.inject.internal.RehashableKeys.Keys.rehash;
+import static com.google.inject.multibindings.Element.Type.MAPBINDER;
 import static com.google.inject.multibindings.Multibinder.checkConfiguration;
 import static com.google.inject.multibindings.Multibinder.checkNotNull;
 import static com.google.inject.multibindings.Multibinder.setOf;
+import static com.google.inject.util.Types.newParameterizedType;
 import static com.google.inject.util.Types.newParameterizedTypeWithOwner;
 
 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.Binder;
 import com.google.inject.Binding;
 import com.google.inject.Inject;
@@ -93,7 +98,7 @@ import java.util.Set;
  * that module can order its bindings appropriately. Avoid relying on the
  * iteration order of elements contributed by different modules, since there is
  * no equivalent mechanism to order modules.
- * 
+ *
  * <p>The map is unmodifiable.  Elements can only be added to the map by
  * configuring the MapBinder.  Elements can never be removed from the map.
  *
@@ -207,6 +212,15 @@ public abstract class MapBinder<K, V> {
     return (TypeLiteral<Map<K, Provider<V>>>) TypeLiteral.get(
         Types.mapOf(keyType.getType(), Types.providerOf(valueType.getType())));
   }
+  
+  // provider map <K, V> is safely a Map<K, javax.inject.Provider<V>>>
+  @SuppressWarnings("unchecked")
+  static <K, V> TypeLiteral<Map<K, javax.inject.Provider<V>>> mapOfJavaxProviderOf(
+      TypeLiteral<K> keyType, TypeLiteral<V> valueType) {
+    return (TypeLiteral<Map<K, javax.inject.Provider<V>>>) TypeLiteral.get(
+        Types.mapOf(keyType.getType(),
+            newParameterizedType(javax.inject.Provider.class, valueType.getType())));
+  }
 
   @SuppressWarnings("unchecked") // a provider map <K, Set<V>> is safely a Map<K, Set<Provider<V>>>
   static <K, V> TypeLiteral<Map<K, Set<Provider<V>>>> mapOfSetOfProviderOf(
@@ -222,7 +236,7 @@ public abstract class MapBinder<K, V> {
         Map.class, Entry.class, keyType.getType(), Types.providerOf(valueType.getType())));
   }
 
-  private static <K, V> MapBinder<K, V> newMapBinder(Binder binder, 
+  private static <K, V> MapBinder<K, V> newMapBinder(Binder binder,
       TypeLiteral<K> keyType, TypeLiteral<V> valueType,
       Key<Map<K, V>> mapKey, Key<Map<K, Provider<V>>> providerMapKey,
       Key<Map<K, Set<V>>> multimapKey, Key<Map<K, Set<Provider<V>>>> providerMultimapKey,
@@ -292,6 +306,7 @@ public abstract class MapBinder<K, V> {
     private final TypeLiteral<K> keyType;
     private final TypeLiteral<V> valueType;
     private final Key<Map<K, V>> mapKey;
+    private final Key<Map<K, javax.inject.Provider<V>>> javaxProviderMapKey;
     private final Key<Map<K, Provider<V>>> providerMapKey;
     private final Key<Map<K, Set<V>>> multimapKey;
     private final Key<Map<K, Set<Provider<V>>>> providerMultimapKey;
@@ -299,7 +314,7 @@ public abstract class MapBinder<K, V> {
 
     /* the target injector's binder. non-null until initialization, null afterwards */
     private Binder binder;
-    
+
     private boolean permitDuplicates;
     private ImmutableList<Map.Entry<K, Binding<V>>> mapBindings;
 
@@ -311,6 +326,7 @@ public abstract class MapBinder<K, V> {
       this.valueType = valueType;
       this.mapKey = mapKey;
       this.providerMapKey = providerMapKey;
+      this.javaxProviderMapKey = providerMapKey.ofType(mapOfJavaxProviderOf(keyType, valueType));
       this.multimapKey = multimapKey;
       this.providerMultimapKey = providerMultimapKey;
       this.entrySetBinder = (RealMultibinder<Entry<K, Provider<V>>>) entrySetBinder;
@@ -333,10 +349,12 @@ public abstract class MapBinder<K, V> {
       checkNotNull(key, "key");
       checkConfiguration(!isInitialized(), "MapBinder was already initialized");
 
-      Key<V> valueKey = Key.get(valueType, new RealElement(entrySetBinder.getSetName()));
-      entrySetBinder.addBinding().toInstance(new MapEntry<K, Provider<V>>(key,
-          binder.getProvider(valueKey), valueKey));
-      return binder.bind(valueKey);
+      RealElement.BindingBuilder<V> valueBinding = RealElement.addMapBinding(
+          binder, key, valueType, entrySetBinder.getSetName());
+      Key<V> valueKey = Key.get(valueType, valueBinding.getAnnotation());
+      entrySetBinder.addBinding().toInstance(new ProviderMapEntry<K, V>(
+          key, binder.getProvider(valueKey), valueKey));
+      return valueBinding;
     }
 
     public void configure(Binder binder) {
@@ -348,10 +366,11 @@ public abstract class MapBinder<K, V> {
       // Binds a Map<K, Provider<V>> from a collection of Set<Entry<K, Provider<V>>.
       final Provider<Set<Entry<K, Provider<V>>>> entrySetProvider = binder
           .getProvider(entrySetBinder.getSetKey());
-      binder.bind(providerMapKey).toProvider(new RealMapBinderProviderWithDependencies<Map<K, Provider<V>>>(mapKey) {
+      
+      binder.bind(providerMapKey).toProvider(
+          new RealMapBinderProviderWithDependencies<Map<K, Provider<V>>>(mapKey) {
         private Map<K, Provider<V>> providerMap;
 
-        @SuppressWarnings({ "unused", "unchecked" })
         @Toolable @Inject void initialize(Injector injector) {
           RealMapBinder.this.binder = null;
           permitDuplicates = entrySetBinder.permitsDuplicates(injector);
@@ -362,9 +381,9 @@ public abstract class MapBinder<K, V> {
             Provider<V> previous = providerMapMutable.put(entry.getKey(), entry.getValue());
             checkConfiguration(previous == null || permitDuplicates,
                 "Map injection failed due to duplicated key \"%s\"", entry.getKey());
-            Key<V> valueKey = (Key<V>)((MapEntry)entry).getValueKey();
-            bindingsMutable.add(new MapEntry(entry.getKey(),
-                injector.getBinding(valueKey), valueKey));
+            ProviderMapEntry<K, V> providerEntry = (ProviderMapEntry<K, V>) entry;
+            Key<V> valueKey = providerEntry.getValueKey();
+            bindingsMutable.add(Maps.immutableEntry(entry.getKey(), injector.getBinding(valueKey)));
           }
 
           providerMap = ImmutableMap.copyOf(providerMapMutable);
@@ -379,9 +398,16 @@ public abstract class MapBinder<K, V> {
           return dependencies;
         }
       });
+      
+      // The map this exposes is internally an ImmutableMap, so it's OK to massage
+      // the guice Provider to javax Provider in the value (since Guice provider
+      // implements javax Provider).
+      @SuppressWarnings("unchecked")
+      Key massagedProviderMapKey = (Key)providerMapKey;
+      binder.bind(javaxProviderMapKey).to(massagedProviderMapKey);
 
       final Provider<Map<K, Provider<V>>> mapProvider = binder.getProvider(providerMapKey);
-      binder.bind(mapKey).toProvider(new RealMapWithExtensionProvider<Map<K, V>>(mapKey) {        
+      binder.bind(mapKey).toProvider(new RealMapWithExtensionProvider<Map<K, V>>(mapKey) {
         public Map<K, V> get() {
           Map<K, V> map = new LinkedHashMap<K, V>();
           for (Entry<K, Provider<V>> entry : mapProvider.get().entrySet()) {
@@ -425,7 +451,7 @@ public abstract class MapBinder<K, V> {
           if (isInitialized()) {
             return (List)mapBindings; // safe because mapBindings is immutable
           } else {
-            throw new UnsupportedOperationException("getElements() not supported for module bindings");   
+            throw new UnsupportedOperationException("getElements() not supported for module bindings");
           }
         }
 
@@ -433,7 +459,7 @@ public abstract class MapBinder<K, V> {
           if (isInitialized()) {
             return permitDuplicates;
           } else {
-            throw new UnsupportedOperationException("permitsDuplicates() not supported for module bindings");   
+            throw new UnsupportedOperationException("permitsDuplicates() not supported for module bindings");
           }
         }
 
@@ -443,15 +469,16 @@ public abstract class MapBinder<K, V> {
           } else {
             Key<?> key;
             if (element instanceof Binding) {
-              key = ((Binding)element).getKey();
+              key = ((Binding<?>)element).getKey();
             } else if (element instanceof ProviderLookup) {
-              key = ((ProviderLookup)element).getKey();
+              key = ((ProviderLookup<?>)element).getKey();
             } else {
               return false; // cannot match;
             }
 
-            return key.equals(mapKey) 
+            return key.equals(mapKey)
                 || key.equals(providerMapKey)
+                || key.equals(javaxProviderMapKey)
                 || key.equals(multimapKey)
                 || key.equals(providerMultimapKey)
                 || key.equals(entrySetBinder.getSetKey())
@@ -462,9 +489,10 @@ public abstract class MapBinder<K, V> {
     }
 
     /** Returns true if the key indicates this is a value in the map. */
-    private boolean matchesValueKey(Key key) {
+    private boolean matchesValueKey(Key<?> key) {
       return key.getAnnotation() instanceof Element
           && ((Element) key.getAnnotation()).setName().equals(entrySetBinder.getSetName())
+          && ((Element) key.getAnnotation()).type() == MAPBINDER
           && key.getTypeLiteral().equals(valueType);
     }
 
@@ -572,51 +600,74 @@ public abstract class MapBinder<K, V> {
       }
 
       @Override public boolean equals(Object o) {
-        return o instanceof MultimapBinder 
+        return o instanceof MultimapBinder
             && ((MultimapBinder<?, ?>) o).multimapKey.equals(multimapKey);
       }
     }
 
-    private static final class MapEntry<K, V> implements Map.Entry<K, V> {
+    /**
+     * Implementation of {@code {@link Map.Entry}<K, {@link Provider}<V>>} that
+     * defines equality based on the provider's key rather than the provider itself. This allows
+     * Guice to remove duplicate bindings.
+     *
+     * @param <K> the map's key type
+     * @param <V> the type provided by the map's values
+     */
+    private static final class ProviderMapEntry<K, V> implements Map.Entry<K, Provider<V>> {
       private final K key;
-      private final V value;
-      private final Key<?> valueKey;
+      private final Provider<V> provider;
+      private volatile Key<V> valueKey;
 
-      private MapEntry(K key, V value, Key<?> valueKey) {
+      private ProviderMapEntry(K key, Provider<V> provider, Key<V> valueKey) {
         this.key = key;
-        this.value = value;
+        this.provider = provider;
         this.valueKey = valueKey;
       }
-      
-      public Key<?> getValueKey() {
-        return valueKey;
+
+      public Key<V> getValueKey() {
+        // Every time, check if the value key needs rehashing.
+        // If so, update the field as an optimization for next time.
+        Key<V> currentValueKey = valueKey;
+        if (needsRehashing(currentValueKey)) {
+          currentValueKey = rehash(currentValueKey);
+          valueKey = currentValueKey;
+        }
+        return currentValueKey;
       }
 
       public K getKey() {
         return key;
       }
 
-      public V getValue() {
-        return value;
+      public Provider<V> getValue() {
+        return provider;
       }
 
-      public V setValue(V value) {
+      public Provider<V> setValue(Provider<V> value) {
         throw new UnsupportedOperationException();
       }
 
+      /**
+       * We don't follow the normal rules for Map.Entry equality, but that's okay, because
+       * the providers we return will never compare equal to anything else anyway. By defining
+       * equality this way, we let Guice remove duplicate bindings.
+       */
       @Override public boolean equals(Object obj) {
-        return obj instanceof Map.Entry
-            && key.equals(((Map.Entry<?, ?>) obj).getKey())
-            && value.equals(((Map.Entry<?, ?>) obj).getValue());
+        return obj instanceof ProviderMapEntry
+            && key.equals(((ProviderMapEntry<?, ?>) obj).getKey())
+            && getValueKey().equals(((ProviderMapEntry<?, ?>) obj).getValueKey());
       }
 
+      /**
+       * Again, we are not following the normal rules for Map.Entry, this time to avoid
+       * our hashCode changing if our {@link #valueKey} does.
+       */
       @Override public int hashCode() {
-        return 127 * ("key".hashCode() ^ key.hashCode())
-            + 127 * ("value".hashCode() ^ value.hashCode());
+        return key.hashCode();
       }
 
       @Override public String toString() {
-        return "MapEntry(" + key + ", " + value + ")";
+        return "ProviderMapEntry(" + key + ", " + valueKey + ")";
       }
     }
 
@@ -627,24 +678,28 @@ public abstract class MapBinder<K, V> {
         super(equality);
       }
     }
-    
+
     /**
      * A base class for ProviderWithDependencies that need equality
      * based on a specific object.
      */
     private static abstract class RealMapBinderProviderWithDependencies<T> implements ProviderWithDependencies<T> {
       private final Object equality;
-      
+
       public RealMapBinderProviderWithDependencies(Object equality) {
         this.equality = equality;
       }
-      
+
       @Override
       public boolean equals(Object obj) {
         return this.getClass() == obj.getClass() &&
           equality.equals(((RealMapBinderProviderWithDependencies<?>)obj).equality);
       }
-      
+
+      @Override
+      public int hashCode() {
+        return equality.hashCode();
+      }
     }
   }
 }
diff --git a/extensions/multibindings/src/com/google/inject/multibindings/Multibinder.java b/extensions/multibindings/src/com/google/inject/multibindings/Multibinder.java
index a066e33..d2126bd 100644
--- a/extensions/multibindings/src/com/google/inject/multibindings/Multibinder.java
+++ b/extensions/multibindings/src/com/google/inject/multibindings/Multibinder.java
@@ -16,8 +16,13 @@
 
 package com.google.inject.multibindings;
 
+import static com.google.common.base.Predicates.equalTo;
+import static com.google.common.collect.Iterables.filter;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static com.google.inject.multibindings.Element.Type.MULTIBINDER;
 import static com.google.inject.name.Names.named;
 
+import com.google.common.base.Objects;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
@@ -44,9 +49,9 @@ import com.google.inject.util.Types;
 
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Type;
-import java.util.Collections;
-import java.util.LinkedHashSet;
+import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 /**
@@ -76,13 +81,13 @@ import java.util.Set;
  * to create their own {@code Multibinder<Snack>}, and to each contribute
  * bindings to the set of snacks. When that set is injected, it will contain
  * elements from both modules.
- * 
+ *
  * <p>The set's iteration order is consistent with the binding order. This is
  * convenient when multiple elements are contributed by the same module because
  * that module can order its bindings appropriately. Avoid relying on the
  * iteration order of elements contributed by different modules, since there is
  * no equivalent mechanism to order modules.
- * 
+ *
  * <p>The set is unmodifiable.  Elements can only be added to the set by
  * configuring the multibinder.  Elements can never be removed from the set.
  *
@@ -219,7 +224,8 @@ public abstract class Multibinder<T> {
    * API.
    */
   static final class RealMultibinder<T> extends Multibinder<T>
-      implements Module, ProviderWithExtensionVisitor<Set<T>>, HasDependencies, MultibinderBinding<Set<T>> {
+      implements Module, ProviderWithExtensionVisitor<Set<T>>, HasDependencies,
+          MultibinderBinding<Set<T>> {
 
     private final TypeLiteral<T> elementType;
     private final String setName;
@@ -243,7 +249,7 @@ public abstract class Multibinder<T> {
       this.setName = nameOf(setKey);
       this.permitDuplicatesKey = Key.get(Boolean.class, named(toString() + " permits duplicates"));
     }
-    
+
     /**
      * Returns the name the set should use.  This is based on the annotation.
      * If the annotation has an instance and is not a marker annotation,
@@ -251,19 +257,18 @@ public abstract class Multibinder<T> {
      * or just an annotation type, we use the annotation's name. Otherwise,
      * the name is the empty string.
      */
-    private String nameOf(Key<?> key) {
+    private static String nameOf(Key<?> setKey) {
       Annotation annotation = setKey.getAnnotation();
-      Class<? extends Annotation> annotationType = setKey.getAnnotationType();      
+      Class<? extends Annotation> annotationType = setKey.getAnnotationType();
       if (annotation != null && !Annotations.isMarker(annotationType)) {
         return setKey.getAnnotation().toString();
-      } else if(setKey.getAnnotationType() != null) {
+      } else if (setKey.getAnnotationType() != null) {
         return "@" + setKey.getAnnotationType().getName();
       } else {
         return "";
       }
     }
 
-    @SuppressWarnings("unchecked")
     public void configure(Binder binder) {
       checkConfiguration(!isInitialized(), "Multibinder was already initialized");
 
@@ -279,7 +284,7 @@ public abstract class Multibinder<T> {
     @Override public LinkedBindingBuilder<T> addBinding() {
       checkConfiguration(!isInitialized(), "Multibinder was already initialized");
 
-      return binder.bind(Key.get(elementType, new RealElement(setName)));
+      return RealElement.addBinding(binder, MULTIBINDER, elementType, setName);
     }
 
     /**
@@ -312,7 +317,8 @@ public abstract class Multibinder<T> {
     private boolean keyMatches(Key<?> key) {
       return key.getTypeLiteral().equals(elementType)
           && key.getAnnotation() instanceof Element
-          && ((Element) key.getAnnotation()).setName().equals(setName);
+          && ((Element) key.getAnnotation()).setName().equals(setName)
+          && ((Element) key.getAnnotation()).type() == MULTIBINDER;
     }
 
     private boolean isInitialized() {
@@ -322,22 +328,24 @@ public abstract class Multibinder<T> {
     public Set<T> get() {
       checkConfiguration(isInitialized(), "Multibinder is not initialized");
 
-      Set<T> result = new LinkedHashSet<T>();
+      Map<T, Binding<T>> result = new LinkedHashMap<T, Binding<T>>();
       for (Binding<T> binding : bindings) {
         final T newValue = binding.getProvider().get();
         checkConfiguration(newValue != null, "Set injection failed due to null element");
-        checkConfiguration(result.add(newValue) || permitDuplicates,
-            "Set injection failed due to duplicated element \"%s\"", newValue);
+        Binding<T> duplicateBinding = result.put(newValue, binding);
+        if (!permitDuplicates && duplicateBinding != null) {
+          throw newDuplicateValuesException(result, binding, newValue, duplicateBinding);
+        }
       }
-      return Collections.unmodifiableSet(result);
+      return ImmutableSet.copyOf(result.keySet());
     }
-    
+
     @SuppressWarnings("unchecked")
     public <B, V> V acceptExtensionVisitor(
         BindingTargetVisitor<B, V> visitor,
         ProviderInstanceBinding<? extends B> binding) {
-      if(visitor instanceof MultibindingsTargetVisitor) {
-        return ((MultibindingsTargetVisitor<Set<T>, V>)visitor).visit(this);
+      if (visitor instanceof MultibindingsTargetVisitor) {
+        return ((MultibindingsTargetVisitor<Set<T>, V>) visitor).visit(this);
       } else {
         return visitor.visit(binding);
       }
@@ -346,7 +354,7 @@ public abstract class Multibinder<T> {
     String getSetName() {
       return setName;
     }
-    
+
     public TypeLiteral<?> getElementTypeLiteral() {
       return elementType;
     }
@@ -354,28 +362,29 @@ public abstract class Multibinder<T> {
     public Key<Set<T>> getSetKey() {
       return setKey;
     }
-    
+
     @SuppressWarnings("unchecked")
     public List<Binding<?>> getElements() {
-      if(isInitialized()) {
-        return (List)bindings; // safe because bindings is immutable.
+      if (isInitialized()) {
+        return (List<Binding<?>>) (List<?>) bindings; // safe because bindings is immutable.
       } else {
         throw new UnsupportedOperationException("getElements() not supported for module bindings");
       }
     }
-    
+
     public boolean permitsDuplicates() {
-      if(isInitialized()) {
+      if (isInitialized()) {
         return permitDuplicates;
       } else {
-        throw new UnsupportedOperationException("permitsDuplicates() not supported for module bindings");   
+        throw new UnsupportedOperationException(
+            "permitsDuplicates() not supported for module bindings");
       }
     }
 
     public boolean containsElement(com.google.inject.spi.Element element) {
-      if(element instanceof Binding) {
-        Binding binding = (Binding)element;
-        return keyMatches(binding.getKey()) 
+      if (element instanceof Binding) {
+        Binding<?> binding = (Binding<?>) element;
+        return keyMatches(binding.getKey())
             || binding.getKey().equals(permitDuplicatesKey)
             || binding.getKey().equals(setKey);
       } else {
@@ -446,6 +455,35 @@ public abstract class Multibinder<T> {
     throw new ConfigurationException(ImmutableSet.of(new Message(Errors.format(format, args))));
   }
 
+  private static <T> ConfigurationException newDuplicateValuesException(
+      Map<T, Binding<T>> existingBindings,
+      Binding<T> binding,
+      final T newValue,
+      Binding<T> duplicateBinding) {
+    T oldValue = getOnlyElement(filter(existingBindings.keySet(), equalTo(newValue)));
+    String oldString = oldValue.toString();
+    String newString = newValue.toString();
+    if (Objects.equal(oldString, newString)) {
+      // When the value strings match, just show the source of the bindings
+      return new ConfigurationException(ImmutableSet.of(new Message(Errors.format(
+          "Set injection failed due to duplicated element \"%s\""
+              + "\n    Bound at %s\n    Bound at %s",
+          newValue,
+          duplicateBinding.getSource(),
+          binding.getSource()))));
+    } else {
+      // When the value strings don't match, include them both as they may be useful for debugging
+      return new ConfigurationException(ImmutableSet.of(new Message(Errors.format(
+          "Set injection failed due to multiple elements comparing equal:"
+              + "\n    \"%s\"\n        bound at %s"
+              + "\n    \"%s\"\n        bound at %s",
+          oldValue,
+          duplicateBinding.getSource(),
+          newValue,
+          binding.getSource()))));
+    }
+  }
+
   static <T> T checkNotNull(T reference, String name) {
     if (reference != null) {
       return reference;
@@ -453,6 +491,6 @@ public abstract class Multibinder<T> {
 
     NullPointerException npe = new NullPointerException(name);
     throw new ConfigurationException(ImmutableSet.of(
-        new Message(ImmutableList.of(), npe.toString(), npe)));
+        new Message(npe.toString(), npe)));
   }
 }
diff --git a/extensions/multibindings/src/com/google/inject/multibindings/RealElement.java b/extensions/multibindings/src/com/google/inject/multibindings/RealElement.java
index d297f90..185d647 100644
--- a/extensions/multibindings/src/com/google/inject/multibindings/RealElement.java
+++ b/extensions/multibindings/src/com/google/inject/multibindings/RealElement.java
@@ -16,48 +16,238 @@
 
 package com.google.inject.multibindings;
 
+import com.google.common.base.Objects;
+import com.google.inject.Binder;
+import com.google.inject.Key;
+import com.google.inject.Provider;
+import com.google.inject.Scope;
+import com.google.inject.Scopes;
+import com.google.inject.TypeLiteral;
+import com.google.inject.binder.LinkedBindingBuilder;
+import com.google.inject.binder.ScopedBindingBuilder;
+import com.google.inject.spi.InjectionPoint;
+
 import java.lang.annotation.Annotation;
+import java.lang.reflect.Constructor;
 import java.util.concurrent.atomic.AtomicInteger;
 
 /**
- * @author jessewilson at google.com (Jesse Wilson)
+ * A mutable annotation implementation with non-conforming {@link #equals} and {@link #hashCode}
+ * methods.
+ * 
+ * <p>Annotation equality is defined based on the binding it is used in, letting Guice spot and
+ * remove duplicate bindings without false conflicts. However, as the binding is mutable, so is
+ * the annotation.
+ * 
+ * @author chrispurcell at google.com (Chris Purcell)
  */
 class RealElement implements Element {
   private static final AtomicInteger nextUniqueId = new AtomicInteger(1);
-
+  
+  /**
+   * Adds a new binding to a multibindings collection, returning its {@link BindingBuilder}.
+   * 
+   * @param binder the current Guice binder
+   * @param type whether the collection is a set or a map
+   * @param elementType the type of element stored in the collection
+   * @param setName the string used internally to identify the collection
+   */
+  static <T> BindingBuilder<T> addBinding(
+      Binder binder, Element.Type type, TypeLiteral<T> elementType, String setName) {
+    RealElement annotation = new RealElement(setName, type, null);
+    LinkedBindingBuilder<T> delegate = binder
+        .skipSources(RealElement.class)
+        .bind(Key.get(elementType, annotation));
+    return new BindingBuilder<T>(annotation, delegate);
+  }
+  
+  /**
+   * Adds a new map value binding to a multibindings collection, returning its
+   * {@link BindingBuilder}.
+   * 
+   * @param binder the current Guice binder
+   * @param mapKey the key used to fetch the element from the map
+   * @param elementType the type of element stored in the collection
+   * @param setName the string used internally to identify the collection
+   */
+  static <T> BindingBuilder<T> addMapBinding(
+      Binder binder, Object mapKey, TypeLiteral<T> elementType, String setName) {
+    RealElement annotation = new RealElement(setName, Element.Type.MAPBINDER, mapKey);
+    LinkedBindingBuilder<T> delegate = binder
+        .skipSources(RealElement.class)
+        .bind(Key.get(elementType, annotation));
+    return new BindingBuilder<T>(annotation, delegate);
+  }
+  
   private final int uniqueId;
   private final String setName;
+  private final Element.Type type;
+  private final Object mapKey;
+  private TargetType targetType = TargetType.UNTARGETTED;
+  private Object target = null;
+  private Object scope = Scopes.NO_SCOPE;
 
-  RealElement(String setName) {
-    uniqueId = nextUniqueId.getAndIncrement();
+  private RealElement(String setName, Element.Type type, Object mapKey) {
+    this.uniqueId = nextUniqueId.incrementAndGet();
     this.setName = setName;
+    this.type = type;
+    this.mapKey = mapKey;
   }
-
+  
   public String setName() {
     return setName;
   }
-
+  
   public int uniqueId() {
     return uniqueId;
   }
+  
+  public Element.Type type() {
+    return type;
+  }
 
   public Class<? extends Annotation> annotationType() {
     return Element.class;
   }
-
+  
   @Override public String toString() {
-    return "@" + Element.class.getName() + "(setName=" + setName
-        + ",uniqueId=" + uniqueId + ")";
+    return String.format("@%s(setName=%s, uniqueId=%d, type=%s)",
+        annotationType().getName(), setName, uniqueId, type);
   }
 
-  @Override public boolean equals(Object o) {
-    return o instanceof Element
-        && ((Element) o).setName().equals(setName())
-        && ((Element) o).uniqueId() == uniqueId();
+  /**
+   * Returns true if the given object is a {@link RealElement} associated with a binding that
+   * is considered a duplicate of the one associated with this object. Specifically, that means all
+   * the following must hold:
+   * <ul>
+   * <li>the bindings are from the same collection (as determined by {@link #setName} and
+   *     {@link #type})
+   * <li>the bindings are in the same scope
+   * <li>the target types ("instance", "linked key", etc.) match
+   * <li>the targets themselves (the instance, the linked key, etc.) are equal
+   * </ul>
+   * 
+   * <p>Note that this means the definition of equality can change if a bound instance changes.
+   * 
+   * <p>This <b>does not</b> match the definition of {@link Annotation#equals}. However, as
+   * these annotations will only ever be used within Guice, and {@link Element} itself is package
+   * private and will never be used as an annotation, this should not cause problems.
+   */
+  @Override public boolean equals(Object obj) {
+    if (obj == null || getClass() != obj.getClass()) {
+      return false;
+    }
+    RealElement other = (RealElement) obj;
+    return (setName.equals(other.setName)
+        && type.equals(other.type)
+        && Objects.equal(mapKey, other.mapKey)
+        && scope.equals(other.scope)
+        && targetType.equals(other.targetType)
+        && Objects.equal(target, other.target));
   }
-
+  
+  /**
+   * Returns the hash code of this annotation. This depends on the binding the annotation is in.
+   * 
+   * <p>This <b>does not</b> match the definition of {@link Annotation#hashCode}. However, as
+   * these annotations will only ever be used within Guice, and {@link Element} itself is package
+   * private and will never be used as an annotation, this should not cause problems.
+   */
   @Override public int hashCode() {
-    return 127 * ("setName".hashCode() ^ setName.hashCode())
-        + 127 * ("uniqueId".hashCode() ^ uniqueId);
+    return Objects.hashCode(setName, type, mapKey, scope, targetType, target);
+  }
+
+  private enum TargetType {
+    INSTANCE, PROVIDER_INSTANCE, PROVIDER_KEY, LINKED_KEY, UNTARGETTED, CONSTRUCTOR
+  }
+  
+  private static final Object EAGER_SINGLETON = new Object();
+
+  static class BindingBuilder<T> implements LinkedBindingBuilder<T> {
+    private final RealElement annotation;
+    private final LinkedBindingBuilder<T> delegate;
+
+    BindingBuilder(RealElement annotation, LinkedBindingBuilder<T> delegate) {
+      this.annotation = annotation;
+      this.delegate = delegate;
+    }
+    
+    RealElement getAnnotation() {
+      return annotation;
+    }
+
+    public void in(Class<? extends Annotation> scopeAnnotation) {
+      delegate.in(scopeAnnotation);
+      annotation.scope = scopeAnnotation;
+    }
+
+    public void in(Scope scope) {
+      delegate.in(scope);
+      annotation.scope = scope;
+    }
+
+    public void asEagerSingleton() {
+      delegate.asEagerSingleton();
+      annotation.scope = EAGER_SINGLETON;
+    }
+
+    public ScopedBindingBuilder to(Class<? extends T> implementation) {
+      return to(Key.get(implementation));
+    }
+
+    public ScopedBindingBuilder to(TypeLiteral<? extends T> implementation) {
+      return to(Key.get(implementation));
+    }
+
+    public ScopedBindingBuilder to(Key<? extends T> targetKey) {
+      delegate.to(targetKey);
+      annotation.targetType = TargetType.LINKED_KEY;
+      annotation.target = targetKey;
+      return this;
+    }
+
+    public void toInstance(T instance) {
+      delegate.toInstance(instance);
+      annotation.scope = EAGER_SINGLETON;
+      annotation.targetType = TargetType.INSTANCE;
+      annotation.target = instance;
+    }
+
+    public ScopedBindingBuilder toProvider(Provider<? extends T> provider) {
+      delegate.toProvider(provider);
+      annotation.targetType = TargetType.PROVIDER_INSTANCE;
+      annotation.target = provider;
+      return this;
+    }
+
+    public ScopedBindingBuilder toProvider(
+        Class<? extends javax.inject.Provider<? extends T>> providerType) {
+      return toProvider(Key.get(providerType));
+    }
+
+    public ScopedBindingBuilder toProvider(
+        TypeLiteral<? extends javax.inject.Provider<? extends T>> providerType) {
+      return toProvider(Key.get(providerType));
+    }
+
+    public ScopedBindingBuilder toProvider(
+        Key<? extends javax.inject.Provider<? extends T>> providerKey) {
+      delegate.toProvider(providerKey);
+      annotation.targetType = TargetType.PROVIDER_KEY;
+      annotation.target = providerKey;
+      return this;
+    }
+
+    public <S extends T> ScopedBindingBuilder toConstructor(Constructor<S> constructor) {
+      return toConstructor(constructor, TypeLiteral.get(constructor.getDeclaringClass()));
+    }
+
+    public <S extends T> ScopedBindingBuilder toConstructor(
+        Constructor<S> constructor, TypeLiteral<? extends S> type) {
+      delegate.toConstructor(constructor, type);
+      annotation.targetType = TargetType.CONSTRUCTOR;
+      annotation.target = InjectionPoint.forConstructor(constructor, type);
+      return this;
+    }
   }
 }
diff --git a/extensions/multibindings/test/com/google/inject/multibindings/MapBinderTest.java b/extensions/multibindings/test/com/google/inject/multibindings/MapBinderTest.java
index ca9dc89..31aeac0 100644
--- a/extensions/multibindings/test/com/google/inject/multibindings/MapBinderTest.java
+++ b/extensions/multibindings/test/com/google/inject/multibindings/MapBinderTest.java
@@ -25,7 +25,9 @@ import static com.google.inject.multibindings.SpiUtils.providerInstance;
 import static com.google.inject.name.Names.named;
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
+import com.google.common.base.Function;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
 import com.google.common.collect.Maps;
 import com.google.inject.AbstractModule;
 import com.google.inject.Binding;
@@ -33,6 +35,7 @@ import com.google.inject.BindingAnnotation;
 import com.google.inject.ConfigurationException;
 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;
@@ -41,10 +44,7 @@ import com.google.inject.ProvisionException;
 import com.google.inject.Stage;
 import com.google.inject.TypeLiteral;
 import com.google.inject.name.Names;
-import com.google.inject.spi.DefaultElementVisitor;
 import com.google.inject.spi.Dependency;
-import com.google.inject.spi.Element;
-import com.google.inject.spi.Elements;
 import com.google.inject.spi.HasDependencies;
 import com.google.inject.util.Modules;
 import com.google.inject.util.Providers;
@@ -64,12 +64,17 @@ import java.util.HashSet;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
 
 /**
  * @author dpb at google.com (David P. Baker)
  */
 public class MapBinderTest extends TestCase {
 
+  final TypeLiteral<Map<String, javax.inject.Provider<String>>> mapOfStringJavaxProvider =
+      new TypeLiteral<Map<String, javax.inject.Provider<String>>>() {};
+  final TypeLiteral<Map<String, Provider<String>>> mapOfStringProvider =
+      new TypeLiteral<Map<String, Provider<String>>>() {}; 
   final TypeLiteral<Map<String, String>> mapOfString = new TypeLiteral<Map<String, String>>() {};
   final TypeLiteral<Map<String, Integer>> mapOfInteger = new TypeLiteral<Map<String, Integer>>() {};
   final TypeLiteral<Map<String, Set<String>>> mapOfSetOfString =
@@ -104,6 +109,10 @@ public class MapBinderTest extends TestCase {
     assertEquals(mapOf("a", "A", "b", "B", "c", "C", "d", "D", "e", "E"), abcde);
     assertMapVisitor(Key.get(mapOfString), stringType, stringType, setOf(abc, de), BOTH, false, 0,
         instance("a", "A"), instance("b", "B"), instance("c", "C"), instance("d", "D"), instance("e", "E"));
+
+    // just make sure these succeed
+    injector.getInstance(Key.get(mapOfStringProvider));
+    injector.getInstance(Key.get(mapOfStringJavaxProvider));
   }
 
   public void testMapBinderAggregationForAnnotationInstance() {
@@ -126,6 +135,10 @@ public class MapBinderTest extends TestCase {
     assertEquals(mapOf("a", "A", "b", "B", "c", "C"), abc);
     assertMapVisitor(key, stringType, stringType, setOf(module), BOTH, false, 0,
         instance("a", "A"), instance("b", "B"), instance("c", "C"));
+    
+    // just make sure these succeed
+    injector.getInstance(Key.get(mapOfStringProvider, Names.named("abc")));
+    injector.getInstance(Key.get(mapOfStringJavaxProvider, Names.named("abc")));
   }
 
   public void testMapBinderAggregationForAnnotationType() {
@@ -148,6 +161,10 @@ public class MapBinderTest extends TestCase {
     assertEquals(mapOf("a", "A", "b", "B", "c", "C"), abc);
     assertMapVisitor(key, stringType, stringType, setOf(module), BOTH, false, 0,
         instance("a", "A"), instance("b", "B"), instance("c", "C"));
+    
+    // just make sure these succeed
+    injector.getInstance(Key.get(mapOfStringProvider, Abc.class));
+    injector.getInstance(Key.get(mapOfStringJavaxProvider, Abc.class));
   }
 
   public void testMapBinderWithMultipleAnnotationValueSets() {
@@ -177,6 +194,12 @@ public class MapBinderTest extends TestCase {
         instance("a", "A"), instance("b", "B"), instance("c", "C"));
     assertMapVisitor(deKey, stringType, stringType, setOf(module), BOTH, false, 1,
         instance("d", "D"), instance("e", "E"));     
+    
+    // just make sure these succeed
+    injector.getInstance(Key.get(mapOfStringProvider, named("abc")));
+    injector.getInstance(Key.get(mapOfStringJavaxProvider, named("abc")));
+    injector.getInstance(Key.get(mapOfStringProvider, named("de")));
+    injector.getInstance(Key.get(mapOfStringJavaxProvider, named("de")));
   }
 
   public void testMapBinderWithMultipleAnnotationTypeSets() {
@@ -206,6 +229,12 @@ public class MapBinderTest extends TestCase {
         instance("a", "A"), instance("b", "B"), instance("c", "C"));
     assertMapVisitor(deKey, stringType, stringType, setOf(module), BOTH, false, 1,
         instance("d", "D"), instance("e", "E"));
+    
+    // just make sure these succeed
+    injector.getInstance(Key.get(mapOfStringProvider, Abc.class));
+    injector.getInstance(Key.get(mapOfStringJavaxProvider, Abc.class));
+    injector.getInstance(Key.get(mapOfStringProvider, De.class));
+    injector.getInstance(Key.get(mapOfStringJavaxProvider, De.class));
   }
 
   public void testMapBinderWithMultipleTypes() {
@@ -317,22 +346,49 @@ public class MapBinderTest extends TestCase {
         multibinder.permitDuplicates();
       }
     };
-    final Set<Key<?>> allBindings = new HashSet<Key<?>>();
-    for (Element element : Elements.getElements(ab, bc)) {
-      element.acceptVisitor(new DefaultElementVisitor<Void>() {
-            @Override public <T> Void visit(Binding<T> binding) {
-              Key<?> key = binding.getKey();
-              assertTrue(String.format("Duplicate binding for %s", key),
-                  allBindings.add(binding.getKey()));
-              return null;
-            }
-          });
-    }
     Injector injector = Guice.createInjector(ab, bc);
 
     assertEquals(mapOf("a", "A", "b", "B", "c", "C"), injector.getInstance(Key.get(mapOfString)));
     assertMapVisitor(Key.get(mapOfString), stringType, stringType, setOf(ab, bc), BOTH, true, 0,
-        instance("a", "A"), instance("b", "B"), instance("b", "B"), instance("c", "C"));
+        instance("a", "A"), instance("b", "B"), instance("c", "C"));
+  }
+
+  public void testMapBinderMapDoesNotDedupeDuplicateValues() {
+    class ValueType {
+      int keyPart;
+      int dataPart;
+      private ValueType(int keyPart, int dataPart) {
+        this.keyPart = keyPart;
+        this.dataPart = dataPart;
+      }
+      @Override
+      public boolean equals(Object obj) {
+        return (obj instanceof ValueType) && (keyPart == ((ValueType) obj).keyPart);
+      }
+      @Override
+      public int hashCode() {
+        return keyPart;
+      }
+    }
+    Module m1 = new AbstractModule() {
+      @Override protected void configure() {
+        MapBinder<String, ValueType> multibinder = MapBinder.newMapBinder(
+            binder(), String.class, ValueType.class);
+        multibinder.addBinding("a").toInstance(new ValueType(1, 2));
+      }
+    };
+    Module m2 = new AbstractModule() {
+      @Override protected void configure() {
+        MapBinder<String, ValueType> multibinder = MapBinder.newMapBinder(
+            binder(), String.class, ValueType.class);
+        multibinder.addBinding("b").toInstance(new ValueType(1, 3));
+      }
+    };
+    
+    Injector injector = Guice.createInjector(m1, m2);
+    Map<String, ValueType> map = injector.getInstance(new Key<Map<String, ValueType>>() {});
+    assertEquals(2, map.get("a").dataPart);
+    assertEquals(3, map.get("b").dataPart);
   }
 
   public void testMapBinderMultimap() {
@@ -359,7 +415,7 @@ public class MapBinderTest extends TestCase {
     assertEquals(mapOf("a", setOf("A"), "b", setOf("B1", "B2"), "c", setOf("C")),
         injector.getInstance(Key.get(mapOfSetOfString)));
     assertMapVisitor(Key.get(mapOfString), stringType, stringType, setOf(ab1c, b2c), BOTH, true, 0,
-        instance("a", "A"), instance("b", "B1"), instance("b", "B2"), instance("c", "C"), instance("c", "C"));
+        instance("a", "A"), instance("b", "B1"), instance("b", "B2"), instance("c", "C"));
   }
 
   public void testMapBinderMultimapWithAnotation() {
@@ -624,11 +680,64 @@ public class MapBinderTest extends TestCase {
     assertEquals(mapOf("a", "A", "b", "B", "c", "C", "d", "D", "e", "E", "f", "F"),
         injector.getInstance(Key.get(mapOfString)));
     assertMapVisitor(Key.get(mapOfString), stringType, stringType, setOf(abcd, ef), BOTH, true, 0,
-        instance("a", "A"), instance("b", "B"), instance("c", "C"), instance("c", "C"), instance(
+        instance("a", "A"), instance("b", "B"), instance("c", "C"), instance(
             "d", "D"), instance("e", "E"), instance("f", "F"));
 
   }  
 
+  /** Ensure there are no initialization race conditions in basic map injection. */
+  public void testBasicMapDependencyInjection() {
+    final AtomicReference<Map<String, String>> injectedMap =
+        new AtomicReference<Map<String, String>>();
+    final Object anObject = new Object() {
+      @Inject void initialize(Map<String, String> map) {
+        injectedMap.set(map);
+      }
+    };
+    Module abc = new AbstractModule() {
+      protected void configure() {
+        requestInjection(anObject);
+        MapBinder<String, String> multibinder =
+            MapBinder.newMapBinder(binder(), String.class, String.class);
+        multibinder.addBinding("a").toInstance("A");
+        multibinder.addBinding("b").toInstance("B");
+        multibinder.addBinding("c").toInstance("C");
+      }
+    };
+    Guice.createInjector(abc);
+    assertEquals(mapOf("a", "A", "b", "B", "c", "C"), injectedMap.get());
+  } 
+
+  /** Ensure there are no initialization race conditions in provider multimap injection. */
+  public void testProviderMultimapDependencyInjection() {
+    final AtomicReference<Map<String, Set<Provider<String>>>> injectedMultimap =
+        new AtomicReference<Map<String, Set<Provider<String>>>>();
+    final Object anObject = new Object() {
+      @Inject void initialize(Map<String, Set<Provider<String>>> multimap) {
+        injectedMultimap.set(multimap);
+      }
+    };
+    Module abc = new AbstractModule() {
+      protected void configure() {
+        requestInjection(anObject);
+        MapBinder<String, String> multibinder =
+            MapBinder.newMapBinder(binder(), String.class, String.class);
+        multibinder.permitDuplicates();
+        multibinder.addBinding("a").toInstance("A");
+        multibinder.addBinding("b").toInstance("B");
+        multibinder.addBinding("c").toInstance("C");
+      }
+    };
+    Guice.createInjector(abc);
+    Map<String, String> map = Maps.transformValues(injectedMultimap.get(),
+        new Function<Set<Provider<String>>, String>() {
+          public String apply(Set<Provider<String>> stringProvidersSet) {
+            return Iterables.getOnlyElement(stringProvidersSet).get();
+          }
+        });
+    assertEquals(mapOf("a", "A", "b", "B", "c", "C"), map);
+  }
+  
   @Retention(RUNTIME) @BindingAnnotation
   @interface Abc {}
 
diff --git a/extensions/multibindings/test/com/google/inject/multibindings/MultibinderTest.java b/extensions/multibindings/test/com/google/inject/multibindings/MultibinderTest.java
index 652fb89..70be338 100644
--- a/extensions/multibindings/test/com/google/inject/multibindings/MultibinderTest.java
+++ b/extensions/multibindings/test/com/google/inject/multibindings/MultibinderTest.java
@@ -27,6 +27,8 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
 import com.google.inject.AbstractModule;
 import com.google.inject.Binding;
@@ -37,11 +39,18 @@ import com.google.inject.Injector;
 import com.google.inject.Key;
 import com.google.inject.Module;
 import com.google.inject.Provider;
+import com.google.inject.Provides;
 import com.google.inject.ProvisionException;
+import com.google.inject.Scopes;
 import com.google.inject.Stage;
 import com.google.inject.TypeLiteral;
+import com.google.inject.internal.RehashableKeys;
+import com.google.inject.name.Named;
 import com.google.inject.name.Names;
+import com.google.inject.spi.DefaultBindingTargetVisitor;
 import com.google.inject.spi.Dependency;
+import com.google.inject.spi.Element;
+import com.google.inject.spi.Elements;
 import com.google.inject.spi.HasDependencies;
 import com.google.inject.spi.InstanceBinding;
 import com.google.inject.spi.LinkedKeyBinding;
@@ -50,6 +59,11 @@ import com.google.inject.util.Providers;
 
 import junit.framework.TestCase;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
 import java.lang.annotation.Annotation;
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
@@ -60,6 +74,8 @@ import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Set;
 
 /**
@@ -67,10 +83,14 @@ import java.util.Set;
  */
 public class MultibinderTest extends TestCase {
 
+  final TypeLiteral<Map<String, String>> mapOfStringString =
+      new TypeLiteral<Map<String, String>>() {};
   final TypeLiteral<Set<String>> setOfString = new TypeLiteral<Set<String>>() {};
   final TypeLiteral<Set<Integer>> setOfInteger = new TypeLiteral<Set<Integer>>() {};
   final TypeLiteral<String> stringType = TypeLiteral.get(String.class);
   final TypeLiteral<Integer> intType = TypeLiteral.get(Integer.class);
+  final TypeLiteral<List<String>> listOfStrings = new TypeLiteral<List<String>>() {};
+  final TypeLiteral<Set<List<String>>> setOfListOfStrings = new TypeLiteral<Set<List<String>>>() {};
 
   public void testMultibinderAggregatesMultipleModules() {
     Module abc = new AbstractModule() {
@@ -249,6 +269,32 @@ public class MultibinderTest extends TestCase {
     }
   }
 
+  public void testMultibinderSetIsSerializable() throws IOException, ClassNotFoundException {
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      protected void configure() {
+        Multibinder.newSetBinder(binder(), String.class)
+            .addBinding().toInstance("A");
+      }
+    });
+
+    Set<String> set = injector.getInstance(Key.get(setOfString));
+    ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
+    ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteStream);
+    try {
+      objectOutputStream.writeObject(set);
+    } finally {
+      objectOutputStream.close();
+    }
+    ObjectInputStream objectInputStream = new ObjectInputStream(
+        new ByteArrayInputStream(byteStream.toByteArray()));
+    try {
+      Object setCopy = objectInputStream.readObject();
+      assertEquals(set, setCopy);
+    } finally {
+      objectInputStream.close();
+    }
+  }
+
   public void testMultibinderSetIsLazy() {
     Module module = new AbstractModule() {
       protected void configure() {
@@ -270,28 +316,94 @@ public class MultibinderTest extends TestCase {
   }
 
   public void testMultibinderSetForbidsDuplicateElements() {
-    Module module = new AbstractModule() {
+    Module module1 = new AbstractModule() {
       protected void configure() {
         final Multibinder<String> multibinder = Multibinder.newSetBinder(binder(), String.class);
-        multibinder.addBinding().toInstance("A");
-        multibinder.addBinding().toInstance("A");
+        multibinder.addBinding().toProvider(Providers.of("A"));
       }
     };
-    Injector injector = Guice.createInjector(module);
+    Module module2 = new AbstractModule() {
+      protected void configure() {
+        final Multibinder<String> multibinder = Multibinder.newSetBinder(binder(), String.class);
+        multibinder.addBinding().toProvider(Providers.of("A"));
+      }
+    };
+    Injector injector = Guice.createInjector(module1, module2);
 
     try {
       injector.getInstance(Key.get(setOfString));
       fail();
-    } catch(ProvisionException expected) {
+    } catch (ProvisionException expected) {
       assertContains(expected.getMessage(),
-          "1) Set injection failed due to duplicated element \"A\"");
+          "1) Set injection failed due to duplicated element \"A\"",
+          "Bound at " + module1.getClass().getName(),
+          "Bound at " + module2.getClass().getName());
     }
 
     // But we can still visit the module!
-    assertSetVisitor(Key.get(setOfString), stringType, setOf(module), MODULE, false, 0,
+    assertSetVisitor(Key.get(setOfString), stringType, setOf(module1, module2), MODULE, false, 0,
         instance("A"), instance("A"));
   }
 
+  public void testMultibinderSetShowsBothElementsIfToStringDifferent() {
+    // A simple example of a type whose toString returns more information than its equals method
+    // considers.
+    class ValueType {
+      int a;
+      int b;
+      ValueType(int a, int b) {
+        this.a = a;
+        this.b = b;
+      }
+      @Override
+      public boolean equals(Object obj) {
+        return (obj instanceof ValueType) && (((ValueType) obj).a == a);
+      }
+      @Override
+      public int hashCode() {
+        return a;
+      }
+      @Override
+      public String toString() {
+        return String.format("ValueType(%d,%d)", a, b);
+      }
+    }
+
+    Module module1 = new AbstractModule() {
+      protected void configure() {
+        final Multibinder<ValueType> multibinder =
+            Multibinder.newSetBinder(binder(), ValueType.class);
+        multibinder.addBinding().toProvider(Providers.of(new ValueType(1, 2)));
+      }
+    };
+    Module module2 = new AbstractModule() {
+      protected void configure() {
+        final Multibinder<ValueType> multibinder =
+            Multibinder.newSetBinder(binder(), ValueType.class);
+        multibinder.addBinding().toProvider(Providers.of(new ValueType(1, 3)));
+      }
+    };
+    Injector injector = Guice.createInjector(module1, module2);
+
+    TypeLiteral<ValueType> valueType = TypeLiteral.get(ValueType.class);
+    TypeLiteral<Set<ValueType>> setOfValueType = new TypeLiteral<Set<ValueType>>() {};
+    try {
+      injector.getInstance(Key.get(setOfValueType));
+      fail();
+    } catch (ProvisionException expected) {
+      assertContains(expected.getMessage(),
+          "1) Set injection failed due to multiple elements comparing equal:",
+          "\"ValueType(1,2)\"",
+          "bound at " + module1.getClass().getName(),
+          "\"ValueType(1,3)\"",
+          "bound at " + module2.getClass().getName());
+    }
+
+    // But we can still visit the module!
+    assertSetVisitor(Key.get(setOfValueType), valueType, setOf(module1, module2), MODULE, false, 0,
+        instance(new ValueType(1, 2)), instance(new ValueType(1, 3)));
+  }
+
   public void testMultibinderSetPermitDuplicateElements() {
     Module ab = new AbstractModule() {
       protected void configure() {
@@ -312,7 +424,7 @@ public class MultibinderTest extends TestCase {
 
     assertEquals(setOf("A", "B", "C"), injector.getInstance(Key.get(setOfString)));
     assertSetVisitor(Key.get(setOfString), stringType, setOf(ab, bc), BOTH, true, 0,
-        instance("A"), instance("B"), instance("B"), instance("C"));
+        instance("A"), instance("B"), instance("C"));
   }
 
   public void testMultibinderSetPermitDuplicateCallsToPermitDuplicates() {
@@ -336,7 +448,7 @@ public class MultibinderTest extends TestCase {
 
     assertEquals(setOf("A", "B", "C"), injector.getInstance(Key.get(setOfString)));
     assertSetVisitor(Key.get(setOfString), stringType, setOf(ab, bc), BOTH, true, 0,
-        instance("A"), instance("B"), instance("B"), instance("C"));
+        instance("A"), instance("B"), instance("C"));
   }
 
   public void testMultibinderSetForbidsNullElements() {
@@ -557,7 +669,342 @@ public class MultibinderTest extends TestCase {
         injector.getInstance(Key.get(setOfString)));
 
     assertSetVisitor(Key.get(setOfString), stringType, setOf(abcd, ef), BOTH, true, 0,
-        instance("A"), instance("B"), instance("C"), instance("C"), instance("D"), instance("E"), instance("F"));
+        instance("A"), instance("B"), instance("C"), instance("D"), instance("E"), instance("F"));
+  }
+
+  /**
+   * Doubly-installed modules should not conflict, even when one is overridden.
+   */
+  public void testModuleOverrideRepeatedInstallsAndMultibindings_toInstance() {
+    Module ab = new AbstractModule() {
+      @Override protected void configure() {
+        Multibinder<String> multibinder = Multibinder.newSetBinder(binder(), String.class);
+        multibinder.addBinding().toInstance("A");
+        multibinder.addBinding().toInstance("B");
+      }
+    };
+
+    // Guice guarantees this assertion, as the same module cannot be installed twice.
+    assertEquals(ImmutableSet.of("A", "B"),
+        Guice.createInjector(ab, ab).getInstance(Key.get(setOfString)));
+
+    // Guice will only guarantee this assertion if Multibinder ensures the bindings match.
+    Injector injector = Guice.createInjector(ab, Modules.override(ab).with(ab));
+    assertEquals(ImmutableSet.of("A", "B"),
+        injector.getInstance(Key.get(setOfString)));
+  }
+
+  public void testModuleOverrideRepeatedInstallsAndMultibindings_toKey() {
+    Module ab = new AbstractModule() {
+      @Override protected void configure() {
+        Key<String> aKey = Key.get(String.class, Names.named("A_string"));
+        Key<String> bKey = Key.get(String.class, Names.named("B_string"));
+        bind(aKey).toInstance("A");
+        bind(bKey).toInstance("B");
+
+        Multibinder<String> multibinder = Multibinder.newSetBinder(binder(), String.class);
+        multibinder.addBinding().to(aKey);
+        multibinder.addBinding().to(bKey);
+      }
+    };
+
+    // Guice guarantees this assertion, as the same module cannot be installed twice.
+    assertEquals(ImmutableSet.of("A", "B"),
+        Guice.createInjector(ab, ab).getInstance(Key.get(setOfString)));
+
+    // Guice will only guarantee this assertion if Multibinder ensures the bindings match.
+    Injector injector = Guice.createInjector(ab, Modules.override(ab).with(ab));
+    assertEquals(ImmutableSet.of("A", "B"),
+        injector.getInstance(Key.get(setOfString)));
+  }
+
+  public void testModuleOverrideRepeatedInstallsAndMultibindings_toProviderInstance() {
+    // Providers#of() does not redefine equals/hashCode, so use the same one both times.
+    final Provider<String> aProvider = Providers.of("A");
+    final Provider<String> bProvider = Providers.of("B");
+    Module ab = new AbstractModule() {
+      @Override protected void configure() {
+        Multibinder<String> multibinder = Multibinder.newSetBinder(binder(), String.class);
+        multibinder.addBinding().toProvider(aProvider);
+        multibinder.addBinding().toProvider(bProvider);
+      }
+    };
+
+    // Guice guarantees this assertion, as the same module cannot be installed twice.
+    assertEquals(ImmutableSet.of("A", "B"),
+        Guice.createInjector(ab, ab).getInstance(Key.get(setOfString)));
+
+    // Guice will only guarantee this assertion if Multibinder ensures the bindings match.
+    Injector injector = Guice.createInjector(ab, Modules.override(ab).with(ab));
+    assertEquals(ImmutableSet.of("A", "B"),
+        injector.getInstance(Key.get(setOfString)));
+  }
+
+  private static class AStringProvider implements Provider<String> {
+    public String get() {
+      return "A";
+    }
+  }
+
+  private static class BStringProvider implements Provider<String> {
+    public String get() {
+      return "B";
+    }
+  }
+
+  public void testModuleOverrideRepeatedInstallsAndMultibindings_toProviderKey() {
+    Module ab = new AbstractModule() {
+      @Override protected void configure() {
+        Multibinder<String> multibinder = Multibinder.newSetBinder(binder(), String.class);
+        multibinder.addBinding().toProvider(Key.get(AStringProvider.class));
+        multibinder.addBinding().toProvider(Key.get(BStringProvider.class));
+      }
+    };
+
+    // Guice guarantees this assertion, as the same module cannot be installed twice.
+    assertEquals(ImmutableSet.of("A", "B"),
+        Guice.createInjector(ab, ab).getInstance(Key.get(setOfString)));
+
+    // Guice will only guarantee this assertion if Multibinder ensures the bindings match.
+    Injector injector = Guice.createInjector(ab, Modules.override(ab).with(ab));
+    assertEquals(ImmutableSet.of("A", "B"),
+        injector.getInstance(Key.get(setOfString)));
+  }
+
+  private static class StringGrabber {
+    private final String string;
+
+    @SuppressWarnings("unused")  // Found by reflection
+    public StringGrabber(@Named("A_string") String string) {
+      this.string = string;
+    }
+
+    @SuppressWarnings("unused")  // Found by reflection
+    public StringGrabber(@Named("B_string") String string, int unused) {
+      this.string = string;
+    }
+
+    @Override
+    public int hashCode() {
+      return string.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+      return (obj instanceof StringGrabber) && ((StringGrabber) obj).string.equals(string);
+    }
+
+    @Override
+    public String toString() {
+      return "StringGrabber(" + string + ")";
+    }
+
+    static Set<String> values(Iterable<StringGrabber> grabbers) {
+      Set<String> result = new HashSet<String>();
+      for (StringGrabber grabber : grabbers) {
+        result.add(grabber.string);
+      }
+      return result;
+    }
+  }
+
+  public void testModuleOverrideRepeatedInstallsAndMultibindings_toConstructor() {
+    TypeLiteral<Set<StringGrabber>> setOfStringGrabber = new TypeLiteral<Set<StringGrabber>>() {};
+    Module ab = new AbstractModule() {
+      @Override protected void configure() {
+        Key<String> aKey = Key.get(String.class, Names.named("A_string"));
+        Key<String> bKey = Key.get(String.class, Names.named("B_string"));
+        bind(aKey).toInstance("A");
+        bind(bKey).toInstance("B");
+        bind(Integer.class).toInstance(0);  // used to disambiguate constructors
+
+        Multibinder<StringGrabber> multibinder =
+            Multibinder.newSetBinder(binder(), StringGrabber.class);
+        try {
+          multibinder.addBinding().toConstructor(
+              StringGrabber.class.getConstructor(String.class));
+          multibinder.addBinding().toConstructor(
+              StringGrabber.class.getConstructor(String.class, int.class));
+        } catch (NoSuchMethodException e) {
+          fail("No such method: " + e.getMessage());
+        }
+      }
+    };
+
+    // Guice guarantees this assertion, as the same module cannot be installed twice.
+    assertEquals(ImmutableSet.of("A", "B"),
+        StringGrabber.values(
+            Guice.createInjector(ab, ab).getInstance(Key.get(setOfStringGrabber))));
+
+    // Guice will only guarantee this assertion if Multibinder ensures the bindings match.
+    Injector injector = Guice.createInjector(ab, Modules.override(ab).with(ab));
+    assertEquals(ImmutableSet.of("A", "B"),
+        StringGrabber.values(injector.getInstance(Key.get(setOfStringGrabber))));
+  }
+
+  /**
+   * Unscoped bindings should not conflict, whether they were bound with no explicit scope, or
+   * explicitly bound in {@link Scopes#NO_SCOPE}.
+   */
+  public void testDuplicateUnscopedBindings() {
+    Module singleBinding = new AbstractModule() {
+      @Override protected void configure() {
+        bind(Integer.class).to(Key.get(Integer.class, named("A")));
+        bind(Integer.class).to(Key.get(Integer.class, named("A"))).in(Scopes.NO_SCOPE);
+      }
+
+      @Provides @Named("A")
+      int provideInteger() {
+        return 5;
+      }
+    };
+    Module multibinding = new AbstractModule() {
+      @Override protected void configure() {
+        Multibinder<Integer> multibinder = Multibinder.newSetBinder(binder(), Integer.class);
+        multibinder.addBinding().to(Key.get(Integer.class, named("A")));
+        multibinder.addBinding().to(Key.get(Integer.class, named("A"))).in(Scopes.NO_SCOPE);
+      }
+    };
+
+    assertEquals(5,
+        (int) Guice.createInjector(singleBinding).getInstance(Integer.class));
+    assertEquals(ImmutableSet.of(5),
+        Guice.createInjector(singleBinding, multibinding).getInstance(Key.get(setOfInteger)));
+  }
+
+  /**
+   * Ensure key hash codes are fixed at injection time, not binding time.
+   */
+  public void testKeyHashCodesFixedAtInjectionTime() {
+    Module ab = new AbstractModule() {
+      @Override protected void configure() {
+        Multibinder<List<String>> multibinder = Multibinder.newSetBinder(binder(), listOfStrings);
+        List<String> list = Lists.newArrayList();
+        multibinder.addBinding().toInstance(list);
+        list.add("A");
+        list.add("B");
+      }
+    };
+
+    Injector injector = Guice.createInjector(ab);
+    for (Entry<Key<?>, Binding<?>> entry : injector.getAllBindings().entrySet()) {
+      Key<?> bindingKey = entry.getKey();
+      Key<?> clonedKey;
+      if (bindingKey.getAnnotation() != null) {
+        clonedKey = Key.get(bindingKey.getTypeLiteral(), bindingKey.getAnnotation());
+      } else if (bindingKey.getAnnotationType() != null) {
+        clonedKey = Key.get(bindingKey.getTypeLiteral(), bindingKey.getAnnotationType());
+      } else {
+        clonedKey = Key.get(bindingKey.getTypeLiteral());
+      }
+      assertEquals(bindingKey, clonedKey);
+      assertEquals("Incorrect hashcode for " + bindingKey + " -> " + entry.getValue(),
+          bindingKey.hashCode(), clonedKey.hashCode());
+    }
+  }
+
+  /**
+   * Ensure bindings do not rehash their keys once returned from {@link Elements#getElements}.
+   */
+  public void testBindingKeysFixedOnReturnFromGetElements() {
+    final List<String> list = Lists.newArrayList();
+    Module ab = new AbstractModule() {
+      @Override protected void configure() {
+        Multibinder<List<String>> multibinder = Multibinder.newSetBinder(binder(), listOfStrings);
+        multibinder.addBinding().toInstance(list);
+        list.add("A");
+        list.add("B");
+      }
+    };
+
+    InstanceBinding<?> binding = Iterables.getOnlyElement(
+        Iterables.filter(Elements.getElements(ab), InstanceBinding.class));
+    Key<?> keyBefore = binding.getKey();
+    assertEquals(listOfStrings, keyBefore.getTypeLiteral());
+    assertFalse(RehashableKeys.Keys.needsRehashing(keyBefore));
+
+    list.add("C");
+    Key<?> keyAfter = binding.getKey();
+    assertSame(keyBefore, keyAfter);
+    assertTrue(RehashableKeys.Keys.needsRehashing(keyAfter));
+  }
+
+  /*
+   * Verify through gratuitous mutation that key hashCode snapshots and whatnot happens at the right
+   * times, by binding two lists that are different at injector creation, but compare equal when the
+   * module is configured *and* when the set is instantiated.
+   */
+  public void testConcurrentMutation_bindingsDiffentAtInjectorCreation() {
+    // We initially bind two equal lists
+    final List<String> list1 = Lists.newArrayList();
+    final List<String> list2 = Lists.newArrayList();
+    Module module = new AbstractModule() {
+      @Override protected void configure() {
+        Multibinder<List<String>> multibinder = Multibinder.newSetBinder(binder(), listOfStrings);
+        multibinder.addBinding().toInstance(list1);
+        multibinder.addBinding().toInstance(list2);
+      }
+    };
+    List<Element> elements = Elements.getElements(module);
+
+    // Now we change the lists so they no longer match, and create the injector.
+    list1.add("A");
+    list2.add("B");
+    Injector injector = Guice.createInjector(Elements.getModule(elements));
+
+    // Now we change the lists so they compare equal again, and create the set.
+    list1.add(1, "B");
+    list2.add(0, "A");
+    try {
+      injector.getInstance(Key.get(setOfListOfStrings));
+      fail();
+    } catch (ProvisionException e) {
+      assertEquals(1, e.getErrorMessages().size());
+      assertContains(
+          Iterables.getOnlyElement(e.getErrorMessages()).getMessage().toString(),
+          "Set injection failed due to duplicated element \"[A, B]\"");
+    }
+
+    // Finally, we change the lists again so they are once more different, and ensure the set
+    // contains both.
+    list1.remove("A");
+    list2.remove("B");
+    Set<List<String>> set = injector.getInstance(Key.get(setOfListOfStrings));
+    assertEquals(ImmutableSet.of(ImmutableList.of("A"), ImmutableList.of("B")), set);
+  }
+
+  /*
+   * Verify through gratuitous mutation that key hashCode snapshots and whatnot happen at the right
+   * times, by binding two lists that compare equal at injector creation, but are different when the
+   * module is configured *and* when the set is instantiated.
+   */
+  public void testConcurrentMutation_bindingsSameAtInjectorCreation() {
+    // We initially bind two distinct lists
+    final List<String> list1 = Lists.newArrayList("A");
+    final List<String> list2 = Lists.newArrayList("B");
+    Module module = new AbstractModule() {
+      @Override protected void configure() {
+        Multibinder<List<String>> multibinder = Multibinder.newSetBinder(binder(), listOfStrings);
+        multibinder.addBinding().toInstance(list1);
+        multibinder.addBinding().toInstance(list2);
+      }
+    };
+    List<Element> elements = Elements.getElements(module);
+
+    // Now we change the lists so they compare equal, and create the injector.
+    list1.add(1, "B");
+    list2.add(0, "A");
+    Injector injector = Guice.createInjector(Elements.getModule(elements));
+
+    // Now we change the lists again so they are once more different, and create the set.
+    list1.remove("A");
+    list2.remove("B");
+    Set<List<String>> set = injector.getInstance(Key.get(setOfListOfStrings));
+
+    // The set will contain just one of the two lists.
+    // (In fact, it will be the first one we bound, but we don't promise that, so we won't test it.)
+    assertTrue(ImmutableSet.of(ImmutableList.of("A")).equals(set)
+        || ImmutableSet.of(ImmutableList.of("B")).equals(set));
   }
 
   @BindingAnnotation
@@ -600,7 +1047,8 @@ public class MultibinderTest extends TestCase {
     assertEquals(expected, s1);
   }
 
-  public void failing_testSetAndMapValueConflict() {
+  // See issue 670
+  public void testSetAndMapValueAreDistinct() {
     Injector injector = Guice.createInjector(new AbstractModule() {
       @Override protected void configure() {
         Multibinder.newSetBinder(binder(), String.class)
@@ -613,4 +1061,61 @@ public class MultibinderTest extends TestCase {
 
     assertEquals(ImmutableSet.<String>of("A"), injector.getInstance(Key.get(setOfString)));
   }
+
+  // See issue 670
+  public void testSetAndMapValueAreDistinctInSpi() {
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      @Override protected void configure() {
+        Multibinder.newSetBinder(binder(), String.class)
+            .addBinding().toInstance("A");
+
+        MapBinder.newMapBinder(binder(), String.class, String.class)
+            .addBinding("B").toInstance("b");
+      }
+    });
+    Collector collector = new Collector();
+    Binding<Map<String, String>> mapbinding = injector.getBinding(Key.get(mapOfStringString));
+    mapbinding.acceptTargetVisitor(collector);
+    assertNotNull(collector.mapbinding);
+
+    Binding<Set<String>> setbinding = injector.getBinding(Key.get(setOfString));
+    setbinding.acceptTargetVisitor(collector);
+    assertNotNull(collector.setbinding);
+
+    // Capture the value bindings and assert we have them right --
+    // they'll always be in the right order because we preserve
+    // binding order.
+    List<Binding<String>> bindings = injector.findBindingsByType(stringType);
+    assertEquals(2, bindings.size());
+    Binding<String> a = bindings.get(0);
+    Binding<String> b = bindings.get(1);
+    assertEquals("A", ((InstanceBinding<String>)a).getInstance());
+    assertEquals("b", ((InstanceBinding<String>)b).getInstance());
+
+    // Now make sure "A" belongs only to the set binding,
+    // and "b" only belongs to the map binding.
+    assertFalse(collector.mapbinding.containsElement(a));
+    assertTrue(collector.mapbinding.containsElement(b));
+
+    assertTrue(collector.setbinding.containsElement(a));
+    assertFalse(collector.setbinding.containsElement(b));
+  }
+
+  private static class Collector extends DefaultBindingTargetVisitor<Object, Object> implements
+      MultibindingsTargetVisitor<Object, Object> {
+    MapBinderBinding<? extends Object> mapbinding;
+    MultibinderBinding<? extends Object> setbinding;
+
+    @Override
+    public Object visit(MapBinderBinding<? extends Object> mapbinding) {
+      this.mapbinding = mapbinding;
+      return null;
+    }
+
+    @Override
+    public Object visit(MultibinderBinding<? extends Object> multibinding) {
+     this.setbinding = multibinding;
+     return null;
+    }
+  }
 }
diff --git a/extensions/multibindings/test/com/google/inject/multibindings/SpiUtils.java b/extensions/multibindings/test/com/google/inject/multibindings/SpiUtils.java
index 11738e6..2589f5d 100644
--- a/extensions/multibindings/test/com/google/inject/multibindings/SpiUtils.java
+++ b/extensions/multibindings/test/com/google/inject/multibindings/SpiUtils.java
@@ -18,6 +18,7 @@ package com.google.inject.multibindings;
 
 import static com.google.inject.multibindings.MapBinder.entryOfProviderOf;
 import static com.google.inject.multibindings.MapBinder.mapOf;
+import static com.google.inject.multibindings.MapBinder.mapOfJavaxProviderOf;
 import static com.google.inject.multibindings.MapBinder.mapOfProviderOf;
 import static com.google.inject.multibindings.MapBinder.mapOfSetOfProviderOf;
 import static com.google.inject.multibindings.Multibinder.setOf;
@@ -32,6 +33,7 @@ import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertTrue;
 import static junit.framework.Assert.fail;
 
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
 import com.google.inject.Binding;
 import com.google.inject.Guice;
@@ -152,11 +154,13 @@ public class SpiUtils {
       fail("Found all entries of: " + mapResults + ", but more were left over: " + entries);
     }
     
-    Key<?> mapOfProvider = adapt(mapKey, mapOfProviderOf(keyType, valueType));
-    Key<?> mapOfSetOfProvider = adapt(mapKey, mapOfSetOfProviderOf(keyType, valueType));
-    Key<?> mapOfSet = adapt(mapKey, mapOf(keyType, setOf(valueType)));
-    Key<?> setOfEntry = adapt(mapKey, setOf(entryOfProviderOf(keyType, valueType)));
+    Key<?> mapOfJavaxProvider = mapKey.ofType(mapOfJavaxProviderOf(keyType, valueType));
+    Key<?> mapOfProvider = mapKey.ofType(mapOfProviderOf(keyType, valueType));
+    Key<?> mapOfSetOfProvider = mapKey.ofType(mapOfSetOfProviderOf(keyType, valueType));
+    Key<?> mapOfSet = mapKey.ofType(mapOf(keyType, setOf(valueType)));
+    Key<?> setOfEntry = mapKey.ofType(setOf(entryOfProviderOf(keyType, valueType)));
     boolean entrySetMatch = false;
+    boolean mapJavaxProviderMatch = false;
     boolean mapProviderMatch = false;
     boolean mapSetMatch = false; 
     boolean mapSetProviderMatch = false;
@@ -174,6 +178,9 @@ public class SpiUtils {
       } else if(b.getKey().equals(mapOfProvider)) {
         assertTrue(contains);
         mapProviderMatch = true;
+      } else if (b.getKey().equals(mapOfJavaxProvider)) {
+        assertTrue(contains);
+        mapJavaxProviderMatch = true;
       } else if(b.getKey().equals(mapOfSet)) {
         assertTrue(contains);
         mapSetMatch = true;
@@ -198,28 +205,18 @@ public class SpiUtils {
     assertEquals("Incorrect other matches: " + otherMatches, mapResults.size(), sizeOfOther);
     assertTrue(entrySetMatch);
     assertTrue(mapProviderMatch);
+    assertTrue(mapJavaxProviderMatch);
     assertEquals(allowDuplicates, mapSetMatch);
     assertEquals(allowDuplicates, mapSetProviderMatch);
     assertEquals("other MapBindings found: " + otherMapBindings, expectedMapBindings,
         otherMapBindings.size());
   }
   
-  /** Adapts a key, keeping the original annotation, using the new type literal. */
-  private static Key<?> adapt(Key<?> mapKey, TypeLiteral<?> resultType) {
-    if(mapKey.getAnnotation() != null) {
-      return Key.get(resultType, mapKey.getAnnotation());
-    } else if(mapKey.getAnnotationType() != null) {
-      return Key.get(resultType, mapKey.getAnnotationType());
-    } else {
-      return Key.get(resultType);
-    }
-  }
-  
   @SuppressWarnings("unchecked")
   private static <T> void mapModuleTest(Key<T> mapKey, TypeLiteral<?> keyType,
       TypeLiteral<?> valueType, Iterable<? extends Module> modules, boolean allowDuplicates,
       int expectedMapBindings, MapResult... results) {
-    List<Element> elements = Elements.getElements(modules);
+    Set<Element> elements = ImmutableSet.copyOf(Elements.getElements(modules));
     Visitor<T> visitor = new Visitor<T>();
     MapBinderBinding<T> mapbinder = null;
     for(Element element : elements) {
@@ -234,10 +231,10 @@ public class SpiUtils {
     assertEquals(valueType, mapbinder.getValueTypeLiteral());
     List<MapResult> mapResults = Lists.newArrayList(results);
     
-    Key<?> mapOfProvider = adapt(mapKey, mapOfProviderOf(keyType, valueType));
-    Key<?> mapOfSetOfProvider = adapt(mapKey, mapOfSetOfProviderOf(keyType, valueType));
-    Key<?> mapOfSet = adapt(mapKey, mapOf(keyType, setOf(valueType)));
-    Key<?> setOfEntry = adapt(mapKey, setOf(entryOfProviderOf(keyType, valueType)));    
+    Key<?> mapOfProvider = mapKey.ofType(mapOfProviderOf(keyType, valueType));
+    Key<?> mapOfSetOfProvider = mapKey.ofType(mapOfSetOfProviderOf(keyType, valueType));
+    Key<?> mapOfSet = mapKey.ofType(mapOf(keyType, setOf(valueType)));
+    Key<?> setOfEntry = mapKey.ofType(setOf(entryOfProviderOf(keyType, valueType)));    
     boolean entrySetMatch = false;
     boolean mapProviderMatch = false;
     boolean mapSetMatch = false; 
@@ -500,7 +497,7 @@ public class SpiUtils {
     return new MapResult<K, V>(k, new BindResult<V>(PROVIDER_INSTANCE, v, null));
   }
 
-  private static class MapResult<K, V> {
+  static class MapResult<K, V> {
     private final K k;
     private final BindResult<V> v;
     
@@ -534,7 +531,7 @@ public class SpiUtils {
   /** The kind of binding. */
   static enum BindType { INSTANCE, LINKED, PROVIDER_INSTANCE }
   /** The result of the binding. */
-  private static class BindResult<T> {
+  static class BindResult<T> {
     private final BindType type;
     private final Key<? extends T> key;
     private final T instance;
diff --git a/extensions/persist/build.xml b/extensions/persist/build.xml
index 9be40c8..764c726 100644
--- a/extensions/persist/build.xml
+++ b/extensions/persist/build.xml
@@ -11,11 +11,10 @@
     <pathelement path="../../build/classes"/>
   </path>
 
-  <target name="jar" depends="jar.withrenameddeps, manifest" description="Build jar.">
+  <target name="jar" depends="compile, manifest" description="Build jar.">
     <jar destfile="${build.dir}/${ant.project.name}-${version}.jar"
         manifest="${build.dir}/META-INF/MANIFEST.MF">
-      <zipfileset src="${build.dir}/${ant.project.name}-with-deps.jar"
-          excludes="com/google/inject/internal/**"/>
+      <fileset dir="${build.dir}/classes"/>
     </jar>
   </target>
 
diff --git a/extensions/persist/pom.xml b/extensions/persist/pom.xml
index 9aaa2cc..e95d0c8 100644
--- a/extensions/persist/pom.xml
+++ b/extensions/persist/pom.xml
@@ -5,8 +5,8 @@
 
   <parent>
     <groupId>org.sonatype.sisu.inject</groupId>
-    <artifactId>guice-extensions</artifactId>
-    <version>3.1.1</version>
+    <artifactId>extensions-parent</artifactId>
+    <version>3.1.9</version>
   </parent>
 
   <artifactId>guice-persist</artifactId>
@@ -29,7 +29,7 @@
     <dependency>
       <groupId>org.slf4j</groupId>
       <artifactId>slf4j-simple</artifactId>
-      <version>1.6.3</version>
+      <version>1.6.4</version>
       <scope>test</scope>
     </dependency>
     <dependency>
@@ -41,9 +41,20 @@
     <dependency>
       <groupId>org.hsqldb</groupId>
       <artifactId>hsqldb-j5</artifactId>
-      <version>2.2.4</version>
+      <version>2.0.0</version>
       <scope>test</scope>
     </dependency>
   </dependencies>
 
+  <build>
+    <plugins>
+      <plugin>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <configuration>
+          <forkMode>never</forkMode>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+
 </project>
diff --git a/extensions/persist/src/com/google/inject/persist/finder/package-info.java b/extensions/persist/src/com/google/inject/persist/finder/package-info.java
index 514a54f..279929c 100644
--- a/extensions/persist/src/com/google/inject/persist/finder/package-info.java
+++ b/extensions/persist/src/com/google/inject/persist/finder/package-info.java
@@ -1,4 +1,20 @@
 /**
+ * Copyright (C) 2010 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.
+ */
+
+/**
  * Dynamic Finder API for Guice Persist.
  */
-package com.google.inject.persist.finder;
\ No newline at end of file
+package com.google.inject.persist.finder;
diff --git a/extensions/persist/src/com/google/inject/persist/jpa/JpaPersistModule.java b/extensions/persist/src/com/google/inject/persist/jpa/JpaPersistModule.java
index 5e9e364..9bbcbfb 100644
--- a/extensions/persist/src/com/google/inject/persist/jpa/JpaPersistModule.java
+++ b/extensions/persist/src/com/google/inject/persist/jpa/JpaPersistModule.java
@@ -65,8 +65,7 @@ public final class JpaPersistModule extends PersistModule {
     if (null != properties) {
       bind(Properties.class).annotatedWith(Jpa.class).toInstance(properties);
     } else if (null != propertiesProvider) {
-      bind(Properties.class).annotatedWith(Jpa.class)
-          .toProvider(propertiesProvider);
+      bind(Properties.class).annotatedWith(Jpa.class).toProvider(propertiesProvider);
     } else {
       bind(Properties.class).annotatedWith(Jpa.class)
           .toProvider(Providers.<Properties>of(null));
diff --git a/extensions/persist/src/com/google/inject/persist/jpa/package-info.java b/extensions/persist/src/com/google/inject/persist/jpa/package-info.java
index 7a55893..f9e46da 100644
--- a/extensions/persist/src/com/google/inject/persist/jpa/package-info.java
+++ b/extensions/persist/src/com/google/inject/persist/jpa/package-info.java
@@ -1,4 +1,20 @@
 /**
+ * Copyright (C) 2010 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.
+ */
+
+/**
  * guice-persist's Java Persistence API (JPA) support.
  */
-package com.google.inject.persist.jpa;
\ No newline at end of file
+package com.google.inject.persist.jpa;
diff --git a/extensions/pom.xml b/extensions/pom.xml
index 10e07d7..354b4e4 100644
--- a/extensions/pom.xml
+++ b/extensions/pom.xml
@@ -6,12 +6,12 @@
   <parent>
     <groupId>org.sonatype.sisu.inject</groupId>
     <artifactId>guice-parent</artifactId>
-    <version>3.1.1</version>
+    <version>3.1.9</version>
   </parent>
 
   <packaging>pom</packaging>
 
-  <artifactId>guice-extensions</artifactId>
+  <artifactId>extensions-parent</artifactId>
 
   <name>Sisu Guice - Extensions</name>
 
@@ -42,6 +42,7 @@
       <groupId>org.sonatype.sisu</groupId>
       <artifactId>sisu-guice</artifactId>
       <version>${project.version}</version>
+      <scope>provided</scope>
     </dependency>
     <!--
      | Some extension tests depend on the core tests
@@ -88,7 +89,7 @@
   <profiles>
     <profile>
       <!--
-       | Non-JarJar build profile: need CGLIB during tests
+       | If JarJar build profile is disabled we need CGLIB during tests
       -->
       <activation>
         <property>
@@ -98,9 +99,15 @@
       </activation>
       <dependencies>
         <dependency>
+          <groupId>org.ow2.asm</groupId>
+          <artifactId>asm</artifactId>
+          <version>5.0_BETA</version>
+          <scope>test</scope>
+        </dependency>
+        <dependency>
           <groupId>cglib</groupId>
           <artifactId>cglib</artifactId>
-          <version>2.2.2</version>
+          <version>3.1</version>
           <scope>test</scope>
         </dependency>
       </dependencies>
diff --git a/extensions/service/pom.xml b/extensions/service/pom.xml
index df91f36..9eede3e 100644
--- a/extensions/service/pom.xml
+++ b/extensions/service/pom.xml
@@ -5,8 +5,8 @@
 
   <parent>
     <groupId>org.sonatype.sisu.inject</groupId>
-    <artifactId>guice-extensions</artifactId>
-    <version>3.0-SNAPSHOT</version>
+    <artifactId>extensions-parent</artifactId>
+    <version>3.2.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>guice-service</artifactId>
diff --git a/extensions/servlet/build.xml b/extensions/servlet/build.xml
index d7a10e1..5bee7e5 100644
--- a/extensions/servlet/build.xml
+++ b/extensions/servlet/build.xml
@@ -12,11 +12,10 @@
     <pathelement path="../../build/classes"/>
   </path>
 
-  <target name="jar" depends="jar.withrenameddeps, manifest" description="Build jar.">
+  <target name="jar" depends="compile, manifest" description="Build jar.">
     <jar destfile="${build.dir}/${ant.project.name}-${version}.jar"
         manifest="${build.dir}/META-INF/MANIFEST.MF">
-      <zipfileset src="${build.dir}/${ant.project.name}-with-deps.jar"
-          excludes="com/google/inject/internal/**"/>
+      <fileset dir="${build.dir}/classes"/>
     </jar>
   </target>
 
diff --git a/extensions/servlet/pom.xml b/extensions/servlet/pom.xml
index db36230..47ea71e 100644
--- a/extensions/servlet/pom.xml
+++ b/extensions/servlet/pom.xml
@@ -5,8 +5,8 @@
 
   <parent>
     <groupId>org.sonatype.sisu.inject</groupId>
-    <artifactId>guice-extensions</artifactId>
-    <version>3.1.1</version>
+    <artifactId>extensions-parent</artifactId>
+    <version>3.1.9</version>
   </parent>
 
   <artifactId>guice-servlet</artifactId>
diff --git a/extensions/servlet/src/com/google/inject/servlet/ContinuingHttpServletRequest.java b/extensions/servlet/src/com/google/inject/servlet/ContinuingHttpServletRequest.java
index 5fd7e54..36c3477 100644
--- a/extensions/servlet/src/com/google/inject/servlet/ContinuingHttpServletRequest.java
+++ b/extensions/servlet/src/com/google/inject/servlet/ContinuingHttpServletRequest.java
@@ -68,6 +68,9 @@ class ContinuingHttpServletRequest extends HttpServletRequestWrapper {
   }
 
   @Override public Cookie[] getCookies() {
+    if (super.getCookies() == null) {
+      return null;
+    }
     // TODO(dhanji): Cookies themselves are mutable. Is this a problem?
     return super.getCookies().clone();
   }
diff --git a/extensions/servlet/src/com/google/inject/servlet/DefaultFilterPipeline.java b/extensions/servlet/src/com/google/inject/servlet/DefaultFilterPipeline.java
index 825d863..db32948 100755
--- a/extensions/servlet/src/com/google/inject/servlet/DefaultFilterPipeline.java
+++ b/extensions/servlet/src/com/google/inject/servlet/DefaultFilterPipeline.java
@@ -17,6 +17,7 @@ package com.google.inject.servlet;
 
 import java.io.IOException;
 
+import javax.inject.Inject;
 import javax.servlet.FilterChain;
 import javax.servlet.ServletContext;
 import javax.servlet.ServletException;
@@ -30,6 +31,9 @@ import javax.servlet.ServletResponse;
  * @see com.google.inject.servlet.ManagedFilterPipeline See Also ManagedFilterPipeline.
  */
 class DefaultFilterPipeline implements FilterPipeline {
+  @Inject DefaultFilterPipeline() {
+  }
+
   public void initPipeline(ServletContext context) {
   }
 
diff --git a/extensions/servlet/src/com/google/inject/servlet/FilterChainInvocation.java b/extensions/servlet/src/com/google/inject/servlet/FilterChainInvocation.java
index ae6d088..b4112cf 100755
--- a/extensions/servlet/src/com/google/inject/servlet/FilterChainInvocation.java
+++ b/extensions/servlet/src/com/google/inject/servlet/FilterChainInvocation.java
@@ -23,6 +23,7 @@ import java.io.IOException;
 import java.util.List;
 import java.util.Set;
 
+import javax.servlet.Filter;
 import javax.servlet.FilterChain;
 import javax.servlet.ServletException;
 import javax.servlet.ServletRequest;
@@ -45,7 +46,6 @@ import javax.servlet.http.HttpServletResponse;
 class FilterChainInvocation implements FilterChain {
   
   private static final Set<String> SERVLET_INTERNAL_METHODS = ImmutableSet.of(
-      FilterDefinition.class.getName() + ".doFilter",
       FilterChainInvocation.class.getName() + ".doFilter");
   
   private final FilterDefinition[] filterDefinitions;
@@ -67,8 +67,6 @@ class FilterChainInvocation implements FilterChain {
 
   public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse)
       throws IOException, ServletException {
-    index++;
-
     GuiceFilter.Context previous = GuiceFilter.localContext.get();
     HttpServletRequest request = (HttpServletRequest) servletRequest;
     HttpServletResponse response = (HttpServletResponse) servletResponse;
@@ -76,9 +74,12 @@ class FilterChainInvocation implements FilterChain {
         = (previous != null) ? previous.getOriginalRequest() : request;
     GuiceFilter.localContext.set(new GuiceFilter.Context(originalRequest, request, response));
     try {
-      //dispatch down the chain while there are more filters
-      if (index < filterDefinitions.length) {
-        filterDefinitions[index].doFilter(servletRequest, servletResponse, this);
+      Filter filter = findNextFilter(request);
+      if (filter != null) {
+        // call to the filter, which can either consume the request or
+        // recurse back into this method. (in which case we will go to find the next filter,
+        // or dispatch to the servlet if no more filters are left)
+        filter.doFilter(servletRequest, servletResponse, this);
       } else {
         //we've reached the end of the filterchain, let's try to dispatch to a servlet
         final boolean serviced = servletPipeline.service(servletRequest, servletResponse);
@@ -103,6 +104,20 @@ class FilterChainInvocation implements FilterChain {
       GuiceFilter.localContext.set(previous);
     }
   }
+
+  /**
+   * Iterates over the remaining filter definitions.
+   * Returns the first applicable filter, or null if none apply.
+   */
+  private Filter findNextFilter(HttpServletRequest request) {
+    while (++index < filterDefinitions.length) {
+      Filter filter = filterDefinitions[index].getFilterIfMatching(request);
+      if (filter != null) {
+        return filter;
+      }
+    }
+    return null;
+  }
   
   /**
    * Removes stacktrace elements related to AOP internal mechanics from the
diff --git a/extensions/servlet/src/com/google/inject/servlet/FilterDefinition.java b/extensions/servlet/src/com/google/inject/servlet/FilterDefinition.java
index ee42b14..ff1e5b6 100755
--- a/extensions/servlet/src/com/google/inject/servlet/FilterDefinition.java
+++ b/extensions/servlet/src/com/google/inject/servlet/FilterDefinition.java
@@ -23,7 +23,6 @@ import com.google.inject.spi.BindingTargetVisitor;
 import com.google.inject.spi.ProviderInstanceBinding;
 import com.google.inject.spi.ProviderWithExtensionVisitor;
 
-import java.io.IOException;
 import java.util.Collections;
 import java.util.Enumeration;
 import java.util.HashMap;
@@ -35,8 +34,6 @@ import javax.servlet.Filter;
 import javax.servlet.FilterConfig;
 import javax.servlet.ServletContext;
 import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
 import javax.servlet.http.HttpServletRequest;
 
 /**
@@ -63,11 +60,11 @@ class FilterDefinition implements ProviderWithExtensionVisitor<FilterDefinition>
     this.initParams = Collections.unmodifiableMap(new HashMap<String, String>(initParams));
     this.filterInstance = filterInstance;
   }
-  
+
   public FilterDefinition get() {
     return this;
   }
-  
+
   public <B, V> V acceptExtensionVisitor(BindingTargetVisitor<B, V> visitor,
       ProviderInstanceBinding<? extends B> binding) {
     if(visitor instanceof ServletModuleTargetVisitor) {
@@ -76,7 +73,7 @@ class FilterDefinition implements ProviderWithExtensionVisitor<FilterDefinition>
             new InstanceFilterBindingImpl(initParams,
                 pattern,
                 filterInstance,
-                patternMatcher));        
+                patternMatcher));
       } else {
         return ((ServletModuleTargetVisitor<B, V>)visitor).visit(
             new LinkedFilterBindingImpl(initParams,
@@ -90,7 +87,7 @@ class FilterDefinition implements ProviderWithExtensionVisitor<FilterDefinition>
   }
 
   private boolean shouldFilter(String uri) {
-    return patternMatcher.matches(uri);
+    return uri != null && patternMatcher.matches(uri);
   }
 
   public void init(final ServletContext servletContext, Injector injector,
@@ -136,7 +133,7 @@ class FilterDefinition implements ProviderWithExtensionVisitor<FilterDefinition>
   public void destroy(Set<Filter> destroyedSoFar) {
     // filters are always singletons
     Filter reference = filter.get();
-    
+
     // Do nothing if this Filter was invalid (usually due to not being scoped
     // properly), or was already destroyed. According to Servlet Spec: it is
     // "out of service", and does not need to be destroyed.
@@ -153,20 +150,13 @@ class FilterDefinition implements ProviderWithExtensionVisitor<FilterDefinition>
     }
   }
 
-  public void doFilter(ServletRequest servletRequest,
-      ServletResponse servletResponse, FilterChainInvocation filterChainInvocation)
-      throws IOException, ServletException {
-
-    final HttpServletRequest request = (HttpServletRequest) servletRequest;
-    final String path = request.getRequestURI().substring(request.getContextPath().length());
+  public Filter getFilterIfMatching(HttpServletRequest request) {
 
+    final String path = ServletUtils.getContextRelativePath(request);
     if (shouldFilter(path)) {
-      filter.get()
-            .doFilter(servletRequest, servletResponse, filterChainInvocation);
-
+      return filter.get();
     } else {
-      //otherwise proceed down chain anyway
-      filterChainInvocation.doFilter(servletRequest, servletResponse);
+      return null;
     }
   }
 
diff --git a/extensions/servlet/src/com/google/inject/servlet/GuiceFilter.java b/extensions/servlet/src/com/google/inject/servlet/GuiceFilter.java
index 2707d08..2913e55 100644
--- a/extensions/servlet/src/com/google/inject/servlet/GuiceFilter.java
+++ b/extensions/servlet/src/com/google/inject/servlet/GuiceFilter.java
@@ -16,11 +16,14 @@
 
 package com.google.inject.servlet;
 
+import com.google.common.base.Preconditions;
+import com.google.common.base.Throwables;
 import com.google.inject.Inject;
 import com.google.inject.OutOfScopeException;
 
 import java.io.IOException;
 import java.lang.ref.WeakReference;
+import java.util.concurrent.Callable;
 import java.util.logging.Logger;
 
 import javax.servlet.Filter;
@@ -109,23 +112,33 @@ public class GuiceFilter implements Filter {
     localContext.remove();
   }
 
-  public void doFilter(ServletRequest servletRequest,
-      ServletResponse servletResponse, FilterChain filterChain)
+  public void doFilter(
+      final ServletRequest servletRequest,
+      final ServletResponse servletResponse,
+      final FilterChain filterChain)
       throws IOException, ServletException {
 
-    FilterPipeline filterPipeline = getFilterPipeline();
+    final FilterPipeline filterPipeline = getFilterPipeline();
 
     Context previous = GuiceFilter.localContext.get();
     HttpServletRequest request = (HttpServletRequest) servletRequest;
     HttpServletResponse response = (HttpServletResponse) servletResponse;
     HttpServletRequest originalRequest
         = (previous != null) ? previous.getOriginalRequest() : request;
-    localContext.set(new Context(originalRequest, request, response));
     try {
-      //dispatch across the servlet pipeline, ensuring web.xml's filterchain is honored
-      filterPipeline.dispatch(servletRequest, servletResponse, filterChain);
-    } finally {
-      localContext.set(previous);
+      new Context(originalRequest, request, response).call(new Callable<Void>() {
+        @Override public Void call() throws Exception {
+          //dispatch across the servlet pipeline, ensuring web.xml's filterchain is honored
+          filterPipeline.dispatch(servletRequest, servletResponse, filterChain);
+          return null;
+        }
+      });
+    } catch (IOException e) {
+      throw e;
+    } catch (ServletException e) {
+      throw e;
+    } catch (Exception e) {
+      Throwables.propagate(e);
     }
   }
 
@@ -160,6 +173,7 @@ public class GuiceFilter implements Filter {
     final HttpServletRequest originalRequest;
     final HttpServletRequest request;
     final HttpServletResponse response;
+    volatile Thread owner;
 
     Context(HttpServletRequest originalRequest, HttpServletRequest request,
         HttpServletResponse response) {
@@ -179,6 +193,22 @@ public class GuiceFilter implements Filter {
     HttpServletResponse getResponse() {
       return response;
     }
+
+    <T> T call(Callable<T> callable) throws Exception {
+      Thread oldOwner = owner;
+      Thread newOwner = Thread.currentThread();
+      Preconditions.checkState(oldOwner == null || oldOwner == newOwner,
+          "Trying to transfer request scope but original scope is still active");
+      owner = newOwner;
+      Context previous = localContext.get();
+      localContext.set(this);
+      try {
+        return callable.call();
+      } finally {
+        owner = oldOwner;
+        localContext.set(previous);
+      }
+    }
   }
 
   public void init(FilterConfig filterConfig) throws ServletException {
diff --git a/extensions/servlet/src/com/google/inject/servlet/InternalServletModule.java b/extensions/servlet/src/com/google/inject/servlet/InternalServletModule.java
index 9bd650d..8043ca9 100644
--- a/extensions/servlet/src/com/google/inject/servlet/InternalServletModule.java
+++ b/extensions/servlet/src/com/google/inject/servlet/InternalServletModule.java
@@ -19,6 +19,7 @@ import static com.google.inject.servlet.ServletScopes.REQUEST;
 import static com.google.inject.servlet.ServletScopes.SESSION;
 
 import com.google.inject.AbstractModule;
+import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Provides;
 import com.google.inject.Singleton;
@@ -51,6 +52,8 @@ final class InternalServletModule extends AbstractModule {
   static class BackwardsCompatibleServletContextProvider implements Provider<ServletContext> {
     private ServletContext injectedServletContext;
 
+    @Inject BackwardsCompatibleServletContextProvider() {}
+
     // This setter is called by the GuiceServletContextListener
     void set(ServletContext injectedServletContext) {
       this.injectedServletContext = injectedServletContext;
diff --git a/core/test/com/google/inject/spi/ElementApplyToTest.java b/extensions/servlet/src/com/google/inject/servlet/ScopingException.java
similarity index 58%
copy from core/test/com/google/inject/spi/ElementApplyToTest.java
copy to extensions/servlet/src/com/google/inject/servlet/ScopingException.java
index baa8ca4..22f90f1 100644
--- a/core/test/com/google/inject/spi/ElementApplyToTest.java
+++ b/extensions/servlet/src/com/google/inject/servlet/ScopingException.java
@@ -1,5 +1,5 @@
 /**
- * Copyright (C) 2008 Google Inc.
+ * Copyright (C) 2012 Google Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,17 +14,15 @@
  * limitations under the License.
  */
 
-package com.google.inject.spi;
-
-import com.google.inject.Module;
+package com.google.inject.servlet;
 
 /**
- * @author jessewilson at google.com (Jesse Wilson)
+ * Exception thrown when there was a failure entering request scope.
+ *
+ * @author Chris Nokleberg
  */
-public class ElementApplyToTest extends ElementsTest {
-
-  protected void checkModule(Module module, ElementVisitor<?>... visitors) {
-    // convert from module to elements and back
-    super.checkModule(Elements.getModule(Elements.getElements(module)), visitors);
+public final class ScopingException extends IllegalStateException {
+  public ScopingException(String message) {
+    super(message);
   }
-}
\ No newline at end of file
+}
diff --git a/extensions/servlet/src/com/google/inject/servlet/ServletDefinition.java b/extensions/servlet/src/com/google/inject/servlet/ServletDefinition.java
index 00ac328..1111fde 100755
--- a/extensions/servlet/src/com/google/inject/servlet/ServletDefinition.java
+++ b/extensions/servlet/src/com/google/inject/servlet/ServletDefinition.java
@@ -68,11 +68,11 @@ class ServletDefinition implements ProviderWithExtensionVisitor<ServletDefinitio
     this.initParams = Collections.unmodifiableMap(new HashMap<String, String>(initParams));
     this.servletInstance = servletInstance;
   }
-  
+
   public ServletDefinition get() {
     return this;
   }
-  
+
   public <B, V> V acceptExtensionVisitor(BindingTargetVisitor<B, V> visitor,
       ProviderInstanceBinding<? extends B> binding) {
     if(visitor instanceof ServletModuleTargetVisitor) {
@@ -81,7 +81,7 @@ class ServletDefinition implements ProviderWithExtensionVisitor<ServletDefinitio
             new InstanceServletBindingImpl(initParams,
                 pattern,
                 servletInstance,
-                patternMatcher));        
+                patternMatcher));
       } else {
         return ((ServletModuleTargetVisitor<B, V>)visitor).visit(
             new LinkedServletBindingImpl(initParams,
@@ -95,7 +95,7 @@ class ServletDefinition implements ProviderWithExtensionVisitor<ServletDefinitio
   }
 
   boolean shouldServe(String uri) {
-    return patternMatcher.matches(uri);
+    return uri != null && patternMatcher.matches(uri);
   }
 
   public void init(final ServletContext servletContext, Injector injector,
@@ -163,7 +163,7 @@ class ServletDefinition implements ProviderWithExtensionVisitor<ServletDefinitio
    *
    * @return Returns true if this servlet triggered for the given request. Or false if
    *          guice-servlet should continue dispatching down the servlet pipeline.
-   * 
+   *
    * @throws IOException If thrown by underlying servlet
    * @throws ServletException If thrown by underlying servlet
    */
@@ -171,7 +171,7 @@ class ServletDefinition implements ProviderWithExtensionVisitor<ServletDefinitio
       ServletResponse servletResponse) throws IOException, ServletException {
 
     final HttpServletRequest request = (HttpServletRequest) servletRequest;
-    final String path = request.getRequestURI().substring(request.getContextPath().length());
+    final String path = ServletUtils.getContextRelativePath(request);
 
     final boolean serve = shouldServe(path);
 
diff --git a/extensions/servlet/src/com/google/inject/servlet/ServletScopes.java b/extensions/servlet/src/com/google/inject/servlet/ServletScopes.java
index 156e9c6..5acf653 100644
--- a/extensions/servlet/src/com/google/inject/servlet/ServletScopes.java
+++ b/extensions/servlet/src/com/google/inject/servlet/ServletScopes.java
@@ -26,11 +26,7 @@ import com.google.inject.OutOfScopeException;
 import com.google.inject.Provider;
 import com.google.inject.Scope;
 import com.google.inject.Scopes;
-import com.google.inject.internal.LinkedBindingImpl;
-import com.google.inject.spi.BindingScopingVisitor;
-import com.google.inject.spi.ExposedBinding;
 
-import java.lang.annotation.Annotation;
 import java.util.Map;
 import java.util.concurrent.Callable;
 
@@ -56,10 +52,10 @@ public class ServletScopes {
   /**
    * A threadlocal scope map for non-http request scopes. The {@link #REQUEST}
    * scope falls back to this scope map if no http request is available, and
-   * requires {@link #scopeRequest} to be called as an alertnative.
+   * requires {@link #scopeRequest} to be called as an alternative.
    */
-  private static final ThreadLocal<Map<String, Object>> requestScopeContext
-      = new ThreadLocal<Map<String, Object>>();
+  private static final ThreadLocal<Context> requestScopeContext
+      = new ThreadLocal<Context>();
 
   /** A sentinel attribute value representing null. */
   enum NullObject { INSTANCE }
@@ -69,7 +65,6 @@ public class ServletScopes {
    */
   public static final Scope REQUEST = new Scope() {
     public <T> Provider<T> scope(final Key<T> key, final Provider<T> creator) {
-      final String name = key.toString();
       return new Provider<T>() {
         public T get() {
           // Check if the alternate request scope should be used, if no HTTP
@@ -79,10 +74,10 @@ public class ServletScopes {
             // NOTE(dhanji): We don't need to synchronize on the scope map
             // unlike the HTTP request because we're the only ones who have
             // a reference to it, and it is only available via a threadlocal.
-            Map<String, Object> scopeMap = requestScopeContext.get();
-            if (null != scopeMap) {
+            Context context = requestScopeContext.get();
+            if (null != context) {
               @SuppressWarnings("unchecked")
-              T t = (T) scopeMap.get(name);
+              T t = (T) context.map.get(key);
 
               // Accounts for @Nullable providers.
               if (NullObject.INSTANCE == t) {
@@ -93,7 +88,7 @@ public class ServletScopes {
                 t = creator.get();
                 if (!Scopes.isCircularProxy(t)) {
                   // Store a sentinel for provider-given null values.
-                  scopeMap.put(name, t != null ? t : NullObject.INSTANCE);
+                  context.map.put(key, t != null ? t : NullObject.INSTANCE);
                 }
               }
 
@@ -113,6 +108,7 @@ public class ServletScopes {
             // GuiceFilter itself.
             return creator.get();
           }
+          String name = key.toString();
           synchronized (request) {
             Object obj = request.getAttribute(name);
             if (NullObject.INSTANCE == obj) {
@@ -198,6 +194,9 @@ public class ServletScopes {
    *      are not available.</li>
    * </ul>
    *
+   * <p>The returned callable will throw a {@link ScopingException} when called
+   * if the HTTP request scope is still active on the current thread.
+   *
    * @param callable code to be executed in another thread, which depends on
    *     the request scope.
    * @param seedMap the initial set of scoped instances for Guice to seed the
@@ -214,7 +213,7 @@ public class ServletScopes {
       final Map<Key<?>, Object> seedMap) {
     Preconditions.checkArgument(null != seedMap,
         "Seed map cannot be null, try passing in Collections.emptyMap() instead.");
-    
+
     // Snapshot the seed map and add all the instances to our continuing HTTP request.
     final ContinuingHttpServletRequest continuingRequest =
         new ContinuingHttpServletRequest(GuiceFilter.getRequest());
@@ -224,24 +223,68 @@ public class ServletScopes {
     }
 
     return new Callable<T>() {
-      private final HttpServletRequest request = continuingRequest;
-
       public T call() throws Exception {
-        GuiceFilter.Context context = GuiceFilter.localContext.get();
-        Preconditions.checkState(null == context,
+        checkScopingState(null == GuiceFilter.localContext.get(),
             "Cannot continue request in the same thread as a HTTP request!");
+        return new GuiceFilter.Context(continuingRequest, continuingRequest, null)
+            .call(callable);
+      }
+    };
+  }
 
-        // Only set up the request continuation if we're running in a
-        // new vanilla thread.
-        GuiceFilter.localContext.set(new GuiceFilter.Context(request, request, null));
-        try {
-          return callable.call();
-        } finally {
-          // Clear the copied context if we set one up.
-          if (null == context) {
-            GuiceFilter.localContext.remove();
-          }
-        }
+  /**
+   * Wraps the given callable in a contextual callable that "transfers" the
+   * request to another thread. This acts as a way of transporting
+   * request context data from the current thread to a future thread.
+   *
+   * <p>As opposed to {@link #continueRequest}, this method propagates all
+   * existing scoped objects. The primary use case is in server implementations
+   * where you can detach the request processing thread while waiting for data,
+   * and reattach to a different thread to finish processing at a later time.
+   *
+   * <p>Because {@code HttpServletRequest} objects are not typically
+   * thread-safe, the callable returned by this method must not be run on a
+   * different thread until the current request scope has terminated. In other
+   * words, do not use this method to propagate the current request scope to
+   * worker threads that may run concurrently with the current thread.
+   *
+   * <p>The returned callable will throw a {@link ScopingException} when called
+   * if the request scope being transferred is still active on a different
+   * thread.
+   *
+   * @param callable code to be executed in another thread, which depends on
+   *     the request scope.
+   * @return a callable that will invoke the given callable, making the request
+   *     context available to it.
+   * @throws OutOfScopeException if this method is called from a non-request
+   *     thread, or if the request has completed.
+   */
+  public static <T> Callable<T> transferRequest(Callable<T> callable) {
+    return (GuiceFilter.localContext.get() != null)
+        ? transferHttpRequest(callable)
+        : transferNonHttpRequest(callable);
+  }
+
+  private static <T> Callable<T> transferHttpRequest(final Callable<T> callable) {
+    final GuiceFilter.Context context = GuiceFilter.localContext.get();
+    if (context == null) {
+      throw new OutOfScopeException("Not in a request scope");
+    }
+    return new Callable<T>() {
+      public T call() throws Exception {
+        return context.call(callable);
+      }
+    };
+  }
+
+  private static <T> Callable<T> transferNonHttpRequest(final Callable<T> callable) {
+    final Context context = requestScopeContext.get();
+    if (context == null) {
+      throw new OutOfScopeException("Not in a request scope");
+    }
+    return new Callable<T>() {
+      public T call() throws Exception {
+        return context.call(callable);
       }
     };
   }
@@ -254,51 +297,7 @@ public class ServletScopes {
    * also return true if the target binding is request-scoped.
    */
   public static boolean isRequestScoped(Binding<?> binding) {
-    do {
-      boolean requestScoped = binding.acceptScopingVisitor(new BindingScopingVisitor<Boolean>() {
-        @Override
-        public Boolean visitNoScoping() {
-          return false;
-        }
-
-        @Override
-        public Boolean visitScopeAnnotation(Class<? extends Annotation> scopeAnnotation) {
-          return scopeAnnotation == RequestScoped.class;
-        }
-
-        @Override
-        public Boolean visitScope(Scope scope) {
-          return scope == ServletScopes.REQUEST;
-        }
-
-        @Override
-        public Boolean visitEagerSingleton() {
-          return false;
-        }
-      });
-
-      if (requestScoped) {
-        return true;
-      }
-
-      if (binding instanceof LinkedBindingImpl) {
-        LinkedBindingImpl<?> linkedBinding = (LinkedBindingImpl<?>) binding;
-        Injector injector = linkedBinding.getInjector();
-        if (injector != null) {
-          binding = injector.getBinding(linkedBinding.getLinkedKey());
-          continue;
-        }
-      } else if (binding instanceof ExposedBinding) {
-        ExposedBinding<?> exposedBinding = (ExposedBinding<?>) binding;
-        Injector injector = exposedBinding.getPrivateElements().getInjector();
-        if (injector != null) {
-          binding = injector.getBinding(exposedBinding.getKey());
-          continue;
-        }
-      }
-
-      return false;
-    } while (true);
+    return Scopes.isScoped(binding, ServletScopes.REQUEST, RequestScoped.class);
   }
 
   /**
@@ -308,6 +307,9 @@ public class ServletScopes {
    * in non-HTTP requests (for example: RPC requests) as well as in HTTP
    * request threads.
    *
+   * <p>The returned callable will throw a {@link ScopingException} when called
+   * if there is a request scope already active on the current thread.
+   *
    * @param callable code to be executed which depends on the request scope.
    *     Typically in another thread, but not necessarily so.
    * @param seedMap the initial set of scoped instances for Guice to seed the
@@ -323,26 +325,19 @@ public class ServletScopes {
         "Seed map cannot be null, try passing in Collections.emptyMap() instead.");
 
     // Copy the seed values into our local scope map.
-    final Map<String, Object> scopeMap = Maps.newHashMap();
+    final Context context = new Context();
     for (Map.Entry<Key<?>, Object> entry : seedMap.entrySet()) {
       Object value = validateAndCanonicalizeValue(entry.getKey(), entry.getValue());
-      scopeMap.put(entry.getKey().toString(), value);
+      context.map.put(entry.getKey(), value);
     }
 
     return new Callable<T>() {
       public T call() throws Exception {
-        Preconditions.checkState(null == GuiceFilter.localContext.get(),
+        checkScopingState(null == GuiceFilter.localContext.get(),
             "An HTTP request is already in progress, cannot scope a new request in this thread.");
-        Preconditions.checkState(null == requestScopeContext.get(),
+        checkScopingState(null == requestScopeContext.get(),
             "A request scope is already in progress, cannot scope a new request in this thread.");
-
-        requestScopeContext.set(scopeMap);
-
-        try {
-          return callable.call();
-        } finally {
-          requestScopeContext.remove();
-        }
+        return context.call(callable);
       }
     };
   }
@@ -363,4 +358,31 @@ public class ServletScopes {
 
     return object;
   }
+
+  private static class Context {
+    final Map<Key, Object> map = Maps.newHashMap();
+    volatile Thread owner;
+
+    <T> T call(Callable<T> callable) throws Exception {
+      Thread oldOwner = owner;
+      Thread newOwner = Thread.currentThread();
+      checkScopingState(oldOwner == null || oldOwner == newOwner,
+          "Trying to transfer request scope but original scope is still active");
+      owner = newOwner;
+      Context previous = requestScopeContext.get();
+      requestScopeContext.set(this);
+      try {
+        return callable.call();
+      } finally {
+        owner = oldOwner;
+        requestScopeContext.set(previous);
+      }
+    }
+  }
+
+  private static void checkScopingState(boolean condition, String msg) {
+    if (!condition) {
+      throw new ScopingException(msg);
+    }
+  }
 }
diff --git a/extensions/servlet/src/com/google/inject/servlet/ServletUtils.java b/extensions/servlet/src/com/google/inject/servlet/ServletUtils.java
new file mode 100644
index 0000000..88ecd31
--- /dev/null
+++ b/extensions/servlet/src/com/google/inject/servlet/ServletUtils.java
@@ -0,0 +1,52 @@
+/**
+ * Copyright (C) 2012 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.servlet;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * Some servlet utility methods.
+ *
+ * @author ntang at google.com (Michael Tang)
+ */
+final class ServletUtils {
+  private ServletUtils() {
+    // private to prevent instantiation.
+  }
+
+  /**
+   * Gets the context path relative path of the URI. Returns the path of the
+   * resource relative to the context path for a request's URI, or null if no
+   * path can be extracted.
+   */
+  // @Nullable
+  public static String getContextRelativePath(
+      // @Nullable
+      final HttpServletRequest request) {
+    if (request != null) {
+      String contextPath = request.getContextPath();
+      String requestURI = request.getRequestURI();
+      if (contextPath.length() < requestURI.length()) {
+        return requestURI.substring(contextPath.length());
+      } else if (requestURI != null && requestURI.trim().length() > 0 &&
+          contextPath.length() == requestURI.length()) {
+        return "/";
+      }
+    }
+    return null;
+  }
+}
diff --git a/extensions/servlet/test/com/google/inject/servlet/AllTests.java b/extensions/servlet/test/com/google/inject/servlet/AllTests.java
index 7eb6446..a002d6a 100644
--- a/extensions/servlet/test/com/google/inject/servlet/AllTests.java
+++ b/extensions/servlet/test/com/google/inject/servlet/AllTests.java
@@ -41,6 +41,7 @@ public class AllTests {
     suite.addTestSuite(ServletPipelineRequestDispatcherTest.class);
     suite.addTestSuite(ServletDispatchIntegrationTest.class);
     suite.addTestSuite(InvalidScopeBindingTest.class);
+    suite.addTestSuite(ContinuingHttpServletRequestTest.class);
 
     // Varargs URL mapping tests.
     suite.addTestSuite(VarargsFilterDispatchIntegrationTest.class);
diff --git a/extensions/servlet/test/com/google/inject/servlet/ContinuingHttpServletRequestTest.java b/extensions/servlet/test/com/google/inject/servlet/ContinuingHttpServletRequestTest.java
new file mode 100644
index 0000000..6063cb8
--- /dev/null
+++ b/extensions/servlet/test/com/google/inject/servlet/ContinuingHttpServletRequestTest.java
@@ -0,0 +1,58 @@
+/**
+ * Copyright (C) 2013 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.servlet;
+
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+
+import junit.framework.TestCase;
+
+import java.util.Arrays;
+
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+
+public class ContinuingHttpServletRequestTest extends TestCase {
+
+  public void testReturnNullCookiesIfDelegateHasNoNull() {
+    HttpServletRequest delegate = createMock(HttpServletRequest.class);
+    expect(delegate.getCookies()).andStubReturn(null);
+
+    replay(delegate);
+
+    assertNull(new ContinuingHttpServletRequest(delegate).getCookies());
+
+    verify(delegate);
+  }
+  
+  public void testReturnDelegateCookies() {
+    Cookie[] cookies = new Cookie[]{
+        new Cookie("testName1", "testValue1"),
+        new Cookie("testName2", "testValue2")
+    };
+    HttpServletRequest delegate = createMock(HttpServletRequest.class);
+    expect(delegate.getCookies()).andStubReturn(cookies);
+
+    replay(delegate);
+
+    assertTrue(Arrays.equals(cookies,
+        new ContinuingHttpServletRequest(delegate).getCookies()));
+
+    verify(delegate);
+  }
+}
\ No newline at end of file
diff --git a/extensions/servlet/test/com/google/inject/servlet/FilterDefinitionTest.java b/extensions/servlet/test/com/google/inject/servlet/FilterDefinitionTest.java
index 9d94c75..c151102 100644
--- a/extensions/servlet/test/com/google/inject/servlet/FilterDefinitionTest.java
+++ b/extensions/servlet/test/com/google/inject/servlet/FilterDefinitionTest.java
@@ -6,6 +6,7 @@ import static org.easymock.EasyMock.expect;
 import static org.easymock.EasyMock.replay;
 import static org.easymock.EasyMock.verify;
 
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
 import com.google.inject.Binding;
@@ -28,6 +29,7 @@ import javax.servlet.ServletException;
 import javax.servlet.ServletRequest;
 import javax.servlet.ServletResponse;
 import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
 
 /**
  * Tests the lifecycle of the encapsulated {@link FilterDefinition} class.
@@ -56,14 +58,9 @@ public class FilterDefinitionTest extends TestCase {
 
     //some init params
     //noinspection SSBasedInspection
-    final Map<String, String> initParams = new HashMap<String, String>() {{
-      put("ahsd", "asdas24dok");
-      put("ahssd", "asdasd124ok");
-      put("ahfsasd", "asda124sdok");
-      put("ahsasgd", "a124sdasdok");
-      put("ahsd124124", "as124124124dasdok");
-    }};
-
+    final Map<String, String> initParams = new ImmutableMap.Builder<String, String>()
+      .put("ahsd", "asdas24dok")
+      .put("ahssd", "asdasd124ok").build();
 
     ServletContext servletContext = createMock(ServletContext.class);
     final String contextName = "thing__!@@44";
@@ -90,7 +87,7 @@ public class FilterDefinitionTest extends TestCase {
       assertTrue(initParams.containsKey(name));
       assertTrue(initParams.get(name).equals(filterConfig.getInitParameter(name)));
     }
-    
+
     verify(binding, injector, servletContext);
   }
 
@@ -128,8 +125,11 @@ public class FilterDefinitionTest extends TestCase {
 
     assertTrue("Init did not fire", mockFilter.isInit());
 
+    Filter matchingFilter = filterDef.getFilterIfMatching(request);
+    assertSame(mockFilter, matchingFilter);
+
     final boolean proceed[] = new boolean[1];
-    filterDef.doFilter(request, null, new FilterChainInvocation(null, null, null) {
+    matchingFilter.doFilter(request, null, new FilterChainInvocation(null, null, null) {
       @Override
       public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse) {
         proceed[0] = true;
@@ -164,7 +164,7 @@ public class FilterDefinitionTest extends TestCase {
         .andReturn(true);
     expect(injector.getBinding(Key.get(Filter.class)))
         .andReturn(binding);
-    
+
     expect(injector.getInstance(Key.get(Filter.class)))
         .andReturn(mockFilter)
         .anyTimes();
@@ -188,8 +188,11 @@ public class FilterDefinitionTest extends TestCase {
 
     assertTrue("init did not fire", mockFilter.isInit());
 
+    Filter matchingFilter = filterDef.getFilterIfMatching(request);
+    assertSame(mockFilter, matchingFilter);
+
     final boolean proceed[] = new boolean[1];
-    filterDef.doFilter(request, null, new FilterChainInvocation(null, null, null) {
+    matchingFilter.doFilter(request, null, new FilterChainInvocation(null, null, null) {
       @Override
       public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse) {
         proceed[0] = true;
@@ -205,6 +208,78 @@ public class FilterDefinitionTest extends TestCase {
 
   }
 
+  public void testGetFilterIfMatching() throws ServletException {
+    String pattern = "/*";
+    final FilterDefinition filterDef = new FilterDefinition(pattern, Key.get(Filter.class),
+        UriPatternType.get(UriPatternType.SERVLET, pattern),
+        new HashMap<String, String>(), null);
+    HttpServletRequest servletRequest = createMock(HttpServletRequest.class);
+    ServletContext servletContext = createMock(ServletContext.class);
+    Injector injector = createMock(Injector.class);
+    Binding binding = createMock(Binding.class);
+
+    final MockFilter mockFilter = new MockFilter() {
+      @Override
+      public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
+          FilterChain filterChain) {
+        //suppress rest of chain...
+      }
+    };
+    expect(injector.getBinding(Key.get(Filter.class)))
+        .andReturn(binding);
+    expect(binding.acceptScopingVisitor((BindingScopingVisitor) anyObject()))
+        .andReturn(true);
+    expect(injector.getInstance(Key.get(Filter.class)))
+        .andReturn(mockFilter)
+        .anyTimes();
+
+    expect(servletRequest.getContextPath()).andReturn("/a_context_path");
+    expect(servletRequest.getRequestURI()).andReturn("/a_context_path/test.html");
+
+    replay(servletRequest, binding, injector);
+    filterDef.init(servletContext, injector,
+        Sets.newSetFromMap(Maps.<Filter, Boolean>newIdentityHashMap()));
+    Filter filter = filterDef.getFilterIfMatching(servletRequest);
+    assertSame(filter, mockFilter);
+    verify(servletRequest, binding, injector);
+  }
+
+  public void testGetFilterIfMatchingNotMatching() throws ServletException {
+    String pattern = "/*";
+    final FilterDefinition filterDef = new FilterDefinition(pattern, Key.get(Filter.class),
+        UriPatternType.get(UriPatternType.SERVLET, pattern),
+        new HashMap<String, String>(), null);
+    HttpServletRequest servletRequest = createMock(HttpServletRequest.class);
+    ServletContext servletContext = createMock(ServletContext.class);
+    Injector injector = createMock(Injector.class);
+    Binding binding = createMock(Binding.class);
+
+    final MockFilter mockFilter = new MockFilter() {
+      @Override
+      public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
+          FilterChain filterChain) {
+        //suppress rest of chain...
+      }
+    };
+    expect(injector.getBinding(Key.get(Filter.class)))
+        .andReturn(binding);
+    expect(binding.acceptScopingVisitor((BindingScopingVisitor) anyObject()))
+        .andReturn(true);
+    expect(injector.getInstance(Key.get(Filter.class)))
+        .andReturn(mockFilter)
+        .anyTimes();
+
+    expect(servletRequest.getContextPath()).andReturn("/a_context_path");
+    expect(servletRequest.getRequestURI()).andReturn("/test.html");
+
+    replay(servletRequest, binding, injector);
+    filterDef.init(servletContext, injector,
+        Sets.newSetFromMap(Maps.<Filter, Boolean>newIdentityHashMap()));
+    Filter filter = filterDef.getFilterIfMatching(servletRequest);
+    assertNull(filter);
+    verify(servletRequest, binding, injector);
+  }
+
   private static class MockFilter implements Filter {
     private boolean init;
     private boolean destroy;
diff --git a/extensions/servlet/test/com/google/inject/servlet/ServletDefinitionTest.java b/extensions/servlet/test/com/google/inject/servlet/ServletDefinitionTest.java
index 7ebc521..1a8224a 100644
--- a/extensions/servlet/test/com/google/inject/servlet/ServletDefinitionTest.java
+++ b/extensions/servlet/test/com/google/inject/servlet/ServletDefinitionTest.java
@@ -22,6 +22,7 @@ import static org.easymock.EasyMock.expect;
 import static org.easymock.EasyMock.replay;
 import static org.easymock.EasyMock.verify;
 
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
 import com.google.inject.Binding;
@@ -31,14 +32,16 @@ import com.google.inject.spi.BindingScopingVisitor;
 
 import junit.framework.TestCase;
 
+import java.io.IOException;
 import java.util.Enumeration;
-import java.util.HashMap;
 import java.util.Map;
 
 import javax.servlet.ServletConfig;
 import javax.servlet.ServletContext;
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
 
 /**
  * Basic unit test for lifecycle of a ServletDefinition (wrapper).
@@ -65,15 +68,9 @@ public class ServletDefinitionTest extends TestCase {
 
     //some init params
     //noinspection SSBasedInspection
-    final Map<String, String> initParams = new HashMap<String, String>() {
-      {
-        put("ahsd", "asdas24dok");
-        put("ahssd", "asdasd124ok");
-        put("ahfsasd", "asda124sdok");
-        put("ahsasgd", "a124sdasdok");
-        put("ahsd124124", "as124124124dasdok");
-      }
-    };
+    final Map<String, String> initParams = new ImmutableMap.Builder<String, String>()
+      .put("ahsd", "asdas24dok")
+      .put("ahssd", "asdasd124ok").build();
 
     String pattern = "/*";
     final ServletDefinition servletDefinition = new ServletDefinition(pattern,
@@ -101,7 +98,28 @@ public class ServletDefinitionTest extends TestCase {
       assertTrue(initParams.containsKey(name));
       assertEquals(initParams.get(name), servletConfig.getInitParameter(name));
     }
-    
+
     verify(injector, binding, servletContext);
   }
+
+  public void testServiceWithContextPath() throws IOException, ServletException   {
+    String pattern = "/*";
+    //some init params
+    Map<String, String> initParams = new ImmutableMap.Builder<String, String>()
+        .put("ahsd", "asdas24dok")
+        .put("ahssd", "asdasd124ok")
+        .build();
+
+    final ServletDefinition servletDefinition = new ServletDefinition(pattern,
+        Key.get(HttpServlet.class), UriPatternType.get(UriPatternType.SERVLET, pattern),
+        initParams, null);
+    HttpServletResponse servletResponse = createMock(HttpServletResponse.class);
+    HttpServletRequest servletRequest = createMock(HttpServletRequest.class);
+
+    expect(servletRequest.getContextPath()).andReturn("/a_context_path");
+    expect(servletRequest.getRequestURI()).andReturn("/test.html");
+    replay(servletRequest, servletResponse);
+    servletDefinition.service(servletRequest, servletResponse);
+    verify(servletRequest, servletResponse);
+  }
 }
diff --git a/extensions/servlet/test/com/google/inject/servlet/ServletUtilsTest.java b/extensions/servlet/test/com/google/inject/servlet/ServletUtilsTest.java
new file mode 100644
index 0000000..bd617ab
--- /dev/null
+++ b/extensions/servlet/test/com/google/inject/servlet/ServletUtilsTest.java
@@ -0,0 +1,58 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+
+package com.google.inject.servlet;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+
+import junit.framework.TestCase;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * Unit test for the servlet utility class.
+ *
+ * @author ntang at google.com (Michael Tang)
+ */
+public class ServletUtilsTest extends TestCase {
+  public void testGetContextRelativePath() {
+    HttpServletRequest servletRequest = createMock(HttpServletRequest.class);
+    expect(servletRequest.getContextPath()).andReturn("/a_context_path");
+    expect(servletRequest.getRequestURI()).andReturn("/a_context_path/test.html");
+    replay(servletRequest);
+    String path = ServletUtils.getContextRelativePath(servletRequest);
+    assertEquals("/test.html", path);
+    verify(servletRequest);
+  }
+
+  public void testGetContextRelativePathWithWrongPath() {
+    HttpServletRequest servletRequest = createMock(HttpServletRequest.class);
+    expect(servletRequest.getContextPath()).andReturn("/a_context_path");
+    expect(servletRequest.getRequestURI()).andReturn("/test.html");
+    replay(servletRequest);
+    String path = ServletUtils.getContextRelativePath(servletRequest);
+    assertNull(path);
+    verify(servletRequest);
+  }
+
+  public void testGetContextRelativePathWithRootPath() {
+    HttpServletRequest servletRequest = createMock(HttpServletRequest.class);
+    expect(servletRequest.getContextPath()).andReturn("/a_context_path");
+    expect(servletRequest.getRequestURI()).andReturn("/a_context_path");
+    replay(servletRequest);
+    String path = ServletUtils.getContextRelativePath(servletRequest);
+    assertEquals("/", path);
+    verify(servletRequest);
+  }
+
+  public void testGetContextRelativePathWithEmptyPath() {
+    HttpServletRequest servletRequest = createMock(HttpServletRequest.class);
+    expect(servletRequest.getContextPath()).andReturn("");
+    expect(servletRequest.getRequestURI()).andReturn("");
+    replay(servletRequest);
+    String path = ServletUtils.getContextRelativePath(servletRequest);
+    assertNull(path);
+    verify(servletRequest);
+  }
+}
diff --git a/extensions/servlet/test/com/google/inject/servlet/TransferRequestIntegrationTest.java b/extensions/servlet/test/com/google/inject/servlet/TransferRequestIntegrationTest.java
new file mode 100644
index 0000000..41a2f61
--- /dev/null
+++ b/extensions/servlet/test/com/google/inject/servlet/TransferRequestIntegrationTest.java
@@ -0,0 +1,122 @@
+/**
+ * Copyright (C) 2012 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.servlet;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.inject.AbstractModule;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Key;
+import com.google.inject.OutOfScopeException;
+import com.google.inject.Provides;
+
+import junit.framework.TestCase;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+// TODO: Add test for HTTP transferring.
+/**
+ * Tests transferring of entire request scope.
+ */
+
+public class TransferRequestIntegrationTest extends TestCase {
+  private final Callable<Boolean> FALSE_CALLABLE = new Callable<Boolean>() {
+    @Override public Boolean call() {
+      return false;
+    }
+  };
+
+  public void testTransferHttp_outOfScope() {
+    try {
+      ServletScopes.transferRequest(FALSE_CALLABLE);
+      fail();
+    } catch (OutOfScopeException expected) {}
+  }
+
+  public void testTransferNonHttp_outOfScope() {
+    try {
+      ServletScopes.transferRequest(FALSE_CALLABLE);
+      fail();
+    } catch (OutOfScopeException expected) {}
+  }
+
+  public void testTransferNonHttpRequest() throws Exception {
+    final Injector injector = Guice.createInjector(new AbstractModule() {
+      @Override protected void configure() {
+        bindScope(RequestScoped.class, ServletScopes.REQUEST);
+      }
+
+      @Provides @RequestScoped Object provideObject() {
+        return new Object();
+      }
+    });
+
+    Callable<Callable<Boolean>> callable = new Callable<Callable<Boolean>>() {
+      @Override public Callable<Boolean> call() {
+        final Object original = injector.getInstance(Object.class);
+        return ServletScopes.transferRequest(new Callable<Boolean>() {
+          @Override public Boolean call() {
+            return original == injector.getInstance(Object.class);
+          }
+        });
+      }
+    };
+
+    ImmutableMap<Key<?>, Object> seedMap = ImmutableMap.of();
+    Callable<Boolean> transfer = ServletScopes.scopeRequest(callable, seedMap).call();
+
+    ExecutorService executor = Executors.newSingleThreadExecutor();
+    assertTrue(executor.submit(transfer).get());
+    executor.shutdownNow();
+  }
+
+  public void testTransferNonHttpRequest_concurrentUseFails() throws Exception {
+    Callable<Boolean> callable = new Callable<Boolean>() {
+      @Override public Boolean call() throws Exception {
+        ExecutorService executor = Executors.newSingleThreadExecutor();
+        try {
+          Future<Boolean> future = executor.submit(ServletScopes.transferRequest(FALSE_CALLABLE));
+          try {
+            return future.get();
+          } catch (ExecutionException e) {
+            return e.getCause() instanceof IllegalStateException;
+          }
+        } finally {
+          executor.shutdownNow();
+        }
+      }
+    };
+
+    ImmutableMap<Key<?>, Object> seedMap = ImmutableMap.of();
+    assertTrue(ServletScopes.scopeRequest(callable, seedMap).call());
+  }
+
+  public void testTransferNonHttpRequest_concurrentUseSameThreadOk() throws Exception {
+    Callable<Boolean> callable = new Callable<Boolean>() {
+      @Override public Boolean call() throws Exception {
+        return ServletScopes.transferRequest(FALSE_CALLABLE).call();
+      }
+    };
+
+    ImmutableMap<Key<?>, Object> seedMap = ImmutableMap.of();
+    assertFalse(ServletScopes.scopeRequest(callable, seedMap).call());
+  }
+}
diff --git a/extensions/spring/build.xml b/extensions/spring/build.xml
index 16c8299..3051929 100644
--- a/extensions/spring/build.xml
+++ b/extensions/spring/build.xml
@@ -10,11 +10,10 @@
     <pathelement path="../../build/classes"/>
   </path>
 
-  <target name="jar" depends="jar.withrenameddeps, manifest" description="Build jar.">
+  <target name="jar" depends="compile, manifest" description="Build jar.">
     <jar destfile="${build.dir}/${ant.project.name}-${version}.jar"
         manifest="${build.dir}/META-INF/MANIFEST.MF">
-      <zipfileset src="${build.dir}/${ant.project.name}-with-deps.jar"
-          excludes="com/google/inject/internal/**"/>
+      <fileset dir="${build.dir}/classes"/>
     </jar>
   </target>
 
diff --git a/extensions/spring/pom.xml b/extensions/spring/pom.xml
index c0bb5fb..6661f10 100644
--- a/extensions/spring/pom.xml
+++ b/extensions/spring/pom.xml
@@ -5,8 +5,8 @@
 
   <parent>
     <groupId>org.sonatype.sisu.inject</groupId>
-    <artifactId>guice-extensions</artifactId>
-    <version>3.1.1</version>
+    <artifactId>extensions-parent</artifactId>
+    <version>3.1.9</version>
   </parent>
 
   <artifactId>guice-spring</artifactId>
@@ -17,7 +17,7 @@
     <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-beans</artifactId>
-      <version>3.0.6.RELEASE</version>
+      <version>3.0.5.RELEASE</version>
       <scope>provided</scope>
     </dependency>
   </dependencies>
diff --git a/extensions/struts2/build.xml b/extensions/struts2/build.xml
index 820dfa2..6463274 100644
--- a/extensions/struts2/build.xml
+++ b/extensions/struts2/build.xml
@@ -11,11 +11,10 @@
     <fileset dir="../servlet/build" includes="*.jar"/>
   </path>
 
-  <target name="jar" depends="jar.withrenameddeps, manifest" description="Build jar.">
+  <target name="jar" depends="compile, manifest" description="Build jar.">
     <jar destfile="${build.dir}/${ant.project.name}-${version}.jar"
         manifest="${build.dir}/META-INF/MANIFEST.MF">
-      <zipfileset src="${build.dir}/${ant.project.name}-with-deps.jar"
-          excludes="com/google/inject/internal/**"/>
+      <fileset dir="${build.dir}/classes"/>
     </jar>
   </target>
 
diff --git a/extensions/struts2/pom.xml b/extensions/struts2/pom.xml
index 77ee2ab..e4ef84f 100644
--- a/extensions/struts2/pom.xml
+++ b/extensions/struts2/pom.xml
@@ -5,8 +5,8 @@
 
   <parent>
     <groupId>org.sonatype.sisu.inject</groupId>
-    <artifactId>guice-extensions</artifactId>
-    <version>3.1.1</version>
+    <artifactId>extensions-parent</artifactId>
+    <version>3.1.9</version>
   </parent>
 
   <artifactId>guice-struts2</artifactId>
@@ -28,7 +28,7 @@
     <dependency>
       <groupId>org.apache.struts</groupId>
       <artifactId>struts2-core</artifactId>
-      <version>2.2.3.1</version>
+      <version>2.2.1</version>
       <scope>provided</scope>
     </dependency>
   </dependencies>
diff --git a/extensions/throwingproviders/build.xml b/extensions/throwingproviders/build.xml
index 9d36431..27c20e0 100644
--- a/extensions/throwingproviders/build.xml
+++ b/extensions/throwingproviders/build.xml
@@ -10,11 +10,10 @@
     <pathelement path="../../build/classes"/>
   </path>
 
-  <target name="jar" depends="jar.withrenameddeps, manifest" description="Build jar.">
+  <target name="jar" depends="compile, manifest" description="Build jar.">
     <jar destfile="${build.dir}/${ant.project.name}-${version}.jar"
         manifest="${build.dir}/META-INF/MANIFEST.MF">
-      <zipfileset src="${build.dir}/${ant.project.name}-with-deps.jar"
-          excludes="com/google/inject/internal/**"/>
+      <fileset dir="${build.dir}/classes"/>
     </jar>
   </target>
 
diff --git a/extensions/throwingproviders/pom.xml b/extensions/throwingproviders/pom.xml
index 07284d9..737eaf1 100644
--- a/extensions/throwingproviders/pom.xml
+++ b/extensions/throwingproviders/pom.xml
@@ -5,8 +5,8 @@
 
   <parent>
     <groupId>org.sonatype.sisu.inject</groupId>
-    <artifactId>guice-extensions</artifactId>
-    <version>3.1.1</version>
+    <artifactId>extensions-parent</artifactId>
+    <version>3.1.9</version>
   </parent>
 
   <artifactId>guice-throwingproviders</artifactId>
diff --git a/extensions/throwingproviders/src/com/google/inject/throwingproviders/CheckedProvideUtils.java b/extensions/throwingproviders/src/com/google/inject/throwingproviders/CheckedProvideUtils.java
new file mode 100644
index 0000000..ca5ab77
--- /dev/null
+++ b/extensions/throwingproviders/src/com/google/inject/throwingproviders/CheckedProvideUtils.java
@@ -0,0 +1,106 @@
+/**
+ * Copyright (C) 2012 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.throwingproviders;
+
+import com.google.inject.Binder;
+import com.google.inject.TypeLiteral;
+import com.google.inject.internal.Annotations;
+import com.google.inject.internal.Errors;
+import com.google.inject.spi.Message;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Constructor;
+
+/**
+ * Utilities for the throwing provider module.
+ * 
+ * @author sameb at google.com (Sam Berlin)
+ */
+class CheckedProvideUtils {
+  
+  private CheckedProvideUtils() {}
+  
+  private static final String CONSTRUCTOR_RULES =
+      "Classes must have either one (and only one) constructor annotated with @ThrowingInject.";
+  
+  @SuppressWarnings("unchecked") // safe because it's a constructor of the typeLiteral
+  static <T> Constructor<? extends T> findThrowingConstructor(
+      TypeLiteral<? extends T> typeLiteral, Binder binder) {
+    
+    Class<?> rawType = typeLiteral.getRawType();
+    Errors errors = new Errors(rawType);
+    Constructor<?> cxtor = null;
+    for (Constructor<?> constructor : rawType.getDeclaredConstructors()) {
+      if (constructor.isAnnotationPresent(ThrowingInject.class)) {
+        if (cxtor != null) {
+          errors.addMessage("%s has more than one constructor annotated with @ThrowingInject. "
+              + CONSTRUCTOR_RULES, rawType);
+        }
+
+        cxtor = constructor;
+        Annotation misplacedBindingAnnotation = Annotations.findBindingAnnotation(
+            errors, cxtor, ((AnnotatedElement) cxtor).getAnnotations());
+        if (misplacedBindingAnnotation != null) {
+          errors.misplacedBindingAnnotation(cxtor, misplacedBindingAnnotation);
+        }
+      }
+    }
+    
+    if (cxtor == null) {
+      errors.addMessage(
+          "Could not find a suitable constructor in %s. " + CONSTRUCTOR_RULES, rawType);
+    }
+
+    for (Message msg : errors.getMessages()) {
+      binder.addError(msg);
+    }
+    return (Constructor<? extends T>) cxtor;
+  }
+  
+  /** Adds errors to the binder if the exceptions aren't valid. */
+  static void validateExceptions(Binder binder,
+      Iterable<TypeLiteral<?>> actualExceptionTypes,
+      Iterable<Class<? extends Throwable>> expectedExceptionTypes,
+      Class<? extends CheckedProvider> checkedProvider) {
+    // Validate the exceptions in the method match the exceptions
+    // in the CheckedProvider.
+    for (TypeLiteral<?> exType : actualExceptionTypes) {
+      Class<?> exActual = exType.getRawType();
+      // Ignore runtime exceptions & errors.
+      if (RuntimeException.class.isAssignableFrom(exActual)
+          || Error.class.isAssignableFrom(exActual)) {
+        continue;
+      }
+
+      boolean notAssignable = true;
+      for (Class<? extends Throwable> exExpected : expectedExceptionTypes) {
+        if (exExpected.isAssignableFrom(exActual)) {
+          notAssignable = false;
+          break;
+        }
+      }
+      if (notAssignable) {
+        binder.addError(
+            "%s is not compatible with the exceptions (%s) declared in " 
+            + "the CheckedProvider interface (%s)",
+            exActual, expectedExceptionTypes, checkedProvider);
+      }
+    }
+  }
+
+}
diff --git a/extensions/throwingproviders/src/com/google/inject/throwingproviders/CheckedProviderMethod.java b/extensions/throwingproviders/src/com/google/inject/throwingproviders/CheckedProviderMethod.java
index 0de744a..8d975ee 100644
--- a/extensions/throwingproviders/src/com/google/inject/throwingproviders/CheckedProviderMethod.java
+++ b/extensions/throwingproviders/src/com/google/inject/throwingproviders/CheckedProviderMethod.java
@@ -76,9 +76,9 @@ class CheckedProviderMethod<T> implements CheckedProvider<T>, HasDependencies {
   void configure(Binder binder) {
     binder = binder.withSource(method);
 
-    SecondaryBinder<?> sbinder = 
+    SecondaryBinder<?, ?> sbinder = 
       ThrowingProviderBinder.create(binder)
-        .bind(checkedProvider, key.getTypeLiteral().getType());
+        .bind(checkedProvider, key.getTypeLiteral());
     if(key.getAnnotation() != null) {
       sbinder = sbinder.annotatedWith(key.getAnnotation());
     } else if(key.getAnnotationType() != null) {
@@ -94,29 +94,9 @@ class CheckedProviderMethod<T> implements CheckedProvider<T>, HasDependencies {
       // misplaced @Exposed, calling this will add an error to the binder's error queue
       ((PrivateBinder) binder).expose(sbinder.getKey());
     }
-
-    // Validate the exceptions in the method match the exceptions
-    // in the CheckedProvider.
-    for(TypeLiteral<?> exType : exceptionTypes) {
-      Class<?> exActual = exType.getRawType();
-      // Ignore runtime exceptions & errors.
-      if(RuntimeException.class.isAssignableFrom(exActual) || Error.class.isAssignableFrom(exActual)) {
-        continue;
-      }
-      
-      boolean notAssignable = true;
-      for(Class<? extends Throwable> exExpected : sbinder.getExceptionTypes()) {
-        if (exExpected.isAssignableFrom(exActual)) {
-          notAssignable = false;
-          break;
-        }
-      }
-      if(notAssignable) {
-        binder.addError(
-            "%s is not compatible with the exceptions (%s) declared in the CheckedProvider interface (%s)",
-            exActual, sbinder.getExceptionTypes(), checkedProvider);
-      }
-    }
+    
+    CheckedProvideUtils.validateExceptions(
+        binder, exceptionTypes, sbinder.getExceptionTypes(), checkedProvider);
   }
 
   public T get() throws Exception {
diff --git a/extensions/throwingproviders/src/com/google/inject/throwingproviders/CheckedProviderMethodsModule.java b/extensions/throwingproviders/src/com/google/inject/throwingproviders/CheckedProviderMethodsModule.java
index 523d020..6c81dab 100644
--- a/extensions/throwingproviders/src/com/google/inject/throwingproviders/CheckedProviderMethodsModule.java
+++ b/extensions/throwingproviders/src/com/google/inject/throwingproviders/CheckedProviderMethodsModule.java
@@ -75,8 +75,7 @@ final class CheckedProviderMethodsModule implements Module {
     List<CheckedProviderMethod<?>> result = Lists.newArrayList();
     for (Class<?> c = delegate.getClass(); c != Object.class; c = c.getSuperclass()) {
       for (Method method : c.getDeclaredMethods()) {
-        CheckedProvides checkedProvides =
-          (CheckedProvides)method.getAnnotation(CheckedProvides.class);
+        CheckedProvides checkedProvides = method.getAnnotation(CheckedProvides.class);
         if(checkedProvides != null) {
           result.add(createProviderMethod(binder, method, checkedProvides.value()));
         }
diff --git a/core/test/com/google/inject/spi/ElementApplyToTest.java b/extensions/throwingproviders/src/com/google/inject/throwingproviders/CheckedProviderWithDependencies.java
similarity index 53%
copy from core/test/com/google/inject/spi/ElementApplyToTest.java
copy to extensions/throwingproviders/src/com/google/inject/throwingproviders/CheckedProviderWithDependencies.java
index baa8ca4..79df84d 100644
--- a/core/test/com/google/inject/spi/ElementApplyToTest.java
+++ b/extensions/throwingproviders/src/com/google/inject/throwingproviders/CheckedProviderWithDependencies.java
@@ -1,5 +1,5 @@
 /**
- * Copyright (C) 2008 Google Inc.
+ * Copyright (C) 2012 Google Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,17 +14,17 @@
  * limitations under the License.
  */
 
-package com.google.inject.spi;
+package com.google.inject.throwingproviders;
 
-import com.google.inject.Module;
+import com.google.inject.spi.HasDependencies;
+import com.google.inject.throwingproviders.ThrowingProviderBinder.SecondaryBinder;
 
 /**
- * @author jessewilson at google.com (Jesse Wilson)
+ * A checked provider with dependencies, so {@link HasDependencies} can be implemented
+ * when using the {@link SecondaryBinder#using} methods.
+ * 
+ * @author sameb at google.com (Sam Berlin)
  */
-public class ElementApplyToTest extends ElementsTest {
+interface CheckedProviderWithDependencies<T> extends CheckedProvider<T>, HasDependencies {
 
-  protected void checkModule(Module module, ElementVisitor<?>... visitors) {
-    // convert from module to elements and back
-    super.checkModule(Elements.getModule(Elements.getElements(module)), visitors);
-  }
-}
\ No newline at end of file
+}
diff --git a/core/src/com/google/inject/Provides.java b/extensions/throwingproviders/src/com/google/inject/throwingproviders/ThrowingInject.java
similarity index 54%
copy from core/src/com/google/inject/Provides.java
copy to extensions/throwingproviders/src/com/google/inject/throwingproviders/ThrowingInject.java
index ca7aa65..6d18882 100644
--- a/core/src/com/google/inject/Provides.java
+++ b/extensions/throwingproviders/src/com/google/inject/throwingproviders/ThrowingInject.java
@@ -1,5 +1,5 @@
 /**
- * Copyright (C) 2007 Google Inc.
+ * Copyright (C) 2012 Google Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,21 +14,29 @@
  * limitations under the License.
  */
 
-package com.google.inject;
+package com.google.inject.throwingproviders;
 
-import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
+import com.google.inject.Inject;
+
 import java.lang.annotation.Documented;
 import java.lang.annotation.Retention;
 import java.lang.annotation.Target;
 
 /**
- * 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.
+ * A version of {@literal @}{@link Inject} designed for ThrowingProviders.  Use by:
+ * <pre><code>ThrowingProviderBinder.create(binder())
+ *    .bind(RemoteProvider.class, Customer.class)
+ *    .providing(CustomerImpl.class);
+ * </code></pre>
+ * where CustomerImpl has a constructor annotated with ThrowingInject.
  *
- * @author crazybob at google.com (Bob Lee)
- * @since 2.0
+ * @author sameb at google.com (Sam Berlin)
  */
- at Documented @Target(METHOD) @Retention(RUNTIME)
-public @interface Provides {}
+ at Target({ CONSTRUCTOR })
+ at Retention(RUNTIME)
+ at Documented
+public @interface ThrowingInject {
+}
diff --git a/extensions/throwingproviders/src/com/google/inject/throwingproviders/ThrowingProviderBinder.java b/extensions/throwingproviders/src/com/google/inject/throwingproviders/ThrowingProviderBinder.java
index 41e68b0..01cbee5 100644
--- a/extensions/throwingproviders/src/com/google/inject/throwingproviders/ThrowingProviderBinder.java
+++ b/extensions/throwingproviders/src/com/google/inject/throwingproviders/ThrowingProviderBinder.java
@@ -22,18 +22,24 @@ import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
 import com.google.inject.Binder;
+import com.google.inject.ConfigurationException;
 import com.google.inject.Key;
 import com.google.inject.Module;
 import com.google.inject.Provider;
+import com.google.inject.ProvisionException;
+import com.google.inject.Scopes;
 import com.google.inject.TypeLiteral;
 import com.google.inject.binder.ScopedBindingBuilder;
 import com.google.inject.internal.UniqueAnnotations;
 import com.google.inject.spi.Dependency;
+import com.google.inject.spi.InjectionPoint;
+import com.google.inject.spi.Message;
 import com.google.inject.spi.ProviderWithDependencies;
 import com.google.inject.util.Types;
 
 import java.io.Serializable;
 import java.lang.annotation.Annotation;
+import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.Method;
 import java.lang.reflect.ParameterizedType;
@@ -66,9 +72,17 @@ import java.util.Set;
  *   }
  * }
  * </code></pre>
+ * You also can declare that a CheckedProvider construct
+ * a particular class whose constructor throws an exception:
+ * <pre><code>ThrowingProviderBinder.create(binder())
+ *    .bind(RemoteProvider.class, Customer.class)
+ *    .providing(CustomerImpl.class)
+ *    .in(RequestScope.class);
+ * </code></pre>
  * 
  * @author jmourits at google.com (Jerome Mourits)
  * @author jessewilson at google.com (Jesse Wilson)
+ * @author sameb at google.com (Sam Berlin)
  */
 public class ThrowingProviderBinder {
 
@@ -92,13 +106,27 @@ public class ThrowingProviderBinder {
   public static Module forModule(Module module) {
     return CheckedProviderMethodsModule.forModule(module);
   }
+  
+  /**
+   * @deprecated Use {@link #bind(Class, Class)} or {@link #bind(Class, TypeLiteral)} instead. 
+   */
+  @Deprecated
+  public <P extends CheckedProvider> SecondaryBinder<P, ?> 
+      bind(Class<P> interfaceType, Type clazz) {
+    return new SecondaryBinder<P, Object>(interfaceType, clazz);
+  }
 
-  public <P extends CheckedProvider> SecondaryBinder<P> 
-      bind(final Class<P> interfaceType, final Type valueType) {
-    return new SecondaryBinder<P>(interfaceType, valueType);
+  public <P extends CheckedProvider, T> SecondaryBinder<P, T> 
+      bind(Class<P> interfaceType, Class<T> clazz) {
+    return new SecondaryBinder<P, T>(interfaceType, clazz);
+  }
+  
+  public <P extends CheckedProvider, T> SecondaryBinder<P, T> 
+      bind(Class<P> interfaceType, TypeLiteral<T> typeLiteral) {
+    return new SecondaryBinder<P, T>(interfaceType, typeLiteral.getType());
   }
 
-  public class SecondaryBinder<P extends CheckedProvider> {
+  public class SecondaryBinder<P extends CheckedProvider, T> {
     private final Class<P> interfaceType;
     private final Type valueType;
     private final List<Class<? extends Throwable>> exceptionTypes;
@@ -128,17 +156,17 @@ public class ThrowingProviderBinder {
       return interfaceKey;
     }
 
-    public SecondaryBinder<P> annotatedWith(Class<? extends Annotation> annotationType) {
+    public SecondaryBinder<P, T> annotatedWith(Class<? extends Annotation> annotationType) {
       if (!(this.annotationType == null && this.annotation == null)) {
-        throw new IllegalStateException();
+        throw new IllegalStateException("Cannot set annotation twice");
       }
       this.annotationType = annotationType;
       return this;
     }
 
-    public SecondaryBinder<P> annotatedWith(Annotation annotation) {
+    public SecondaryBinder<P, T> annotatedWith(Annotation annotation) {
       if (!(this.annotationType == null && this.annotation == null)) {
-        throw new IllegalStateException();
+        throw new IllegalStateException("Cannot set annotation twice");
       }
       this.annotation = annotation;
       return this;
@@ -149,16 +177,76 @@ public class ThrowingProviderBinder {
       binder.bind(targetKey).toInstance(target);
       return to(targetKey);
     }
-
+    
     public ScopedBindingBuilder to(Class<? extends P> targetType) {
       return to(Key.get(targetType));
     }
     
+    public ScopedBindingBuilder providing(Class<? extends T> cxtorClass) {
+      return providing(TypeLiteral.get(cxtorClass));
+    }
+    
+    @SuppressWarnings("unchecked") // safe because this is the cxtor of the literal
+    public ScopedBindingBuilder providing(TypeLiteral<? extends T> cxtorLiteral) {     
+      // Find a constructor that has @ThrowingInject.
+      Constructor<? extends T> cxtor =
+          CheckedProvideUtils.findThrowingConstructor(cxtorLiteral, binder);
+
+      final Provider<T> typeProvider;
+      final Key<? extends T> typeKey;
+      // If we found an injection point, then bind the cxtor to a unique key
+      if (cxtor != null) {
+        // Validate the exceptions are consistent with the CheckedProvider interface.
+        CheckedProvideUtils.validateExceptions(
+            binder, cxtorLiteral.getExceptionTypes(cxtor), exceptionTypes, interfaceType);
+        
+        typeKey = Key.get(cxtorLiteral, UniqueAnnotations.create());
+        binder.bind(typeKey).toConstructor((Constructor) cxtor).in(Scopes.NO_SCOPE);
+        typeProvider = binder.getProvider((Key<T>) typeKey);
+      } else {
+        // never used, but need it assigned.
+        typeProvider = null;
+        typeKey = null;
+      }
+        
+      // Create a CheckedProvider that calls our cxtor
+      CheckedProvider<T> checkedProvider = new CheckedProviderWithDependencies<T>() {
+        @Override
+        public T get() throws Exception {
+          try {
+            return typeProvider.get();
+          } catch (ProvisionException pe) {
+            // Rethrow the provision cause as the actual exception
+            if (pe.getCause() instanceof Exception) {
+              throw (Exception) pe.getCause();
+            } else if (pe.getCause() instanceof Error) {
+              throw (Error) pe.getCause();
+            } else {
+              // If this failed because of multiple reasons (ie, more than
+              // one dependency failed due to scoping errors), then
+              // the ProvisionException won't have a cause, so we need
+              // to rethrow it as-is.
+              throw pe;
+            }
+          }
+        }
+        
+        @Override
+        public Set<Dependency<?>> getDependencies() {
+          return ImmutableSet.<Dependency<?>>of(Dependency.get(typeKey));
+        }
+      };
+      
+      Key<CheckedProvider> targetKey = Key.get(CheckedProvider.class, UniqueAnnotations.create());
+      binder.bind(targetKey).toInstance(checkedProvider);
+      return toInternal(targetKey);
+    }
+    
     ScopedBindingBuilder toProviderMethod(CheckedProviderMethod<?> target) {
       Key<CheckedProviderMethod> targetKey =
-        Key.get(CheckedProviderMethod.class, UniqueAnnotations.create());
+          Key.get(CheckedProviderMethod.class, UniqueAnnotations.create());
       binder.bind(targetKey).toInstance(target);
-      
+
       return toInternal(targetKey);
     }
 
@@ -181,6 +269,10 @@ public class ThrowingProviderBinder {
               new InvocationHandler() {
                 public Object invoke(Object proxy, Method method, Object[] args)
                     throws Throwable {
+                  // Allow methods like .equals(..), .hashcode(..), .toString(..) to work.
+                  if (method.getDeclaringClass() == Object.class) {
+                    return method.invoke(this, args);
+                  }
                   return resultProvider.get().getOrThrow();
                 }
               }));
diff --git a/extensions/throwingproviders/test/com/google/inject/throwingproviders/CheckedProviderTest.java b/extensions/throwingproviders/test/com/google/inject/throwingproviders/CheckedProviderTest.java
index 3d58791..ca13c63 100644
--- a/extensions/throwingproviders/test/com/google/inject/throwingproviders/CheckedProviderTest.java
+++ b/extensions/throwingproviders/test/com/google/inject/throwingproviders/CheckedProviderTest.java
@@ -20,6 +20,7 @@ import com.google.common.base.Function;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
 import com.google.inject.AbstractModule;
 import com.google.inject.Asserts;
 import com.google.inject.CreationException;
@@ -27,6 +28,11 @@ import com.google.inject.Guice;
 import com.google.inject.Inject;
 import com.google.inject.Injector;
 import com.google.inject.Key;
+import com.google.inject.OutOfScopeException;
+import com.google.inject.Provider;
+import com.google.inject.ProvisionException;
+import com.google.inject.Scope;
+import com.google.inject.ScopeAnnotation;
 import com.google.inject.TypeLiteral;
 import com.google.inject.internal.util.Classes;
 import com.google.inject.name.Named;
@@ -39,9 +45,14 @@ import com.google.inject.throwingproviders.ThrowingProviderBinder.Result;
 import junit.framework.TestCase;
 
 import java.io.IOException;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
 import java.net.BindException;
 import java.rmi.AccessException;
 import java.rmi.RemoteException;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Set;
@@ -50,34 +61,68 @@ import java.util.TooManyListenersException;
 /**
  * @author jmourits at google.com (Jerome Mourits)
  * @author jessewilson at google.com (Jesse Wilson)
+ * @author sameb at google.com (Sam Berlin)
  */
 public class CheckedProviderTest extends TestCase {
+  
+  private static final Function<Dependency<?>, Key<?>> DEPENDENCY_TO_KEY =
+      new Function<Dependency<?>, Key<?>>() {
+        public Key<?> apply(Dependency<?> from) {
+          return from.getKey();
+        }
+      };
 
-  private final TypeLiteral<RemoteProvider<String>> remoteProviderOfString
-      = new TypeLiteral<RemoteProvider<String>>() { };
-  private final MockRemoteProvider<String> mockRemoteProvider = new MockRemoteProvider<String>();
+  private final TypeLiteral<RemoteProvider<Foo>> remoteProviderOfFoo
+      = new TypeLiteral<RemoteProvider<Foo>>() { };
+  private final MockRemoteProvider<Foo> mockRemoteProvider = new MockRemoteProvider<Foo>();
   private final TestScope testScope = new TestScope();
-  private Injector bindInjector = Guice.createInjector(new AbstractModule() {
-    protected void configure() {
-      ThrowingProviderBinder.create(binder())
-          .bind(RemoteProvider.class, String.class)
-          .to(mockRemoteProvider)
-          .in(testScope);
-    }
-  });
-  private Injector providesInjector = Guice.createInjector(new AbstractModule() {
-    protected void configure() {
-     install(ThrowingProviderBinder.forModule(this));
-     bindScope(TestScope.Scoped.class, testScope);
-    }
+  
+  private Injector bindInjector;  
+  private Injector providesInjector;
+  private Injector cxtorInjector;
+  
+  @Override
+  protected void setUp() throws Exception {
+    MockFoo.nextToThrow = null;
+    MockFoo.nextToReturn = null;
+    AnotherMockFoo.nextToThrow = null;
+    AnotherMockFoo.nextToReturn = null;
     
-    @SuppressWarnings("unused")
-    @CheckedProvides(RemoteProvider.class)
-    @TestScope.Scoped
-    String throwOrGet() throws RemoteException, BindException {
-      return mockRemoteProvider.get();
-    }
-  });
+    bindInjector = Guice.createInjector(new AbstractModule() {
+      @Override
+      protected void configure() {
+        ThrowingProviderBinder.create(binder())
+            .bind(RemoteProvider.class, Foo.class)
+            .to(mockRemoteProvider)
+            .in(testScope);
+      }
+    });  
+    
+    providesInjector = Guice.createInjector(new AbstractModule() {
+      @Override
+      protected void configure() {
+       install(ThrowingProviderBinder.forModule(this));
+       bindScope(TestScope.Scoped.class, testScope);
+      }
+      
+      @SuppressWarnings("unused")
+      @CheckedProvides(RemoteProvider.class)
+      @TestScope.Scoped
+      Foo throwOrGet() throws RemoteException, BindException {
+        return mockRemoteProvider.get();
+      }
+    });
+    
+    cxtorInjector = Guice.createInjector(new AbstractModule() {
+      @Override
+      protected void configure() {
+        ThrowingProviderBinder.create(binder())
+          .bind(RemoteProvider.class, Foo.class)
+          .providing(MockFoo.class)
+          .in(testScope);
+      }
+    });
+  }
 
   public void testExceptionsThrown_Bind() throws Exception {
     tExceptionsThrown(bindInjector);
@@ -87,11 +132,16 @@ public class CheckedProviderTest extends TestCase {
     tExceptionsThrown(providesInjector);
   }
   
+  public void testExceptionsThrown_Cxtor() throws Exception {
+    tExceptionsThrown(cxtorInjector);
+  }
+  
   private void tExceptionsThrown(Injector injector) throws Exception {
-    RemoteProvider<String> remoteProvider = 
-      injector.getInstance(Key.get(remoteProviderOfString));
+    RemoteProvider<Foo> remoteProvider = 
+      injector.getInstance(Key.get(remoteProviderOfFoo));
 
     mockRemoteProvider.throwOnNextGet(new BindException("kaboom!"));
+    MockFoo.nextToThrow = new BindException("kaboom!");
     try {
       remoteProvider.get();
       fail();
@@ -109,17 +159,28 @@ public class CheckedProviderTest extends TestCase {
   }
   
   private void tValuesScoped(Injector injector) throws Exception {
-    RemoteProvider<String> remoteProvider = 
-      injector.getInstance(Key.get(remoteProviderOfString));
+    RemoteProvider<Foo> remoteProvider = 
+      injector.getInstance(Key.get(remoteProviderOfFoo));
 
-    mockRemoteProvider.setNextToReturn("A");
-    assertEquals("A", remoteProvider.get());
+    mockRemoteProvider.setNextToReturn(new SimpleFoo("A"));
+    assertEquals("A", remoteProvider.get().s());
 
-    mockRemoteProvider.setNextToReturn("B");
-    assertEquals("A", remoteProvider.get());
+    mockRemoteProvider.setNextToReturn(new SimpleFoo("B"));
+    assertEquals("A", remoteProvider.get().s());
 
     testScope.beginNewScope();
-    assertEquals("B", remoteProvider.get());
+    assertEquals("B", remoteProvider.get().s());
+  }
+  
+  public void testValuesScoped_Cxtor() throws Exception {
+    RemoteProvider<Foo> remoteProvider = 
+        cxtorInjector.getInstance(Key.get(remoteProviderOfFoo));
+
+    Foo retrieved = remoteProvider.get();
+    assertSame(retrieved, remoteProvider.get()); // same, not in new scope.
+    
+    testScope.beginNewScope();
+    assertNotSame(retrieved, remoteProvider.get()); // different, new scope.
   }
 
   public void testExceptionsScoped_Bind() throws Exception {
@@ -130,11 +191,16 @@ public class CheckedProviderTest extends TestCase {
     tExceptionsScoped(providesInjector);
   }
   
+  public void testExceptionScopes_Cxtor() throws Exception {
+    tExceptionsScoped(cxtorInjector);
+  }
+  
   private void tExceptionsScoped(Injector injector) throws Exception {
-    RemoteProvider<String> remoteProvider = 
-        injector.getInstance(Key.get(remoteProviderOfString));
+    RemoteProvider<Foo> remoteProvider = 
+        injector.getInstance(Key.get(remoteProviderOfFoo));
 
     mockRemoteProvider.throwOnNextGet(new RemoteException("A"));
+    MockFoo.nextToThrow = new RemoteException("A");
     try {
       remoteProvider.get();
       fail();
@@ -143,6 +209,7 @@ public class CheckedProviderTest extends TestCase {
     }
     
     mockRemoteProvider.throwOnNextGet(new RemoteException("B"));
+    MockFoo.nextToThrow = new RemoteException("B");
     try {
       remoteProvider.get();
       fail();
@@ -152,17 +219,18 @@ public class CheckedProviderTest extends TestCase {
   }
   
   public void testAnnotations_Bind() throws Exception {
-    final MockRemoteProvider<String> mockRemoteProviderA = new MockRemoteProvider<String>();
-    final MockRemoteProvider<String> mockRemoteProviderB = new MockRemoteProvider<String>();
+    final MockRemoteProvider<Foo> mockRemoteProviderA = new MockRemoteProvider<Foo>();
+    final MockRemoteProvider<Foo> mockRemoteProviderB = new MockRemoteProvider<Foo>();
     bindInjector = Guice.createInjector(new AbstractModule() {
+      @Override
       protected void configure() {
         ThrowingProviderBinder.create(binder())
-            .bind(RemoteProvider.class, String.class)
+            .bind(RemoteProvider.class, Foo.class)
             .annotatedWith(Names.named("a"))
             .to(mockRemoteProviderA);
 
         ThrowingProviderBinder.create(binder())
-            .bind(RemoteProvider.class, String.class)
+            .bind(RemoteProvider.class, Foo.class)
             .to(mockRemoteProviderB);
       }
     });
@@ -170,9 +238,10 @@ public class CheckedProviderTest extends TestCase {
   }
   
   public void testAnnotations_Provides() throws Exception {
-    final MockRemoteProvider<String> mockRemoteProviderA = new MockRemoteProvider<String>();
-    final MockRemoteProvider<String> mockRemoteProviderB = new MockRemoteProvider<String>();
+    final MockRemoteProvider<Foo> mockRemoteProviderA = new MockRemoteProvider<Foo>();
+    final MockRemoteProvider<Foo> mockRemoteProviderB = new MockRemoteProvider<Foo>();
     providesInjector = Guice.createInjector(new AbstractModule() {
+      @Override
       protected void configure() {
         install(ThrowingProviderBinder.forModule(this));
        }
@@ -180,28 +249,51 @@ public class CheckedProviderTest extends TestCase {
        @SuppressWarnings("unused")
        @CheckedProvides(RemoteProvider.class)
        @Named("a")
-       String throwOrGet() throws RemoteException, BindException {
+       Foo throwOrGet() throws RemoteException, BindException {
          return mockRemoteProviderA.get();
        }
        
        @SuppressWarnings("unused")
        @CheckedProvides(RemoteProvider.class)
-       String throwOrGet2() throws RemoteException, BindException {
+       Foo throwOrGet2() throws RemoteException, BindException {
          return mockRemoteProviderB.get();
        }
     });
     tAnnotations(providesInjector, mockRemoteProviderA, mockRemoteProviderB);
   }
   
-  private void tAnnotations(Injector injector, MockRemoteProvider<String> mockA,
-      MockRemoteProvider<String> mockB) throws Exception {
-    mockA.setNextToReturn("A");
-    mockB.setNextToReturn("B");
+  private void tAnnotations(Injector injector, MockRemoteProvider<Foo> mockA,
+      MockRemoteProvider<Foo> mockB) throws Exception {
+    mockA.setNextToReturn(new SimpleFoo("A"));
+    mockB.setNextToReturn(new SimpleFoo("B"));
+    assertEquals("A", 
+        injector.getInstance(Key.get(remoteProviderOfFoo, Names.named("a"))).get().s());
+
+    assertEquals("B", 
+        injector.getInstance(Key.get(remoteProviderOfFoo)).get().s());
+  }
+  
+  public void testAnnotations_Cxtor() throws Exception {
+    cxtorInjector = Guice.createInjector(new AbstractModule() {
+      @Override
+      protected void configure() {
+        ThrowingProviderBinder.create(binder())
+            .bind(RemoteProvider.class, Foo.class)
+            .annotatedWith(Names.named("a"))
+            .providing(MockFoo.class);
+
+        ThrowingProviderBinder.create(binder())
+            .bind(RemoteProvider.class, Foo.class)
+            .providing(AnotherMockFoo.class);
+      }
+    });
+    MockFoo.nextToReturn = "A";
+    AnotherMockFoo.nextToReturn = "B";
     assertEquals("A", 
-        injector.getInstance(Key.get(remoteProviderOfString, Names.named("a"))).get());
+        cxtorInjector.getInstance(Key.get(remoteProviderOfFoo, Names.named("a"))).get().s());
 
     assertEquals("B", 
-        injector.getInstance(Key.get(remoteProviderOfString)).get());
+        cxtorInjector.getInstance(Key.get(remoteProviderOfFoo)).get().s());
   }
   
   public void testUndeclaredExceptions_Bind() throws Exception {
@@ -211,11 +303,16 @@ public class CheckedProviderTest extends TestCase {
   public void testUndeclaredExceptions_Provides() throws Exception {
     tUndeclaredExceptions(providesInjector);
   }
+  
+  public void testUndeclaredExceptions_Cxtor() throws Exception {
+    tUndeclaredExceptions(cxtorInjector);
+  }
 
   private void tUndeclaredExceptions(Injector injector) throws Exception { 
-    RemoteProvider<String> remoteProvider = 
-        injector.getInstance(Key.get(remoteProviderOfString));
+    RemoteProvider<Foo> remoteProvider = 
+        injector.getInstance(Key.get(remoteProviderOfFoo));
     mockRemoteProvider.throwOnNextGet(new IndexOutOfBoundsException("A"));
+    MockFoo.nextToThrow = new IndexOutOfBoundsException("A");
     try {
       remoteProvider.get();
       fail();
@@ -225,6 +322,7 @@ public class CheckedProviderTest extends TestCase {
 
     // undeclared exceptions shouldn't be scoped
     mockRemoteProvider.throwOnNextGet(new IndexOutOfBoundsException("B"));
+    MockFoo.nextToThrow = new IndexOutOfBoundsException("B");
     try {
       remoteProvider.get();
       fail();
@@ -235,28 +333,30 @@ public class CheckedProviderTest extends TestCase {
 
   public void testThrowingProviderSubclassing() throws Exception {
     final SubMockRemoteProvider aProvider = new SubMockRemoteProvider();
-    aProvider.setNextToReturn("A");
+    aProvider.setNextToReturn(new SimpleFoo("A"));
 
     bindInjector = Guice.createInjector(new AbstractModule() {
+      @Override
       protected void configure() {
         ThrowingProviderBinder.create(binder())
-            .bind(RemoteProvider.class, String.class)
+            .bind(RemoteProvider.class, Foo.class)
             .to(aProvider);
       }
     });
 
     assertEquals("A",
-        bindInjector.getInstance(Key.get(remoteProviderOfString)).get());
+        bindInjector.getInstance(Key.get(remoteProviderOfFoo)).get().s());
   }
 
-  static class SubMockRemoteProvider extends MockRemoteProvider<String> { }
+  static class SubMockRemoteProvider extends MockRemoteProvider<Foo> { }
 
   public void testBindingToNonInterfaceType_Bind() throws Exception {
     try {
       Guice.createInjector(new AbstractModule() {
+        @Override
         protected void configure() {
           ThrowingProviderBinder.create(binder())
-              .bind(MockRemoteProvider.class, String.class)
+              .bind(MockRemoteProvider.class, Foo.class)
               .to(mockRemoteProvider);
         }
       });
@@ -270,13 +370,14 @@ public class CheckedProviderTest extends TestCase {
   public void testBindingToNonInterfaceType_Provides() throws Exception {
     try {
       Guice.createInjector(new AbstractModule() {
+        @Override
         protected void configure() {
           install(ThrowingProviderBinder.forModule(this));
         }
           
         @SuppressWarnings("unused")
         @CheckedProvides(MockRemoteProvider.class)
-        String foo() {
+        Foo foo() {
           return null;
         }
       });
@@ -290,9 +391,10 @@ public class CheckedProviderTest extends TestCase {
   public void testBindingToSubSubInterface_Bind() throws Exception {
     try {
       bindInjector = Guice.createInjector(new AbstractModule() {
+        @Override
         protected void configure() {
           ThrowingProviderBinder.create(binder())
-              .bind(SubRemoteProvider.class, String.class);
+              .bind(SubRemoteProvider.class, Foo.class);
         }
       });
       fail();
@@ -305,13 +407,14 @@ public class CheckedProviderTest extends TestCase {
   public void testBindingToSubSubInterface_Provides() throws Exception {
     try {
       Guice.createInjector(new AbstractModule() {
+        @Override
         protected void configure() {
           install(ThrowingProviderBinder.forModule(this));
         }
           
         @SuppressWarnings("unused")
         @CheckedProvides(SubRemoteProvider.class)
-        String foo() {
+        Foo foo() {
           return null;
         }
       });
@@ -327,9 +430,10 @@ public class CheckedProviderTest extends TestCase {
   public void testBindingToInterfaceWithExtraMethod_Bind() throws Exception {
     try {
       bindInjector = Guice.createInjector(new AbstractModule() {
+        @Override
         protected void configure() {
           ThrowingProviderBinder.create(binder())
-              .bind(RemoteProviderWithExtraMethod.class, String.class);
+              .bind(RemoteProviderWithExtraMethod.class, Foo.class);
         }
       });
       fail();
@@ -343,13 +447,14 @@ public class CheckedProviderTest extends TestCase {
   public void testBindingToInterfaceWithExtraMethod_Provides() throws Exception {
     try {
       Guice.createInjector(new AbstractModule() {
+        @Override
         protected void configure() {
           install(ThrowingProviderBinder.forModule(this));
         }
           
         @SuppressWarnings("unused")
         @CheckedProvides(RemoteProviderWithExtraMethod.class)
-        String foo() {
+        Foo foo() {
           return null;
         }
       });
@@ -363,19 +468,20 @@ public class CheckedProviderTest extends TestCase {
   
   public void testDependencies_Bind() {
     bindInjector = Guice.createInjector(new AbstractModule() {
+      @Override
       protected void configure() {
         bind(String.class).toInstance("Foo");
         bind(Integer.class).toInstance(5);
         bind(Double.class).toInstance(5d);
         bind(Long.class).toInstance(5L);
         ThrowingProviderBinder.create(binder())
-            .bind(RemoteProvider.class, String.class)
+            .bind(RemoteProvider.class, Foo.class)
             .to(DependentRemoteProvider.class);
       }
     });
     
     HasDependencies hasDependencies =
-        (HasDependencies)bindInjector.getBinding(Key.get(remoteProviderOfString));
+        (HasDependencies)bindInjector.getBinding(Key.get(remoteProviderOfFoo));
     hasDependencies = 
         (HasDependencies)bindInjector.getBinding(
             Iterables.getOnlyElement(hasDependencies.getDependencies()).getKey());
@@ -385,18 +491,14 @@ public class CheckedProviderTest extends TestCase {
     // And make sure DependentRemoteProvider has the proper dependencies.
     hasDependencies = (HasDependencies)bindInjector.getBinding(DependentRemoteProvider.class);
     Set<Key<?>> dependencyKeys = ImmutableSet.copyOf(
-        Iterables.transform(hasDependencies.getDependencies(),
-          new Function<Dependency<?>, Key<?>>() {
-            public Key<?> apply(Dependency<?> from) {
-              return from.getKey();
-            }
-          }));
+        Iterables.transform(hasDependencies.getDependencies(), DEPENDENCY_TO_KEY));
     assertEquals(ImmutableSet.<Key<?>>of(Key.get(String.class), Key.get(Integer.class),
         Key.get(Long.class), Key.get(Double.class)), dependencyKeys);
   }
   
   public void testDependencies_Provides() {
     providesInjector = Guice.createInjector(new AbstractModule() {
+      @Override
       protected void configure() {
         bind(String.class).toInstance("Foo");
         bind(Integer.class).toInstance(5);
@@ -407,27 +509,60 @@ public class CheckedProviderTest extends TestCase {
       
       @SuppressWarnings("unused")
       @CheckedProvides(RemoteProvider.class)
-      String foo(String s, Integer i, Double d, Long l) {
+      Foo foo(String s, Integer i, Double d, Long l) {
         return null;
       }
     });
     
     HasDependencies hasDependencies =
-        (HasDependencies)providesInjector.getBinding(Key.get(remoteProviderOfString));
+        (HasDependencies) providesInjector.getBinding(Key.get(remoteProviderOfFoo));
     // RemoteProvider<String> is dependent on the provider method..
-    hasDependencies = 
-        (HasDependencies)providesInjector.getBinding(
-            Iterables.getOnlyElement(hasDependencies.getDependencies()).getKey());
+    hasDependencies = (HasDependencies) providesInjector.getBinding(
+        Iterables.getOnlyElement(hasDependencies.getDependencies()).getKey());
     // And the provider method has our real dependencies..
     hasDependencies = (HasDependencies)providesInjector.getBinding(
         Iterables.getOnlyElement(hasDependencies.getDependencies()).getKey());
     Set<Key<?>> dependencyKeys = ImmutableSet.copyOf(
-        Iterables.transform(hasDependencies.getDependencies(),
-          new Function<Dependency<?>, Key<?>>() {
-            public Key<?> apply(Dependency<?> from) {
-              return from.getKey();
-            }
-          }));
+        Iterables.transform(hasDependencies.getDependencies(), DEPENDENCY_TO_KEY));
+    assertEquals(ImmutableSet.<Key<?>>of(Key.get(String.class), Key.get(Integer.class),
+        Key.get(Long.class), Key.get(Double.class)), dependencyKeys);
+  }  
+  
+  public void testDependencies_Cxtor() {
+    cxtorInjector = Guice.createInjector(new AbstractModule() {
+      @Override
+      protected void configure() {
+        bind(String.class).toInstance("Foo");
+        bind(Integer.class).toInstance(5);
+        bind(Double.class).toInstance(5d);
+        bind(Long.class).toInstance(5L);
+        ThrowingProviderBinder.create(binder())
+            .bind(RemoteProvider.class, Foo.class)
+            .providing(DependentMockFoo.class);
+      }
+    });
+    
+    Key<?> key = Key.get(remoteProviderOfFoo);
+    
+    // RemoteProvider<String> is dependent on Result.
+    HasDependencies hasDependencies = (HasDependencies) cxtorInjector.getBinding(key);
+    key = Iterables.getOnlyElement(hasDependencies.getDependencies()).getKey();
+    assertEquals(Result.class, key.getTypeLiteral().getRawType());
+
+    // Result is dependent on the fake CheckedProvider impl
+    hasDependencies = (HasDependencies) cxtorInjector.getBinding(key);
+    key = Iterables.getOnlyElement(hasDependencies.getDependencies()).getKey();
+    assertTrue(CheckedProvider.class.isAssignableFrom(key.getTypeLiteral().getRawType()));
+    
+    // And the CheckedProvider is dependent on DependentMockFoo...
+    hasDependencies = (HasDependencies) cxtorInjector.getBinding(key);
+    key = Iterables.getOnlyElement(hasDependencies.getDependencies()).getKey();
+    assertEquals(DependentMockFoo.class, key.getTypeLiteral().getRawType());
+    
+    // And DependentMockFoo is dependent on the goods.
+    hasDependencies = (HasDependencies) cxtorInjector.getBinding(key); 
+    Set<Key<?>> dependencyKeys = ImmutableSet.copyOf(
+        Iterables.transform(hasDependencies.getDependencies(), DEPENDENCY_TO_KEY));
     assertEquals(ImmutableSet.<Key<?>>of(Key.get(String.class), Key.get(Integer.class),
         Key.get(Long.class), Key.get(Double.class)), dependencyKeys);
   }  
@@ -440,6 +575,20 @@ public class CheckedProviderTest extends TestCase {
     public T get() throws RemoteException, BindException;
   }
   
+  static class DependentMockFoo implements Foo {
+    @Inject double foo;
+    
+    @ThrowingInject public DependentMockFoo(String foo, int bar) {
+    }
+    
+    @Inject void initialize(long foo) {}
+    
+    @Override
+    public String s() {
+      return null;
+    }
+  }
+  
   static class DependentRemoteProvider<T> implements RemoteProvider<T> {
     @Inject double foo;
     
@@ -448,11 +597,93 @@ public class CheckedProviderTest extends TestCase {
     
     @Inject void initialize(long foo) {}
     
-    public T get() throws RemoteException {
+    public T get() {
       return null;
     }
   }
   
+  interface Foo {
+    String s();
+  }
+  
+  static class SimpleFoo implements Foo {
+    private String s;
+    
+    SimpleFoo(String s) {
+      this.s = s;
+    }
+    
+    @Override
+    public String s() {
+      return s;
+    }
+    
+    @Override
+    public String toString() {
+      return s;
+    }
+  }
+  
+  static class MockFoo implements Foo {
+    static Exception nextToThrow;    
+    static String nextToReturn;
+    
+    @ThrowingInject
+    MockFoo() throws RemoteException, BindException {
+      if (nextToThrow instanceof RemoteException) {
+        throw (RemoteException) nextToThrow;
+      } else if (nextToThrow instanceof BindException) {
+        throw (BindException) nextToThrow;
+      } else if (nextToThrow instanceof RuntimeException) {
+        throw (RuntimeException) nextToThrow;
+      } else if (nextToThrow == null) {
+        // Do nothing, return this.
+      } else {
+        throw new AssertionError("nextToThrow must be a runtime or remote exception");
+      }
+    }
+    
+    @Override
+    public String s() {
+      return nextToReturn;
+    }
+    
+    @Override
+    public String toString() {
+      return nextToReturn;
+    }
+  }
+  
+  static class AnotherMockFoo implements Foo {
+    static Exception nextToThrow;    
+    static String nextToReturn;
+    
+    @ThrowingInject
+    AnotherMockFoo() throws RemoteException, BindException {
+      if (nextToThrow instanceof RemoteException) {
+        throw (RemoteException) nextToThrow;
+      } else if (nextToThrow instanceof BindException) {
+        throw (BindException) nextToThrow;
+      } else if (nextToThrow instanceof RuntimeException) {
+        throw (RuntimeException) nextToThrow;
+      } else if (nextToThrow == null) {
+        // Do nothing, return this.
+      } else {
+        throw new AssertionError("nextToThrow must be a runtime or remote exception");
+      }
+    }
+    
+    @Override
+    public String s() {
+      return nextToReturn;
+    }
+    
+    @Override
+    public String toString() {
+      return nextToReturn;
+    }
+  }
+  
   static class MockRemoteProvider<T> implements RemoteProvider<T> {
     Exception nextToThrow;
     T nextToReturn;
@@ -482,11 +713,12 @@ public class CheckedProviderTest extends TestCase {
 
   public void testBindingToInterfaceWithBoundValueType_Bind() throws RemoteException {
     bindInjector = Guice.createInjector(new AbstractModule() {
+      @Override
       protected void configure() {
         ThrowingProviderBinder.create(binder())
             .bind(StringRemoteProvider.class, String.class)
             .to(new StringRemoteProvider() {
-              public String get() throws RemoteException {
+              public String get() {
                 return "A";
               }
             });
@@ -498,6 +730,7 @@ public class CheckedProviderTest extends TestCase {
   
   public void testBindingToInterfaceWithBoundValueType_Provides() throws RemoteException {
     providesInjector = Guice.createInjector(new AbstractModule() {
+      @Override
       protected void configure() {
         install(ThrowingProviderBinder.forModule(this));
       }
@@ -516,13 +749,34 @@ public class CheckedProviderTest extends TestCase {
     String get() throws RemoteException;  
   }
 
+  @SuppressWarnings("deprecation")
   public void testBindingToInterfaceWithGeneric_Bind() throws Exception {
     bindInjector = Guice.createInjector(new AbstractModule() {
+      @Override
       protected void configure() {
         ThrowingProviderBinder.create(binder())
             .bind(RemoteProvider.class, new TypeLiteral<List<String>>() { }.getType())
             .to(new RemoteProvider<List<String>>() {
-              public List<String> get() throws RemoteException {
+              public List<String> get() {
+                return Arrays.asList("A", "B");
+              }
+            });
+      }
+    });
+
+    Key<RemoteProvider<List<String>>> key
+        = Key.get(new TypeLiteral<RemoteProvider<List<String>>>() { });
+    assertEquals(Arrays.asList("A", "B"), bindInjector.getInstance(key).get());
+  }
+  
+  public void testBindingToInterfaceWithGeneric_BindUsingTypeLiteral() throws Exception {
+    bindInjector = Guice.createInjector(new AbstractModule() {
+      @Override
+      protected void configure() {
+        ThrowingProviderBinder.create(binder())
+            .bind(RemoteProvider.class, new TypeLiteral<List<String>>() {})
+            .to(new RemoteProvider<List<String>>() {
+              public List<String> get() {
                 return Arrays.asList("A", "B");
               }
             });
@@ -536,6 +790,7 @@ public class CheckedProviderTest extends TestCase {
   
   public void testBindingToInterfaceWithGeneric_Provides() throws Exception {
     providesInjector = Guice.createInjector(new AbstractModule() {
+      @Override
       protected void configure() {
         install(ThrowingProviderBinder.forModule(this));
       }
@@ -552,9 +807,31 @@ public class CheckedProviderTest extends TestCase {
     assertEquals(Arrays.asList("A", "B"), providesInjector.getInstance(key).get());
   }
   
+  public void testBindingToInterfaceWithGeneric_Cxtor() throws Exception {
+    cxtorInjector = Guice.createInjector(new AbstractModule() {
+      @Override
+      protected void configure() {
+        ThrowingProviderBinder.create(binder())
+        .bind(RemoteProvider.class, new TypeLiteral<List<String>>() {})
+        .providing(new TypeLiteral<ThrowingArrayList<String>>() {});
+      }
+    });
+
+    Key<RemoteProvider<List<String>>> key
+        = Key.get(new TypeLiteral<RemoteProvider<List<String>>>() { });
+    assertEquals(Arrays.asList(), cxtorInjector.getInstance(key).get());
+  }
+  
+  private static class ThrowingArrayList<T> extends ArrayList<T> {
+    @SuppressWarnings("unused")
+    @ThrowingInject
+    ThrowingArrayList() {}
+  }
+  
   public void testProviderMethodWithWrongException() {
     try {
       Guice.createInjector(new AbstractModule() {
+        @Override
         protected void configure() {
           install(ThrowingProviderBinder.forModule(this));
         }
@@ -577,21 +854,54 @@ public class CheckedProviderTest extends TestCase {
     }
   }
   
+  public void testCxtorWithWrongException() {
+    try {
+      Guice.createInjector(new AbstractModule() {
+        @Override
+        protected void configure() {
+          ThrowingProviderBinder.create(binder())
+              .bind(RemoteProvider.class, Foo.class)
+              .providing(WrongExceptionFoo.class);
+        }
+      });
+      fail();
+    } catch (CreationException ce) {
+      assertEquals(InterruptedException.class.getName()
+          + " is not compatible with the exceptions (["
+          + RemoteException.class + ", " + BindException.class
+          + "]) declared in the CheckedProvider interface ("
+          + RemoteProvider.class.getName()
+          + ")", 
+          Iterables.getOnlyElement(ce.getErrorMessages()).getMessage());
+    }
+  }
+  
+  static class WrongExceptionFoo implements Foo {
+    @SuppressWarnings("unused")
+    @ThrowingInject
+    public WrongExceptionFoo() throws InterruptedException {
+    }
+    
+    @Override
+    public String s() { return null; }
+  }
+  
   public void testProviderMethodWithSubclassOfExceptionIsOk() throws Exception {
     providesInjector = Guice.createInjector(new AbstractModule() {
+      @Override
       protected void configure() {
         install(ThrowingProviderBinder.forModule(this));
       }
       
       @SuppressWarnings("unused")
       @CheckedProvides(RemoteProvider.class)
-      String foo() throws AccessException {
+      Foo foo() throws AccessException {
         throw new AccessException("boo!");
       }
     });
     
-    RemoteProvider<String> remoteProvider = 
-      providesInjector.getInstance(Key.get(remoteProviderOfString));
+    RemoteProvider<Foo> remoteProvider = 
+      providesInjector.getInstance(Key.get(remoteProviderOfFoo));
 
     try {
       remoteProvider.get();
@@ -602,16 +912,49 @@ public class CheckedProviderTest extends TestCase {
     }
   }
   
-  public void testProviderMethodWithSuperclassFails() {
+  public void testCxtorWithSubclassOfExceptionIsOk() throws Exception {
+    cxtorInjector = Guice.createInjector(new AbstractModule() {
+        @Override
+        protected void configure() {
+          ThrowingProviderBinder.create(binder())
+              .bind(RemoteProvider.class, Foo.class)
+              .providing(SubclassExceptionFoo.class);
+        }
+      });
+    
+    RemoteProvider<Foo> remoteProvider = 
+        cxtorInjector.getInstance(Key.get(remoteProviderOfFoo));
+
+      try {
+        remoteProvider.get();
+        fail();
+      } catch (RemoteException expected) {
+        assertTrue(expected instanceof AccessException);
+        assertEquals("boo!", expected.getMessage());
+      }
+  }
+  
+  static class SubclassExceptionFoo implements Foo {
+    @ThrowingInject
+    public SubclassExceptionFoo() throws AccessException {
+      throw new AccessException("boo!");
+    }
+    
+    @Override
+    public String s() { return null; }
+  }
+  
+  public void testProviderMethodWithSuperclassExceptionFails() {
     try {
       Guice.createInjector(new AbstractModule() {
+        @Override
         protected void configure() {
           install(ThrowingProviderBinder.forModule(this));
         }
         
         @SuppressWarnings("unused")
         @CheckedProvides(RemoteProvider.class)
-        String foo() throws IOException {
+        Foo foo() throws IOException {
             return null;
         }
       });
@@ -627,21 +970,54 @@ public class CheckedProviderTest extends TestCase {
     }
   }
   
+  public void testCxtorWithSuperclassExceptionFails() {
+    try {
+      Guice.createInjector(new AbstractModule() {
+        @Override
+        protected void configure() {
+          ThrowingProviderBinder.create(binder())
+              .bind(RemoteProvider.class, Foo.class)
+              .providing(SuperclassExceptionFoo.class);
+        }
+      });
+      fail();
+    } catch (CreationException ce) {
+      assertEquals(IOException.class.getName()
+          + " is not compatible with the exceptions (["
+          + RemoteException.class + ", " + BindException.class
+          + "]) declared in the CheckedProvider interface ("
+          + RemoteProvider.class.getName()
+          + ")", 
+          Iterables.getOnlyElement(ce.getErrorMessages()).getMessage());
+    }
+  }
+  
+  static class SuperclassExceptionFoo implements Foo {
+    @SuppressWarnings("unused")
+    @ThrowingInject
+    public SuperclassExceptionFoo() throws IOException {
+    }
+    
+    @Override
+    public String s() { return null; }
+  }
+  
   public void testProviderMethodWithRuntimeExceptionsIsOk() throws Exception {
     providesInjector = Guice.createInjector(new AbstractModule() {
+      @Override
       protected void configure() {
         install(ThrowingProviderBinder.forModule(this));
       }
       
       @SuppressWarnings("unused")
       @CheckedProvides(RemoteProvider.class)
-      String foo() throws RuntimeException {
+      Foo foo() throws RuntimeException {
         throw new RuntimeException("boo!");
       }
     });
     
-    RemoteProvider<String> remoteProvider = 
-      providesInjector.getInstance(Key.get(remoteProviderOfString));
+    RemoteProvider<Foo> remoteProvider = 
+      providesInjector.getInstance(Key.get(remoteProviderOfFoo));
 
     try {
       remoteProvider.get();
@@ -651,11 +1027,43 @@ public class CheckedProviderTest extends TestCase {
     }
   }
   
+  public void testCxtorWithRuntimeExceptionsIsOk() throws Exception {
+    cxtorInjector = Guice.createInjector(new AbstractModule() {
+      @Override
+      protected void configure() {
+        ThrowingProviderBinder.create(binder())
+            .bind(RemoteProvider.class, Foo.class)
+            .providing(RuntimeExceptionFoo.class);
+      }
+    });
+    
+    RemoteProvider<Foo> remoteProvider = 
+        cxtorInjector.getInstance(Key.get(remoteProviderOfFoo));
+
+    try {
+      remoteProvider.get();
+      fail();
+    } catch (RuntimeException expected) {
+      assertEquals("boo!", expected.getCause().getMessage());
+    }
+  }
+    
+  static class RuntimeExceptionFoo implements Foo {
+    @ThrowingInject
+    public RuntimeExceptionFoo() throws RuntimeException {
+      throw new RuntimeException("boo!");
+    }
+    
+    @Override
+    public String s() { return null; }
+  }
+  
   private static class SubBindException extends BindException {}
   
   public void testProviderMethodWithManyExceptions() {
     try {
       Guice.createInjector(new AbstractModule() {
+        @Override
         protected void configure() {
           install(ThrowingProviderBinder.forModule(this));
         }
@@ -690,9 +1098,59 @@ public class CheckedProviderTest extends TestCase {
     }
   }
   
+  public void testCxtorWithManyExceptions() {
+    try {
+      Guice.createInjector(new AbstractModule() {
+        @Override
+        protected void configure() {
+          ThrowingProviderBinder.create(binder())
+              .bind(RemoteProvider.class, Foo.class)
+              .providing(ManyExceptionFoo.class);
+        }
+      });
+      fail();
+    } catch (CreationException ce) {
+      // The only two that should fail are Interrupted & TooManyListeners.. the rest are OK.
+      List<Message> errors = ImmutableList.copyOf(ce.getErrorMessages());
+      assertEquals(InterruptedException.class.getName()
+          + " is not compatible with the exceptions (["
+          + RemoteException.class + ", " + BindException.class
+          + "]) declared in the CheckedProvider interface ("
+          + RemoteProvider.class.getName()
+          + ")", 
+          errors.get(0).getMessage());
+      assertEquals(TooManyListenersException.class.getName()
+          + " is not compatible with the exceptions (["
+          + RemoteException.class + ", " + BindException.class
+          + "]) declared in the CheckedProvider interface ("
+          + RemoteProvider.class.getName()
+          + ")", 
+          errors.get(1).getMessage());
+      assertEquals(2, errors.size());
+    }
+  }
+  
+  static class ManyExceptionFoo implements Foo {
+    @SuppressWarnings("unused")
+    @ThrowingInject
+    public ManyExceptionFoo()
+        throws InterruptedException,
+        RuntimeException,
+        RemoteException,
+        AccessException,
+        TooManyListenersException,
+        BindException,
+        SubBindException {
+    }
+    
+    @Override
+    public String s() { return null; }
+  }
+  
   public void testMoreTypeParameters() {
     try {
       Guice.createInjector(new AbstractModule() {
+        @Override
         protected void configure() {
           install(ThrowingProviderBinder.forModule(this));
         }
@@ -713,6 +1171,7 @@ public class CheckedProviderTest extends TestCase {
   public void testWrongThrowingProviderType() {
     try {
       Guice.createInjector(new AbstractModule() {
+        @Override
         protected void configure() {
           install(ThrowingProviderBinder.forModule(this));
         }
@@ -735,6 +1194,7 @@ public class CheckedProviderTest extends TestCase {
   public void testOneMethodThatIsntGet() {
     try {
       Guice.createInjector(new AbstractModule() {
+        @Override
         protected void configure() {
           install(ThrowingProviderBinder.forModule(this));
         }
@@ -756,6 +1216,7 @@ public class CheckedProviderTest extends TestCase {
   public void testManyMethods() {
     try {
       Guice.createInjector(new AbstractModule() {
+        @Override
         protected void configure() {
           install(ThrowingProviderBinder.forModule(this));
         }
@@ -777,11 +1238,12 @@ public class CheckedProviderTest extends TestCase {
   public void testIncorrectPredefinedType_Bind() {
     try {
       Guice.createInjector(new AbstractModule() {
+        @Override
         protected void configure() {
           ThrowingProviderBinder.create(binder())
               .bind(StringRemoteProvider.class, Integer.class)
               .to(new StringRemoteProvider() {
-                public String get() throws RemoteException {
+                public String get() {
                   return "A";
                 }
               });
@@ -798,6 +1260,7 @@ public class CheckedProviderTest extends TestCase {
   public void testIncorrectPredefinedType_Provides() {
     try {
       Guice.createInjector(new AbstractModule() {
+        @Override
         protected void configure() {
           install(ThrowingProviderBinder.forModule(this));
         }
@@ -851,6 +1314,7 @@ public class CheckedProviderTest extends TestCase {
   public void testEarlyBindingError() {
     try {
       Guice.createInjector(new AbstractModule() {
+        @Override
         protected void configure() {
           ThrowingProviderBinder.create(binder())
               .bind(StringRemoteProvider.class, String.class)
@@ -875,4 +1339,136 @@ public class CheckedProviderTest extends TestCase {
       return null;
     }
   }
+  
+  public void testNoInjectionPointForUsing() {
+    try {
+      Guice.createInjector(new AbstractModule() {
+        @Override
+        protected void configure() {
+          ThrowingProviderBinder.create(binder())
+              .bind(RemoteProvider.class, Foo.class)
+              .providing(InvalidFoo.class);
+        }
+      });
+      fail();
+    } catch (CreationException ce) {
+      assertEquals("Could not find a suitable constructor in " + InvalidFoo.class.getName()
+          + ". Classes must have either one (and only one) constructor annotated with "
+          + "@ThrowingInject.",
+          Iterables.getOnlyElement(ce.getErrorMessages()).getMessage());
+    }
+  }
+  
+  static class InvalidFoo implements Foo {
+    public InvalidFoo(String dep) {
+    }
+    
+    @Override public String s() { return null; }
+  }
+  
+  public void testNoThrowingInject() {
+    try {
+      Guice.createInjector(new AbstractModule() {
+        @Override
+        protected void configure() {
+          ThrowingProviderBinder.create(binder())
+              .bind(RemoteProvider.class, Foo.class)
+              .providing(NormalInjectableFoo.class);
+        }
+      });
+      fail();
+    } catch (CreationException ce) {
+      assertEquals("Could not find a suitable constructor in " + NormalInjectableFoo.class.getName()
+          + ". Classes must have either one (and only one) constructor annotated with "
+          + "@ThrowingInject.",
+          Iterables.getOnlyElement(ce.getErrorMessages()).getMessage());
+    }
+  }
+  
+  static class NormalInjectableFoo implements Foo {
+    @Inject
+    public NormalInjectableFoo() {
+    }
+    
+    @Override public String s() { return null; }
+  }
+  
+  public void testProvisionExceptionOnDependenciesOfCxtor() throws Exception {
+    Injector injector = Guice.createInjector(new AbstractModule() {
+        @Override
+        protected void configure() {
+          ThrowingProviderBinder.create(binder())
+              .bind(RemoteProvider.class, Foo.class)
+              .providing(ProvisionExceptionFoo.class);
+          bindScope(BadScope.class, new Scope() {
+            @Override
+            public <T> Provider<T> scope(Key<T> key, Provider<T> unscoped) {
+              return new Provider<T>() {
+                @Override
+                public T get() {
+                  throw new OutOfScopeException("failure");
+                }
+              };
+            }
+          });
+        }
+      });
+    
+    try {
+      injector.getInstance(Key.get(remoteProviderOfFoo)).get();
+      fail();
+    } catch(ProvisionException pe) {
+      assertEquals(2, pe.getErrorMessages().size());
+      List<Message> messages = Lists.newArrayList(pe.getErrorMessages());
+      assertEquals("Error in custom provider, com.google.inject.OutOfScopeException: failure",
+          messages.get(0).getMessage());
+      assertEquals("Error in custom provider, com.google.inject.OutOfScopeException: failure",
+          messages.get(1).getMessage());
+    }
+  }
+  
+  @ScopeAnnotation
+  @Target(ElementType.TYPE)
+  @Retention(RetentionPolicy.RUNTIME)
+  private @interface BadScope { }
+  
+  @BadScope private static class Unscoped1 {}
+  @BadScope private static class Unscoped2 {}
+  
+  static class ProvisionExceptionFoo implements Foo {
+    @ThrowingInject
+    public ProvisionExceptionFoo(Unscoped1 a, Unscoped2 b) {
+    }
+    
+    @Override public String s() { return null; }
+  }
+  
+  public void testUsingDoesntClashWithBindingsOfSameType() throws Exception {
+    cxtorInjector = Guice.createInjector(new AbstractModule() {
+        @Override
+        protected void configure() {
+          ThrowingProviderBinder.create(binder())
+              .bind(RemoteProvider.class, Foo.class)
+              .providing(MockFoo.class);
+          bind(Foo.class).to(MockFoo.class);
+          bind(MockFoo.class).to(SubMockFoo.class);
+        }
+      });
+    
+    RemoteProvider<Foo> remoteProvider = 
+        cxtorInjector.getInstance(Key.get(remoteProviderOfFoo));
+    Foo providerGot = remoteProvider.get();
+    Foo fooGot = cxtorInjector.getInstance(Foo.class);
+    Foo mockGot = cxtorInjector.getInstance(MockFoo.class);
+    
+    assertEquals(MockFoo.class, providerGot.getClass());
+    assertEquals(SubMockFoo.class, fooGot.getClass());
+    assertEquals(SubMockFoo.class, mockGot.getClass());
+  }
+  
+  static class SubMockFoo extends MockFoo {
+    public SubMockFoo() throws RemoteException, BindException {
+    }
+    
+  }
 }
diff --git a/guice.iml b/guice.iml
index 53b2d05..cd75c94 100644
--- a/guice.iml
+++ b/guice.iml
@@ -87,6 +87,24 @@
         <SOURCES />
       </library>
     </orderEntry>
+    <orderEntry type="module-library" exported="">
+      <library>
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/lib/guava-11.0.2.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES />
+      </library>
+    </orderEntry>
+    <orderEntry type="module-library" exported="">
+      <library>
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/lib/build/slf4j-api-1.6.4.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES />
+      </library>
+    </orderEntry>
   </component>
 </module>
 
diff --git a/pom.xml b/pom.xml
index 7180f1f..9e20e2d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -20,14 +20,14 @@ See the Apache License Version 2.0 for the specific language governing permissio
   <parent>
     <groupId>org.sonatype.forge</groupId>
     <artifactId>forge-parent</artifactId>
-    <version>10</version>
+    <version>35</version>
   </parent>
 
   <packaging>pom</packaging>
 
   <groupId>org.sonatype.sisu.inject</groupId>
   <artifactId>guice-parent</artifactId>
-  <version>3.1.1</version>
+  <version>3.1.9</version>
 
   <name>Sisu Guice</name>
 
@@ -47,15 +47,15 @@ See the Apache License Version 2.0 for the specific language governing permissio
     <mailingList>
       <name>Guice Users List</name>
       <archive>http://groups.google.com/group/google-guice/topics</archive>
-      <subscribe>google-guice+subscribe at googlegroups.com</subscribe>
-      <unsubscribe>google-guice+unsubscribe at googlegroups.com</unsubscribe>
+      <subscribe>http://groups.google.com/group/google-guice/subscribe</subscribe>
+      <unsubscribe>http://groups.google.com/group/google-guice/subscribe</unsubscribe>
       <post>http://groups.google.com/group/google-guice/post</post>
     </mailingList>
     <mailingList>
       <name>Guice Developers List</name>
       <archive>http://groups.google.com/group/google-guice-dev/topics</archive>
-      <subscribe>google-guice-dev+subscribe at googlegroups.com</subscribe>
-      <unsubscribe>google-guice-dev+unsubscribe at googlegroups.com</unsubscribe>
+      <subscribe>http://groups.google.com/group/google-guice-dev/subscribe</subscribe>
+      <unsubscribe>http://groups.google.com/group/google-guice-dev/subscribe</unsubscribe>
       <post>http://groups.google.com/group/google-guice-dev/post</post>
     </mailingList>
   </mailingLists>
@@ -116,7 +116,8 @@ See the Apache License Version 2.0 for the specific language governing permissio
     <dependency>
       <groupId>org.testng</groupId>
       <artifactId>testng</artifactId>
-      <version>6.3</version>
+      <version>5.11</version>
+      <classifier>jdk15</classifier>
       <scope>test</scope>
     </dependency>
   </dependencies>
@@ -152,7 +153,7 @@ See the Apache License Version 2.0 for the specific language governing permissio
         -->
         <plugin>
           <artifactId>maven-remote-resources-plugin</artifactId>
-          <version>1.2.1</version>
+          <version>1.1</version>
           <executions>
             <execution>
               <goals>
@@ -176,7 +177,7 @@ See the Apache License Version 2.0 for the specific language governing permissio
         <plugin>
           <groupId>org.codehaus.mojo</groupId>
           <artifactId>animal-sniffer-maven-plugin</artifactId>
-          <version>1.7</version>
+          <version>1.6</version>
           <configuration>
             <signature>
               <groupId>org.codehaus.mojo.signature</groupId>
@@ -196,10 +197,37 @@ See the Apache License Version 2.0 for the specific language governing permissio
         </plugin>
         <plugin>
           <artifactId>maven-surefire-plugin</artifactId>
-          <version>2.10</version>
+          <version>2.6</version>
           <configuration>
             <redirectTestOutputToFile>true</redirectTestOutputToFile>
+            <!--<argLine>-Dguice_include_stack_traces=OFF</argLine>-->
           </configuration>
+          <executions>
+            <execution>
+              <id>stack-traces-off</id>
+              <phase>test</phase>
+              <goals><goal>test</goal></goals>
+              <configuration>
+                <argLine>-Dguice_include_stack_traces=OFF</argLine>
+              </configuration>
+            </execution>
+            <execution>
+              <id>stack-traces-complete</id>
+              <phase>test</phase>
+              <goals><goal>test</goal></goals>
+              <configuration>
+                <argLine>-Dguice_include_stack_traces=COMPLETE</argLine>
+              </configuration>
+            </execution>
+            <execution>
+              <id>default-test</id>
+              <phase>test</phase>
+              <goals><goal>test</goal></goals>
+              <configuration>
+                <argLine>-Dguice_include_stack_traces=ONLY_FOR_DECLARING_SOURCE</argLine>
+              </configuration>
+            </execution>
+          </executions>
         </plugin>
         <!--
          | Shared OSGi manifest configuration
@@ -207,19 +235,21 @@ See the Apache License Version 2.0 for the specific language governing permissio
         <plugin>
           <groupId>org.apache.felix</groupId>
           <artifactId>maven-bundle-plugin</artifactId>
-          <version>2.3.6</version>
+          <version>2.1.0</version>
           <configuration>
             <instructions>
-              <Bundle-Name>${project.artifactId}</Bundle-Name>
-              <Bundle-SymbolicName>$(maven-symbolicname);singleton:=true</Bundle-SymbolicName>
+              <module>com.google.inject</module>
+              <_include>-${project.basedir}/build.properties</_include>
               <Bundle-Copyright>Copyright (C) 2006 Google Inc.</Bundle-Copyright>
               <Bundle-DocURL>http://code.google.com/p/google-guice/</Bundle-DocURL>
+              <Bundle-Name>${project.artifactId}</Bundle-Name>
               <Bundle-Vendor>Sonatype, Inc.</Bundle-Vendor>
               <Bundle-RequiredExecutionEnvironment>
                 J2SE-1.5,JavaSE-1.6
               </Bundle-RequiredExecutionEnvironment>
-              <_exportcontents>!*.internal.*,*;version=${guice.api.version}</_exportcontents>
-              <Import-Package>!com.google.*,*</Import-Package>
+              <Import-Package>!com.google.inject.*,*</Import-Package>
+              <_exportcontents>!*.internal.*,$(module).*;version=${guice.api.version}</_exportcontents>
+              <_versionpolicy>[$(version;==;$(@)),$(version;+;$(@)))</_versionpolicy>
               <_nouses>true</_nouses>
               <_removeheaders>
                 Embed-Dependency,Embed-Transitive,
@@ -229,9 +259,10 @@ See the Apache License Version 2.0 for the specific language governing permissio
                 Ignore-Package,Bnd-LastModified
               </_removeheaders>
             </instructions>
-            <archive>
-              <forced>true</forced><!-- dummy entry to stop bundleplugin from picking up jar config -->
-            </archive>
+            <!--
+             | Exclude from version calculations, as it doesn't use semantic versioning
+            -->
+            <excludeDependencies>guava</excludeDependencies>
           </configuration>
           <executions>
             <execution>
@@ -247,11 +278,12 @@ See the Apache License Version 2.0 for the specific language governing permissio
         -->
         <plugin>
           <artifactId>maven-jar-plugin</artifactId>
-          <version>2.3.2</version>
+          <version>2.3.1</version>
           <configuration>
             <archive>
               <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
-              <addMavenDescriptor>false</addMavenDescriptor><!--  Exclude to mirror ant build -->
+              <!--  Exclude to mirror ant build -->
+          <addMavenDescriptor>false</addMavenDescriptor>
             </archive>
           </configuration>
           <executions>
@@ -265,7 +297,7 @@ See the Apache License Version 2.0 for the specific language governing permissio
         </plugin>
         <plugin>
           <artifactId>maven-javadoc-plugin</artifactId>
-          <version>2.8</version>
+          <version>2.7</version>
           <configuration>
             <doclet>com.google.doclava.Doclava</doclet>
             <docletPath>
@@ -293,7 +325,7 @@ See the Apache License Version 2.0 for the specific language governing permissio
         </plugin>
         <plugin>
           <artifactId>maven-site-plugin</artifactId>
-          <version>3.0</version>
+          <version>3.2</version>
         </plugin>
         <plugin>
           <artifactId>maven-source-plugin</artifactId>
diff --git a/uploadApiDiffs.sh b/uploadApiDiffs.sh
index 80eab79..83e2166 100644
--- a/uploadApiDiffs.sh
+++ b/uploadApiDiffs.sh
@@ -1,9 +1,8 @@
 rm -rf build/docs
-CV=3.0
+CV=4.0
 
 # remove old api-diffs
-svn rm latest-api-diffs/$CV
-svn ci -m "Removed old $CV api diffs." latest-api-diffs/$CV
+git rm -r latest-api-diffs/$CV
 
 # create new api-diffs
 ant jdiff
@@ -16,5 +15,5 @@ ant javadoc
 cp -r build/docs/javadoc latest-api-diffs/$CV/javadoc
 
 # commit changes
-svn add latest-api-diffs/$CV
-svn ci -m "Added updated $CV api diffs." latest-api-diffs/$CV latest-api-diffs/$CV.xml
+git add -A latest-api-diffs
+git commit -m "Added updated $CV api diffs."  latest-api-diffs

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/sisu-guice.git



More information about the pkg-java-commits mailing list