issue 610 - let requireExplicitBindings still allow TypeConverters to create ConvertedConstantBindings.
git-svn-id: https://google-guice.googlecode.com/svn/trunk@1512 d779f126-a31b-0410-b53b-1d3aecad763e
diff --git a/core/src/com/google/inject/internal/InjectorImpl.java b/core/src/com/google/inject/internal/InjectorImpl.java
index 6f332fb..929e3b7 100644
--- a/core/src/com/google/inject/internal/InjectorImpl.java
+++ b/core/src/com/google/inject/internal/InjectorImpl.java
@@ -232,11 +232,7 @@
private <T> BindingImpl<T> getJustInTimeBinding(Key<T> key, Errors errors, JitLimitation jitType)
throws ErrorsException {
- boolean jitOverride = isProvider(key) || isTypeLiteral(key) || isMembersInjector(key);
- if(options.jitDisabled && jitType == JitLimitation.NO_JIT && !jitOverride) {
- throw errors.jitDisabled(key).toException();
- }
-
+ boolean jitOverride = isProvider(key) || isTypeLiteral(key) || isMembersInjector(key);
synchronized (state.lock()) {
// first try to find a JIT binding that we've already created
for (InjectorImpl injector = this; injector != null; injector = injector.parent) {
@@ -244,15 +240,20 @@
BindingImpl<T> binding = (BindingImpl<T>) injector.jitBindings.get(key);
if (binding != null) {
- return binding;
+ // If we found a JIT binding and we don't allow them,
+ // fail. (But allow bindings created through TypeConverters.)
+ if (options.jitDisabled
+ && jitType == JitLimitation.NO_JIT
+ && !jitOverride
+ && !(binding instanceof ConvertedConstantBindingImpl)) {
+ throw errors.jitDisabled(key).toException();
+ } else {
+ return binding;
+ }
}
}
- if(options.jitDisabled && jitType != JitLimitation.NEW_OR_EXISTING_JIT && !jitOverride) {
- throw errors.jitDisabled(key).toException();
- } else {
- return createJustInTimeBindingRecursive(key, errors);
- }
+ return createJustInTimeBindingRecursive(key, errors, options.jitDisabled, jitType);
}
}
@@ -752,12 +753,13 @@
* Attempts to create a just-in-time binding for {@code key} in the root injector, falling back to
* other ancestor injectors until this injector is tried.
*/
- private <T> BindingImpl<T> createJustInTimeBindingRecursive(Key<T> key, Errors errors)
- throws ErrorsException {
+ private <T> BindingImpl<T> createJustInTimeBindingRecursive(Key<T> key, Errors errors,
+ boolean jitDisabled, JitLimitation jitType) throws ErrorsException {
// ask the parent to create the JIT binding
- if (parent != null && !parent.options.jitDisabled) {
+ if (parent != null) {
try {
- return parent.createJustInTimeBindingRecursive(key, new Errors());
+ return parent.createJustInTimeBindingRecursive(key, new Errors(), jitDisabled,
+ parent.options.jitDisabled ? JitLimitation.NO_JIT : jitType);
} catch (ErrorsException ignored) {
}
}
@@ -767,7 +769,7 @@
throw errors.childBindingAlreadySet(key, sources).toException();
}
- BindingImpl<T> binding = createJustInTimeBinding(key, errors);
+ BindingImpl<T> binding = createJustInTimeBinding(key, errors, jitDisabled, jitType);
state.parent().blacklist(key, binding.getSource());
jitBindings.put(key, binding);
return binding;
@@ -786,8 +788,8 @@
*
* @throws com.google.inject.internal.ErrorsException if the binding cannot be created.
*/
- private <T> BindingImpl<T> createJustInTimeBinding(Key<T> key, Errors errors)
- throws ErrorsException {
+ private <T> BindingImpl<T> createJustInTimeBinding(Key<T> key, Errors errors,
+ boolean jitDisabled, JitLimitation jitType) throws ErrorsException {
int numErrorsBefore = errors.size();
if (state.isBlacklisted(key)) {
@@ -818,6 +820,12 @@
if (convertedBinding != null) {
return convertedBinding;
}
+
+ if (!isTypeLiteral(key)
+ && jitDisabled
+ && jitType != JitLimitation.NEW_OR_EXISTING_JIT) {
+ throw errors.jitDisabled(key).toException();
+ }
// If the key has an annotation...
if (key.getAnnotationType() != null) {
diff --git a/core/test/com/google/inject/JitBindingsTest.java b/core/test/com/google/inject/JitBindingsTest.java
index 8a7f9b4..500c9dc 100644
--- a/core/test/com/google/inject/JitBindingsTest.java
+++ b/core/test/com/google/inject/JitBindingsTest.java
@@ -41,6 +41,12 @@
return "Explicit bindings are required and " + clazz + " is not explicitly bound.";
}
+ private String inChildMessage(Class<?> clazz) {
+ return "Unable to create binding for "
+ + clazz.getName()
+ + ". It was already configured on one or more child injectors or private modules";
+ }
+
public void testLinkedBindingWorks() {
Injector injector = Guice.createInjector(new AbstractModule() {
@Override
@@ -317,7 +323,7 @@
});
ensureWorks(child, Foo.class, Bar.class);
ensureFails(child, ALLOW_BINDING, FooImpl.class);
- ensureFails(parent, FAIL_ALL, FooImpl.class, FooBar.class, Foo.class); // parent still doesn't have these
+ ensureInChild(parent, FooImpl.class, FooBar.class, Foo.class);
Injector grandchild = child.createChildInjector(new AbstractModule() {
@Override
@@ -328,7 +334,7 @@
ensureWorks(grandchild, FooBar.class, Foo.class, Bar.class);
ensureFails(grandchild, ALLOW_BINDING, FooImpl.class);
ensureFails(child, ALLOW_BINDING, FooImpl.class);
- ensureFails(parent, FAIL_ALL, FooImpl.class, FooBar.class, Foo.class); // parent still doesn't have these
+ ensureInChild(parent, FooImpl.class, FooBar.class, Foo.class);
}
public void testChildInjectorAddsOption() {
@@ -418,6 +424,20 @@
assertContains(expected.getMessage(), "1) " + jitFailed(Bar.class));
assertTrue(expected.getMessage(), !expected.getMessage().contains("2) "));
}
+
+ Injector injector = Guice.createInjector(new AbstractModule() {
+ protected void configure() {
+ binder().requireExplicitBindings();
+
+ install(new PrivateModule() {
+ public void configure() {
+ bind(Foo.class).to(FooImpl.class);
+ expose(Foo.class);
+ }
+ });
+ }
+ });
+ ensureInChild(injector, FooImpl.class);
}
public void testPrivateModuleAddsOption() {
@@ -553,6 +573,34 @@
}
}
+ private void ensureInChild(Injector injector, Class<?>... classes) {
+ for(int i = 0; i < classes.length; i++) {
+ try {
+ injector.getInstance(classes[i]);
+ fail("should have failed tring to retrieve class: " + classes[i]);
+ } catch(ConfigurationException expected) {
+ assertContains(expected.getMessage(), "1) " + inChildMessage(classes[i]));
+ assertTrue(expected.getMessage(), !expected.getMessage().contains("2) "));
+ }
+
+ try {
+ injector.getProvider(classes[i]);
+ fail("should have failed tring to retrieve class: " + classes[i]);
+ } catch(ConfigurationException expected) {
+ assertContains(expected.getMessage(), "1) " + inChildMessage(classes[i]));
+ assertTrue(expected.getMessage(), !expected.getMessage().contains("2) "));
+ }
+
+ try {
+ injector.getBinding(classes[i]);
+ fail("should have failed tring to retrieve class: " + classes[i]);
+ } catch(ConfigurationException expected) {
+ assertContains(expected.getMessage(), "1) " + inChildMessage(classes[i]));
+ assertTrue(expected.getMessage(), !expected.getMessage().contains("2) "));
+ }
+ }
+ }
+
private static interface Foo {}
private static class FooImpl implements Foo {}
@Singleton private static class ScopedFooImpl implements Foo {}
diff --git a/core/test/com/google/inject/TypeConversionTest.java b/core/test/com/google/inject/TypeConversionTest.java
index 3d57d31..31f651d 100644
--- a/core/test/com/google/inject/TypeConversionTest.java
+++ b/core/test/com/google/inject/TypeConversionTest.java
@@ -114,6 +114,39 @@
assertEquals(Bar.TEE, foo.enumField);
assertEquals(Foo.class, foo.classField);
}
+
+ public void testConstantInjectionWithExplicitBindingsRequired() throws CreationException {
+ Injector injector = Guice.createInjector(new AbstractModule() {
+ protected void configure() {
+ binder().requireExplicitBindings();
+ bind(Foo.class);
+ bindConstant().annotatedWith(NumericValue.class).to("5");
+ bindConstant().annotatedWith(BooleanValue.class).to("true");
+ bindConstant().annotatedWith(EnumValue.class).to("TEE");
+ bindConstant().annotatedWith(ClassName.class).to(Foo.class.getName());
+ }
+ });
+
+ Foo foo = injector.getInstance(Foo.class);
+
+ checkNumbers(
+ foo.integerField,
+ foo.primitiveIntField,
+ foo.longField,
+ foo.primitiveLongField,
+ foo.byteField,
+ foo.primitiveByteField,
+ foo.shortField,
+ foo.primitiveShortField,
+ foo.floatField,
+ foo.primitiveFloatField,
+ foo.doubleField,
+ foo.primitiveDoubleField
+ );
+
+ assertEquals(Bar.TEE, foo.enumField);
+ assertEquals(Foo.class, foo.classField);
+ }
void checkNumbers(Number... ns) {
for (Number n : ns) {