Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Load classes from loader of the requesting object, starting at an entry point. #4

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,
Thread.currentThread().getContextClassLoader());
}
if (supertypeKey != null) {
supertypeBinding = linker.requestBinding(supertypeKey, requiredBy, false, true);
supertypeBinding = linker.requestBinding(supertypeKey, requiredBy,
Thread.currentThread().getContextClassLoader(), 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(),
Thread.currentThread().getContextClassLoader());
}
}
}
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 cl, 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(),
Thread.currentThread().getContextClassLoader(), 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(),
Thread.currentThread().getContextClassLoader());
}
}

Expand Down
34 changes: 20 additions & 14 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,29 +278,33 @@ 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, %s.class.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);
strippedTypeName,
adapterName);
}
}
for (Element field : fields) {
writer.emitStatement("%s = (%s) linker.requestBinding(%s, %s.class)",
writer.emitStatement(
"%s = (%s) linker.requestBinding(%s, %s.class, %s.class.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);
strippedTypeName,
adapterName);
}
if (supertype != null) {
writer.emitStatement("%s = (%s) linker.requestBinding(%s, %s.class, false, true)",
writer.emitStatement(
"%s = (%s) linker.requestBinding(%s, %s.class, %s.class.getClassLoader(), false, true)",
"supertype",
writer.compressType(JavaWriter.type(Binding.class,
CodeGen.rawTypeToString(supertype, '.'))),
JavaWriter.stringLiteral(GeneratorKeys.rawMembersKey(supertype)),
strippedTypeName);
strippedTypeName,
adapterName);
}
writer.endMethod();

Expand Down Expand Up @@ -427,12 +432,13 @@ 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, %s.class.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);
typeName,
adapterName);
}
writer.endMethod();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -438,12 +438,14 @@ 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, %s.class.getClassLoader())",
parameterName(parameter),
writer.compressType(JavaWriter.type(Binding.class,
CodeGen.typeToString(parameter.asType()))),
JavaWriter.stringLiteral(parameterKey),
writer.compressType(moduleType));
writer.compressType(moduleType),
className);
}
writer.endMethod();

Expand Down
22 changes: 13 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,46 @@ 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 cl = 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(cl, injectableTypeKey, key);
return binding.get();
}

@Override public <T> T inject(T instance) {
String membersKey = Keys.getMembersKey(instance.getClass());
ClassLoader cl = 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(cl, membersKey, membersKey);
binding.injectMembers(instance);
return instance;
}

/**
* @param injectableTypeKey the key used to store the injectable type. This
* @param cl 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 cl, 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, cl, false, true);
if (binding == null || !binding.isLinked()) {
linker.linkRequested();
binding = linker.requestBinding(key, moduleClass, false, true);
binding = linker.requestBinding(key, moduleClass, cl, false, true);
}
return binding;
}
Expand Down
6 changes: 4 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,17 @@
*/
final class BuiltInBinding<T> extends Binding<T> {
private final String delegateKey;
private final ClassLoader loader;
private Binding<?> delegate;

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

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

@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
40 changes: 23 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,22 +168,22 @@ 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 cl,
boolean mustHaveInjections) {
String builtInBindingsKey = Keys.getBuiltInBindingsKey(key);
if (builtInBindingsKey != null) {
return new BuiltInBinding<Object>(key, requiredBy, builtInBindingsKey);
return new BuiltInBinding<Object>(key, requiredBy, cl, builtInBindingsKey);
}
String lazyKey = Keys.getLazyKey(key);
if (lazyKey != null) {
return new LazyBinding<Object>(key, requiredBy, lazyKey);
return new LazyBinding<Object>(key, requiredBy, cl, 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, cl, mustHaveInjections);
if (binding != null) {
return binding;
}
}

Expand All @@ -194,23 +195,24 @@ private Binding<?> createJitBinding(String key, Object requiredBy, boolean mustH
* 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 cl) {
return requestBinding(key, requiredBy, cl, 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.
* @param cl
*
* @param mustHaveInjections true if the the referenced key requires either an
* {@code @Inject} annotation is produced by a {@code @Provides} method.
* This isn't necessary for Module.injects types because frameworks need
* 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 cl,
boolean mustHaveInjections, boolean library) {
assertLockHeld();

Binding<?> binding = null;
Expand All @@ -224,7 +226,7 @@ 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, cl, requiredBy, mustHaveInjections);
deferredBinding.setLibrary(library);
deferredBinding.setDependedOn(true);
toLink.add(deferredBinding);
Expand Down Expand Up @@ -384,12 +386,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 cl, boolean mustHaveInjections);

/**
* Returns a module adapter for {@code module}.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,11 @@ private static void collectIncludedModulesRecursively(Loader plugin, ModuleAdapt
throw new AssertionError();
}

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