Skip to content

Commit

Permalink
Merge pull request google#271 from cgruber/loadfromentrypoints
Browse files Browse the repository at this point in the history
Request classloading from the classloader of the requesting object
  • Loading branch information
cgruber committed Jun 6, 2013
2 parents 2cacf63 + 5b4ba94 commit 08fd708
Show file tree
Hide file tree
Showing 18 changed files with 124 additions and 67 deletions.
5 changes: 4 additions & 1 deletion checkstyle.xml
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,10 @@
<module name="LineLength">
<property name="max" value="100"/>
</module>
<module name="MethodLength"/>
<module name="MethodLength">
<property name="max" value="200"/>
</module>

<!--module name="ParameterNumber"/-->


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,10 +113,12 @@ private static boolean hasAtInject(Element enclosed) {
@Override public void attach(Linker linker) {
String requiredBy = type.getQualifiedName().toString();
for (int i = 0; i < keys.size(); i++) {
bindings[i] = linker.requestBinding(keys.get(i), requiredBy);
bindings[i] = linker.requestBinding(keys.get(i), requiredBy,
getClass().getClassLoader());
}
if (supertypeKey != null) {
supertypeBinding = linker.requestBinding(supertypeKey, requiredBy, false, true);
supertypeBinding = linker.requestBinding(supertypeKey, requiredBy,
getClass().getClassLoader(), false, true);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ public CodeGenStaticInjection(Element enclosingClass) {
Inject injectAnnotation = enclosedElement.getAnnotation(Inject.class);
if (injectAnnotation != null) {
String key = GeneratorKeys.get(enclosedElement.asType());
linker.requestBinding(key, enclosingClass.toString());
linker.requestBinding(key, enclosingClass.toString(),
getClass().getClassLoader());
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public CompileTimeLoader(ProcessingEnvironment processingEnv) {
}

@Override public Binding<?> getAtInjectBinding(
String key, String className, boolean mustHaveInjections) {
String key, String className, ClassLoader classLoader, boolean mustHaveInjections) {
String sourceClassName = className.replace('$', '.');
TypeElement type = processingEnv.getElementUtils().getTypeElement(sourceClassName);
if (type == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,8 @@ private Map<String, Binding<?>> processCompleteModule(TypeElement rootModule,
String key = CodeGen.isInterface(injectableType)
? GeneratorKeys.get(injectableType)
: GeneratorKeys.rawMembersKey(injectableType);
linker.requestBinding(key, module.getQualifiedName().toString(), false, true);
linker.requestBinding(key, module.getQualifiedName().toString(),
getClass().getClassLoader(), false, true);
}

// Gather the static injections.
Expand Down Expand Up @@ -286,7 +287,8 @@ protected ProviderMethodBinding(String provideKey, ExecutableElement method, boo
for (int i = 0; i < method.getParameters().size(); i++) {
VariableElement parameter = method.getParameters().get(i);
String parameterKey = GeneratorKeys.get(parameter);
parameters[i] = linker.requestBinding(parameterKey, method.toString());
parameters[i] = linker.requestBinding(parameterKey, method.toString(),
getClass().getClassLoader());
}
}

Expand Down
23 changes: 13 additions & 10 deletions compiler/src/main/java/dagger/internal/codegen/InjectProcessor.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;

import static dagger.internal.codegen.CodeGen.typeToString;
import static dagger.internal.codegen.ProcessorJavadocs.binderTypeDocs;
import static dagger.internal.loaders.generated.GeneratedAdapterLoader.INJECT_ADAPTER_SUFFIX;
import static dagger.internal.loaders.generated.GeneratedAdapterLoader.STATIC_INJECTION_SUFFIX;
Expand Down Expand Up @@ -277,24 +278,26 @@ private void writeInjectAdapter(TypeElement type, ExecutableElement constructor,
writer.beginMethod("void", "attach", PUBLIC, Linker.class.getCanonicalName(), "linker");
if (constructor != null) {
for (VariableElement parameter : constructor.getParameters()) {
writer.emitStatement("%s = (%s) linker.requestBinding(%s, %s.class)",
writer.emitStatement(
"%s = (%s) linker.requestBinding(%s, %s.class, getClass().getClassLoader())",
parameterName(disambiguateFields, parameter),
writer.compressType(JavaWriter.type(Binding.class,
CodeGen.typeToString(parameter.asType()))),
writer.compressType(JavaWriter.type(Binding.class, typeToString(parameter.asType()))),
JavaWriter.stringLiteral(GeneratorKeys.get(parameter)),
strippedTypeName);
}
}
for (Element field : fields) {
writer.emitStatement("%s = (%s) linker.requestBinding(%s, %s.class)",
writer.emitStatement(
"%s = (%s) linker.requestBinding(%s, %s.class, getClass().getClassLoader())",
fieldName(disambiguateFields, field),
writer.compressType(JavaWriter.type(Binding.class,
CodeGen.typeToString(field.asType()))),
writer.compressType(JavaWriter.type(Binding.class, typeToString(field.asType()))),
JavaWriter.stringLiteral(GeneratorKeys.get((VariableElement) field)),
strippedTypeName);
}
if (supertype != null) {
writer.emitStatement("%s = (%s) linker.requestBinding(%s, %s.class, false, true)",
writer.emitStatement(
"%s = (%s) linker.requestBinding(%s, %s.class, getClass().getClassLoader()"
+ ", false, true)", // Yep. This is a dumb line-length violation otherwise.
"supertype",
writer.compressType(JavaWriter.type(Binding.class,
CodeGen.rawTypeToString(supertype, '.'))),
Expand Down Expand Up @@ -427,10 +430,10 @@ private void writeStaticInjection(TypeElement type, List<Element> fields) throws
writer.emitAnnotation(Override.class);
writer.beginMethod("void", "attach", PUBLIC, Linker.class.getName(), "linker");
for (Element field : fields) {
writer.emitStatement("%s = (%s) linker.requestBinding(%s, %s.class)",
writer.emitStatement(
"%s = (%s) linker.requestBinding(%s, %s.class, getClass().getClassLoader())",
fieldName(false, field),
writer.compressType(JavaWriter.type(Binding.class,
CodeGen.typeToString(field.asType()))),
writer.compressType(JavaWriter.type(Binding.class, typeToString(field.asType()))),
JavaWriter.stringLiteral(GeneratorKeys.get((VariableElement) field)),
typeName);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -438,7 +438,8 @@ private void writeProvidesAdapter(JavaWriter writer, ExecutableElement providerM
writer.beginMethod("void", "attach", PUBLIC, Linker.class.getCanonicalName(), "linker");
for (VariableElement parameter : parameters) {
String parameterKey = GeneratorKeys.get(parameter);
writer.emitStatement("%s = (%s) linker.requestBinding(%s, %s.class)",
writer.emitStatement(
"%s = (%s) linker.requestBinding(%s, %s.class, getClass().getClassLoader())",
parameterName(parameter),
writer.compressType(JavaWriter.type(Binding.class,
CodeGen.typeToString(parameter.asType()))),
Expand Down
25 changes: 16 additions & 9 deletions core/src/main/java/dagger/ObjectGraph.java
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,8 @@ private void linkStaticInjections() {

private void linkInjectableTypes() {
for (Map.Entry<String, Class<?>> entry : injectableTypes.entrySet()) {
linker.requestBinding(entry.getKey(), entry.getValue(), false, true);
linker.requestBinding(entry.getKey(), entry.getValue(), entry.getValue().getClassLoader(),
false, true);
}
}

Expand Down Expand Up @@ -242,43 +243,49 @@ private Map<String, Binding<?>> linkEverything() {
@Override public <T> T get(Class<T> type) {
String key = Keys.get(type);
String injectableTypeKey = type.isInterface() ? key : Keys.getMembersKey(type);
ClassLoader classLoader = type.getClassLoader();
@SuppressWarnings("unchecked") // The linker matches keys to bindings by their type.
Binding<T> binding = (Binding<T>) getInjectableTypeBinding(injectableTypeKey, key);
Binding<T> binding =
(Binding<T>) getInjectableTypeBinding(classLoader, injectableTypeKey, key);
return binding.get();
}

@Override public <T> T inject(T instance) {
String membersKey = Keys.getMembersKey(instance.getClass());
ClassLoader classLoader = instance.getClass().getClassLoader();
@SuppressWarnings("unchecked") // The linker matches keys to bindings by their type.
Binding<Object> binding = (Binding<Object>) getInjectableTypeBinding(membersKey, membersKey);
Binding<T> binding =
(Binding<T>) getInjectableTypeBinding(classLoader, membersKey, membersKey);
binding.injectMembers(instance);
return instance;
}

/**
* @param injectableTypeKey the key used to store the injectable type. This
* @param classLoader the {@code ClassLoader} used to load dependent bindings.
* @param injectableKey the key used to store the injectable type. This
* is a provides key for interfaces and a members injection key for
* other types. That way keys can always be created, even if the type
* has no injectable constructor.
* @param key the key to use when retrieving the binding. This may be a
* regular (provider) key or a members key.
*/
private Binding<?> getInjectableTypeBinding(String injectableTypeKey, String key) {
private Binding<?> getInjectableTypeBinding(
ClassLoader classLoader, String injectableKey, String key) {
Class<?> moduleClass = null;
for (DaggerObjectGraph graph = this; graph != null; graph = graph.base) {
moduleClass = graph.injectableTypes.get(injectableTypeKey);
moduleClass = graph.injectableTypes.get(injectableKey);
if (moduleClass != null) break;
}
if (moduleClass == null) {
throw new IllegalArgumentException("No inject registered for " + injectableTypeKey
throw new IllegalArgumentException("No inject registered for " + injectableKey
+ ". You must explicitly add it to the 'injects' option in one of your modules.");
}

synchronized (linker) {
Binding<?> binding = linker.requestBinding(key, moduleClass, false, true);
Binding<?> binding = linker.requestBinding(key, moduleClass, classLoader, false, true);
if (binding == null || !binding.isLinked()) {
linker.linkRequested();
binding = linker.requestBinding(key, moduleClass, false, true);
binding = linker.requestBinding(key, moduleClass, classLoader, false, true);
}
return binding;
}
Expand Down
7 changes: 5 additions & 2 deletions core/src/main/java/dagger/internal/BuiltInBinding.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,18 @@
*/
final class BuiltInBinding<T> extends Binding<T> {
private final String delegateKey;
private final ClassLoader classLoader;
private Binding<?> delegate;

public BuiltInBinding(String key, Object requiredBy, String delegateKey) {
public BuiltInBinding(
String key, Object requiredBy, ClassLoader classLoader, String delegateKey) {
super(key, null, false, requiredBy);
this.classLoader = classLoader;
this.delegateKey = delegateKey;
}

@Override public void attach(Linker linker) {
delegate = linker.requestBinding(delegateKey, requiredBy);
delegate = linker.requestBinding(delegateKey, requiredBy, classLoader);
}

@Override public void injectMembers(T t) {
Expand Down
6 changes: 4 additions & 2 deletions core/src/main/java/dagger/internal/LazyBinding.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,19 @@ final class LazyBinding<T> extends Binding<Lazy<T>> {
private final static Object NOT_PRESENT = new Object();

private final String lazyKey;
private final ClassLoader loader;
private Binding<T> delegate;

public LazyBinding(String key, Object requiredBy, String lazyKey) {
public LazyBinding(String key, Object requiredBy, ClassLoader loader, String lazyKey) {
super(key, null, false, requiredBy);
this.loader = loader;
this.lazyKey = lazyKey;
}

@SuppressWarnings("unchecked") // At runtime we know it's a Binding<Lazy<T>>.
@Override
public void attach(Linker linker) {
delegate = (Binding<T>) linker.requestBinding(lazyKey, requiredBy);
delegate = (Binding<T>) linker.requestBinding(lazyKey, requiredBy, loader);
}

@Override public void injectMembers(Lazy<T> t) {
Expand Down
56 changes: 39 additions & 17 deletions core/src/main/java/dagger/internal/Linker.java
Original file line number Diff line number Diff line change
Expand Up @@ -99,14 +99,15 @@ public void linkRequested() {
Binding<?> binding;
while ((binding = toLink.poll()) != null) {
if (binding instanceof DeferredBinding) {
DeferredBinding deferredBinding = (DeferredBinding) binding;
String key = deferredBinding.deferredKey;
boolean mustHaveInjections = deferredBinding.mustHaveInjections;
DeferredBinding deferred = (DeferredBinding) binding;
String key = deferred.deferredKey;
boolean mustHaveInjections = deferred.mustHaveInjections;
if (bindings.containsKey(key)) {
continue; // A binding for this key has since been linked.
}
try {
Binding<?> jitBinding = createJitBinding(key, binding.requiredBy, mustHaveInjections);
Binding<?> jitBinding =
createJitBinding(key, binding.requiredBy, deferred.classLoader, mustHaveInjections);
jitBinding.setLibrary(binding.library());
jitBinding.setDependedOn(binding.dependedOn());
// Fail if the type of binding we got wasn't capable of what was requested.
Expand Down Expand Up @@ -167,35 +168,51 @@ private void assertLockHeld() {
* <li>Injections of other types will use the injectable constructors of those classes.
* </ul>
*/
private Binding<?> createJitBinding(String key, Object requiredBy, boolean mustHaveInjections)
throws ClassNotFoundException {
private Binding<?> createJitBinding(String key, Object requiredBy, ClassLoader classLoader,
boolean mustHaveInjections) {
String builtInBindingsKey = Keys.getBuiltInBindingsKey(key);
if (builtInBindingsKey != null) {
return new BuiltInBinding<Object>(key, requiredBy, builtInBindingsKey);
return new BuiltInBinding<Object>(key, requiredBy, classLoader, builtInBindingsKey);
}
String lazyKey = Keys.getLazyKey(key);
if (lazyKey != null) {
return new LazyBinding<Object>(key, requiredBy, lazyKey);
return new LazyBinding<Object>(key, requiredBy, classLoader, lazyKey);
}

String className = Keys.getClassName(key);
if (className != null && !Keys.isAnnotated(key)) {
Binding<?> atInjectBinding = plugin.getAtInjectBinding(key, className, mustHaveInjections);
if (atInjectBinding != null) {
return atInjectBinding;
Binding<?> binding =
plugin.getAtInjectBinding(key, className, classLoader, mustHaveInjections);
if (binding != null) {
return binding;
}
}

throw new IllegalArgumentException("No binding for " + key);
}

/** @deprecated Older, generated code still using this should be re-generated. */
@Deprecated
public Binding<?> requestBinding(String key, Object requiredBy) {
return requestBinding(
key, requiredBy, getClass().getClassLoader(), true, true);
}

/**
* Returns the binding if it exists immediately. Otherwise this returns
* null. If the returned binding didn't exist or was unlinked, it will be
* enqueued to be linked.
*/
public Binding<?> requestBinding(String key, Object requiredBy) {
return requestBinding(key, requiredBy, true, true);
public Binding<?> requestBinding(String key, Object requiredBy, ClassLoader classLoader) {
return requestBinding(key, requiredBy, classLoader, true, true);
}

/** @deprecated Older, generated code still using this should be re-generated. */
@Deprecated
public Binding<?> requestBinding(String key, Object requiredBy,
boolean mustHaveInjections, boolean library) {
return requestBinding(key, requiredBy, getClass().getClassLoader(),
mustHaveInjections, library);
}

/**
Expand All @@ -209,8 +226,8 @@ public Binding<?> requestBinding(String key, Object requiredBy) {
* to inject arbitrary classes like JUnit test cases and Android
* activities. It also isn't necessary for supertypes.
*/
public Binding<?> requestBinding(String key, Object requiredBy, boolean mustHaveInjections,
boolean library) {
public Binding<?> requestBinding(String key, Object requiredBy, ClassLoader classLoader,
boolean mustHaveInjections, boolean library) {
assertLockHeld();

Binding<?> binding = null;
Expand All @@ -224,7 +241,8 @@ public Binding<?> requestBinding(String key, Object requiredBy, boolean mustHave

if (binding == null) {
// We can't satisfy this binding. Make sure it'll work next time!
Binding<?> deferredBinding = new DeferredBinding(key, requiredBy, mustHaveInjections);
Binding<?> deferredBinding =
new DeferredBinding(key, classLoader, requiredBy, mustHaveInjections);
deferredBinding.setLibrary(library);
deferredBinding.setDependedOn(true);
toLink.add(deferredBinding);
Expand Down Expand Up @@ -384,12 +402,16 @@ public interface ErrorHandler {
}

private static class DeferredBinding extends Binding<Object> {
/** Loader originally intended to load this binding, to be used in loading the actual one */
final ClassLoader classLoader;
final String deferredKey;
final boolean mustHaveInjections;

private DeferredBinding(String deferredKey, Object requiredBy, boolean mustHaveInjections) {
private DeferredBinding(String deferredKey, ClassLoader classLoader, Object requiredBy,
boolean mustHaveInjections) {
super(null, null, false, requiredBy);
this.deferredKey = deferredKey;
this.classLoader = classLoader;
this.mustHaveInjections = mustHaveInjections;
}
@Override public void injectMembers(Object t) {
Expand Down
3 changes: 2 additions & 1 deletion core/src/main/java/dagger/internal/Loader.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ public interface Loader {
/**
* Returns a binding that uses {@code @Inject} annotations.
*/
Binding<?> getAtInjectBinding(String key, String className, boolean mustHaveInjections);
Binding<?> getAtInjectBinding(
String key, String className, ClassLoader classLoader, boolean mustHaveInjections);

/**
* Returns a module adapter for {@code module}.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,10 @@ private static void collectIncludedModulesRecursively(Loader plugin, ModuleAdapt
}

@Override public Binding<?> getAtInjectBinding(String key, String className,
boolean mustHaveInjections) {
ClassLoader classLoader, boolean mustHaveInjections) {
for (int i = 0; i < plugins.length; i++) {
try {
return plugins[i].getAtInjectBinding(key, className, mustHaveInjections);
return plugins[i].getAtInjectBinding(key, className, classLoader, mustHaveInjections);
} catch (RuntimeException e) {
if (i == plugins.length - 1) throw e;
logNotFound("Binding", className, e);
Expand Down
Loading

0 comments on commit 08fd708

Please sign in to comment.