Skip to content

Commit

Permalink
Merge pull request square#23 from square/jwilson/moduleadapter
Browse files Browse the repository at this point in the history
Optimize ObjectGraph.
  • Loading branch information
pforhan committed Jul 18, 2012
2 parents c1c7d6b + b5d5fc1 commit a081fb0
Show file tree
Hide file tree
Showing 8 changed files with 310 additions and 200 deletions.
50 changes: 50 additions & 0 deletions src/main/java/com/squareup/codegen/CodeGen.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,24 @@
package com.squareup.codegen;

import com.squareup.injector.internal.Keys;
import java.lang.reflect.Method;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.AnnotationValueVisitor;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.ArrayType;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.PrimitiveType;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.type.TypeVariable;
import javax.lang.model.util.SimpleAnnotationValueVisitor6;
import javax.lang.model.util.SimpleTypeVisitor6;

/**
Expand Down Expand Up @@ -139,6 +147,48 @@ public static void typeToString(final TypeMirror type, final StringBuilder resul
}, null);
}

private static final AnnotationValueVisitor<Object, Void> VALUE_EXTRACTOR
= new SimpleAnnotationValueVisitor6<Object, Void>() {
@Override protected Object defaultAction(Object o, Void v) {
return o;
}
@Override public Object visitArray(List<? extends AnnotationValue> values, Void v) {
Object[] result = new Object[values.size()];
for (int i = 0; i < values.size(); i++) {
result[i] = values.get(i).accept(this, null);
}
return result;
}
};

/**
* Returns the annotation on {@code element} formatted as a Map. This returns
* a Map rather than an instance of the annotation interface to work-around
* the fact that Class and Class[] fields won't work at code generation time.
* See http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5089128
*/
public static Map<String, Object> getAnnotation(Class<?> annotationType, Element element) {
for (AnnotationMirror annotation : element.getAnnotationMirrors()) {
if (!annotation.getAnnotationType().toString().equals(annotationType.getName())) {
continue;
}

Map<String, Object> result = new LinkedHashMap<String, Object>();
for (Method m : annotationType.getMethods()) {
result.put(m.getName(), m.getDefaultValue());
}
for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> e
: annotation.getElementValues().entrySet()) {
String name = e.getKey().getSimpleName().toString();
Object value = e.getValue().accept(VALUE_EXTRACTOR, null);
result.put(name, value);
}
return result;
}

return null; // Annotation not found.
}

static void rawTypeToString(StringBuilder result, TypeElement type,
char innerClassSeparator) {
String packageName = getPackage(type).getQualifiedName().toString();
Expand Down
42 changes: 40 additions & 2 deletions src/main/java/com/squareup/codegen/ProvidesProcessor.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package com.squareup.codegen;

import com.squareup.injector.Module;
import com.squareup.injector.Provides;
import com.squareup.injector.internal.Binding;
import com.squareup.injector.internal.Linker;
Expand Down Expand Up @@ -71,11 +72,15 @@ public final class ProvidesProcessor extends AbstractProcessor {
writeModuleAdapter(module.getKey(), module.getValue());
}
} catch (IOException e) {
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Code gen failed: " + e);
error("Code gen failed: " + e);
}
return true;
}

private void error(String message) {
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, message);
}

/**
* Returns a map containing all {@code @Provides} methods, indexed by class.
*/
Expand Down Expand Up @@ -131,6 +136,16 @@ private Set<? extends Element> providesMethods(RoundEnvironment env) {
*/
private void writeModuleAdapter(TypeElement type, List<ExecutableElement> providerMethods)
throws IOException {
Map<String, Object> module = CodeGen.getAnnotation(Module.class, type);
if (module == null) {
error(type + " has @Provides methods but no @Module annotation");
return;
}

Object[] staticInjections = (Object[]) module.get("staticInjections");
Object[] entryPoints = (Object[]) module.get("entryPoints");
boolean overrides = (Boolean) module.get("overrides");

String adapterName = CodeGen.adapterName(type, "$ModuleAdapter");
JavaFileObject sourceFile = processingEnv.getFiler()
.createSourceFile(adapterName, type);
Expand All @@ -143,9 +158,32 @@ private void writeModuleAdapter(TypeElement type, List<ExecutableElement> provid
writer.addImport(Linker.class);

String typeName = type.getQualifiedName().toString();
writer.beginType(adapterName, "class", PUBLIC | FINAL, null,
writer.beginType(adapterName, "class", PUBLIC | FINAL,
CodeGen.parameterizedType(ModuleAdapter.class, typeName));

StringBuilder entryPointsField = new StringBuilder().append("{ ");
for (Object entryPoint : entryPoints) {
TypeMirror typeMirror = (TypeMirror) entryPoint;
String key = GeneratorKeys.rawMembersKey(typeMirror);
entryPointsField.append(JavaWriter.stringLiteral(key)).append(", ");
}
entryPointsField.append("}");
writer.field("String[]", "ENTRY_POINTS", PRIVATE | STATIC | FINAL,
entryPointsField.toString());

StringBuilder staticInjectionsField = new StringBuilder().append("{ ");
for (Object staticInjection : staticInjections) {
TypeMirror typeMirror = (TypeMirror) staticInjection;
staticInjectionsField.append(CodeGen.typeToString(typeMirror)).append(".class, ");
}
staticInjectionsField.append("}");
writer.field("Class<?>[]", "STATIC_INJECTIONS", PRIVATE | STATIC | FINAL,
staticInjectionsField.toString());

writer.beginMethod(null, adapterName, PUBLIC);
writer.statement("super(ENTRY_POINTS, STATIC_INJECTIONS, %s)", overrides);
writer.endMethod();

writer.annotation(Override.class);
writer.beginMethod("void", "getBindings", PUBLIC, typeName, "module", BINDINGS_MAP, "map");
for (ExecutableElement providerMethod : providerMethods) {
Expand Down
54 changes: 24 additions & 30 deletions src/main/java/com/squareup/injector/ObjectGraph.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@
import com.squareup.injector.internal.Binding;
import com.squareup.injector.internal.Keys;
import com.squareup.injector.internal.Linker;
import com.squareup.injector.internal.ModuleAdapter;
import com.squareup.injector.internal.ProblemDetector;
import com.squareup.injector.internal.StaticInjection;
import java.util.ArrayList;
import com.squareup.injector.internal.UniqueMap;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
Expand Down Expand Up @@ -56,10 +56,10 @@
public final class ObjectGraph {
private final Linker linker;
private final Map<Class<?>, StaticInjection> staticInjections;
private final Map<Class<?>, Class<?>> entryPoints;
private final Map<String, Class<?>> entryPoints;

private ObjectGraph(Linker linker, Map<Class<?>, StaticInjection> staticInjections,
Map<Class<?>, Class<?>> entryPoints) {
Map<String, Class<?>> entryPoints) {
this.linker = linker;
this.staticInjections = staticInjections;
this.entryPoints = entryPoints;
Expand All @@ -82,36 +82,31 @@ public static ObjectGraph getLazy(Object... modules) {
}

private static ObjectGraph get(boolean lazy, Object... modules) {
Map<Class<?>, Class<?>> entryPoints = new LinkedHashMap<Class<?>, Class<?>>();
Map<String, Class<?>> entryPoints = new LinkedHashMap<String, Class<?>>();
Map<Class<?>, StaticInjection> staticInjections
= new LinkedHashMap<Class<?>, StaticInjection>();

List<Object> baseModules = new ArrayList<Object>();
List<Object> overrideModules = new ArrayList<Object>();
// Extract bindings in the 'base' and 'overrides' set. Within each set no
// duplicates are permitted.
Map<String, Binding<?>> baseBindings = new UniqueMap<String, Binding<?>>();
Map<String, Binding<?>> overrideBindings = new UniqueMap<String, Binding<?>>();
for (Object module : modules) {
Class<?> moduleClass = module.getClass();
Module annotation = moduleClass.getAnnotation(Module.class);
if (annotation == null) {
throw new IllegalArgumentException("No @Module on " + moduleClass.getName());
ModuleAdapter<Object> adapter = ModuleAdapter.get(module);
for (String key : adapter.entryPoints) {
entryPoints.put(key, moduleClass);
}
for (Class<?> c : annotation.entryPoints()) {
entryPoints.put(c, moduleClass);
}
for (Class<?> c : annotation.staticInjections()) {
for (Class<?> c : adapter.staticInjections) {
staticInjections.put(c, lazy ? null : StaticInjection.get(c));
}
if (annotation.overrides()) {
overrideModules.add(module);
} else {
baseModules.add(module);
}
Map<String, Binding<?>> addTo = adapter.overrides ? overrideBindings : baseBindings;
adapter.getBindings(module, addTo);
}

// Create a linker and install all of the user's modules. Modules provided
// at runtime may override modules provided in the @Module annotation.
// Create a linker and install all of the user's bindings.
Linker linker = new Linker();
linker.installModules(baseModules);
linker.installModules(overrideModules);
linker.installBindings(baseBindings);
linker.installBindings(overrideBindings);

ObjectGraph result = new ObjectGraph(linker, staticInjections, entryPoints);

Expand All @@ -137,8 +132,8 @@ private void linkStaticInjections() {
}

private void linkEntryPoints() {
for (Map.Entry<Class<?>, Class<?>> entry : entryPoints.entrySet()) {
linker.requestBinding(Keys.getMembersKey(entry.getKey()), entry.getValue());
for (Map.Entry<String, Class<?>> entry : entryPoints.entrySet()) {
linker.requestBinding(entry.getKey(), entry.getValue());
}
}

Expand Down Expand Up @@ -180,13 +175,12 @@ public void injectStatics() {
*/
@SuppressWarnings("unchecked") // the linker matches keys to bindings by their type
public void inject(Object instance) {
Class<?> type = instance.getClass();
Class<?> moduleClass = entryPoints.get(type);
String key = Keys.getMembersKey(instance.getClass());
Class<?> moduleClass = entryPoints.get(key);
if (moduleClass == null) {
throw new IllegalArgumentException("No entry point for " + type.getName() + ". "
+ "You must explicitly add an entry point to one of your modules.");
throw new IllegalArgumentException("No entry point for " + instance.getClass().getName()
+ ". You must explicitly add an entry point to one of your modules.");
}
String key = Keys.getMembersKey(type);
Binding<?> binding = linker.requestBinding(key, moduleClass);
if (binding == null || !binding.linked) {
linker.linkRequested();
Expand Down
Loading

0 comments on commit a081fb0

Please sign in to comment.