• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Dagger Authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 // This must be in the dagger.internal.codegen package since Dagger doesn't expose its APIs publicly
18 // https://github.com/google/dagger/issues/773 could present an opportunity to put this somewhere in
19 // the regular kythe/java tree.
20 package dagger.internal.codegen.kythe;
21 
22 import static androidx.room.compiler.processing.compat.XConverters.toJavac;
23 import static androidx.room.compiler.processing.compat.XConverters.toXProcessing;
24 
25 import androidx.room.compiler.processing.XElement;
26 import androidx.room.compiler.processing.XProcessingEnv;
27 import androidx.room.compiler.processing.XTypeElement;
28 import com.google.auto.service.AutoService;
29 import com.google.common.collect.Iterables;
30 import com.google.devtools.kythe.analyzers.base.EntrySet;
31 import com.google.devtools.kythe.analyzers.base.FactEmitter;
32 import com.google.devtools.kythe.analyzers.base.KytheEntrySets;
33 import com.google.devtools.kythe.analyzers.java.Plugin;
34 import com.google.devtools.kythe.proto.Storage.VName;
35 import com.sun.tools.javac.code.Symbol;
36 import com.sun.tools.javac.tree.JCTree.JCClassDecl;
37 import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
38 import dagger.Component;
39 import dagger.internal.codegen.binding.Binding;
40 import dagger.internal.codegen.binding.BindingDeclaration;
41 import dagger.internal.codegen.binding.BindingGraphFactory;
42 import dagger.internal.codegen.binding.BindingNode;
43 import dagger.internal.codegen.binding.ComponentDescriptor;
44 import dagger.internal.codegen.binding.ModuleDescriptor;
45 import dagger.internal.codegen.javac.JavacPluginModule;
46 import dagger.internal.codegen.javapoet.TypeNames;
47 import dagger.internal.codegen.model.BindingGraph;
48 import dagger.internal.codegen.model.BindingGraph.DependencyEdge;
49 import dagger.internal.codegen.model.BindingGraph.Edge;
50 import dagger.internal.codegen.model.BindingGraph.Node;
51 import dagger.internal.codegen.model.DependencyRequest;
52 import dagger.internal.codegen.validation.InjectBindingRegistryModule;
53 import java.util.Optional;
54 import java.util.logging.Logger;
55 import javax.inject.Inject;
56 import javax.inject.Singleton;
57 
58 /**
59  * A plugin which emits nodes and edges for <a href="https://github.com/google/dagger">Dagger</a>
60  * specific code.
61  */
62 @AutoService(Plugin.class)
63 public class DaggerKythePlugin extends Plugin.Scanner<Void, Void> {
64   // TODO(ronshapiro): use flogger
65   private static final Logger logger = Logger.getLogger(DaggerKythePlugin.class.getCanonicalName());
66   private FactEmitter emitter;
67   @Inject ComponentDescriptor.Factory componentDescriptorFactory;
68   @Inject BindingGraphFactory bindingGraphFactory;
69   @Inject XProcessingEnv xProcessingEnv;
70 
71   @Override
visitClassDef(JCClassDecl tree, Void p)72   public Void visitClassDef(JCClassDecl tree, Void p) {
73     if (tree.sym != null) {
74       XTypeElement type = toXProcessing(tree.sym, xProcessingEnv);
75       if (type.hasAnyAnnotation(TypeNames.COMPONENT, TypeNames.PRODUCTION_COMPONENT)) {
76         addNodesForGraph(
77             bindingGraphFactory.create(
78                 componentDescriptorFactory.rootComponentDescriptor(type), false));
79       }
80     }
81     return super.visitClassDef(tree, p);
82   }
83 
addNodesForGraph(dagger.internal.codegen.binding.BindingGraph graph)84   private void addNodesForGraph(dagger.internal.codegen.binding.BindingGraph graph) {
85     addDependencyEdges(graph.topLevelBindingGraph());
86 
87     // TODO(bcorso): Convert these to use the new BindingGraph
88     addModuleEdges(graph);
89     addChildComponentEdges(graph);
90   }
91 
addDependencyEdges(BindingGraph graph)92   private void addDependencyEdges(BindingGraph graph) {
93     for (DependencyEdge dependencyEdge : graph.dependencyEdges()) {
94       DependencyRequest dependency = dependencyEdge.dependencyRequest();
95       Node node = graph.network().incidentNodes(dependencyEdge).target();
96       addEdgesForDependencyRequest(dependency, (BindingNode) node, graph);
97     }
98   }
99 
100   /**
101    * Add {@code /inject/satisfiedby} edges from {@code dependency}'s {@link
102    * DependencyRequest#requestElement()} to any {@link BindingDeclaration#bindingElement() binding
103    * elements} that satisfy the request.
104    *
105    * <p>This collapses requests for synthetic bindings so that a request for a multibound key
106    * points to all of the contributions for the multibound object. It does so by recursively calling
107    * this method, with each dependency's key as the {@code targetKey}.
108    */
addEdgesForDependencyRequest( DependencyRequest dependency, BindingNode bindingNode, BindingGraph graph)109   private void addEdgesForDependencyRequest(
110       DependencyRequest dependency, BindingNode bindingNode, BindingGraph graph) {
111     if (!dependency.requestElement().isPresent()) {
112       return;
113     }
114     Binding binding = bindingNode.delegate();
115     if (binding.bindingElement().isPresent()) {
116       addDependencyEdge(dependency, binding);
117     } else {
118       for (Edge outEdge : graph.network().outEdges(bindingNode)) {
119         if (outEdge instanceof DependencyEdge) {
120           Node outNode = graph.network().incidentNodes(outEdge).target();
121           addEdgesForDependencyRequest(dependency, (BindingNode) outNode, graph);
122         }
123       }
124     }
125     for (BindingDeclaration bindingDeclaration :
126         Iterables.concat(
127             bindingNode.multibindingDeclarations(),
128             bindingNode.optionalBindingDeclarations())) {
129       addDependencyEdge(dependency, bindingDeclaration);
130     }
131   }
132 
addDependencyEdge( DependencyRequest dependency, BindingDeclaration bindingDeclaration)133   private void addDependencyEdge(
134       DependencyRequest dependency, BindingDeclaration bindingDeclaration) {
135     XElement requestElement = dependency.requestElement().get().xprocessing();
136     XElement bindingElement = bindingDeclaration.bindingElement().get();
137     Optional<VName> requestElementNode = jvmNode(requestElement, "request element");
138     Optional<VName> bindingElementNode = jvmNode(bindingElement, "binding element");
139     emitEdge(requestElementNode, "/inject/satisfiedby", bindingElementNode);
140     // TODO(ronshapiro): emit facts about the component that satisfies the edge
141   }
142 
addModuleEdges(dagger.internal.codegen.binding.BindingGraph graph)143   private void addModuleEdges(dagger.internal.codegen.binding.BindingGraph graph) {
144     Optional<VName> componentNode = jvmNode(graph.componentTypeElement(), "component");
145     for (ModuleDescriptor module : graph.componentDescriptor().modules()) {
146       Optional<VName> moduleNode = jvmNode(module.moduleElement(), "module");
147       emitEdge(componentNode, "/inject/installsmodule", moduleNode);
148     }
149     graph.subgraphs().forEach(this::addModuleEdges);
150   }
151 
addChildComponentEdges(dagger.internal.codegen.binding.BindingGraph graph)152   private void addChildComponentEdges(dagger.internal.codegen.binding.BindingGraph graph) {
153     Optional<VName> componentNode = jvmNode(graph.componentTypeElement(), "component");
154     for (dagger.internal.codegen.binding.BindingGraph subgraph : graph.subgraphs()) {
155       Optional<VName> subcomponentNode =
156           jvmNode(subgraph.componentTypeElement(), "child component");
157       emitEdge(componentNode, "/inject/childcomponent", subcomponentNode);
158     }
159     graph.subgraphs().forEach(this::addChildComponentEdges);
160   }
161 
jvmNode(XElement element, String name)162   private Optional<VName> jvmNode(XElement element, String name) {
163     Optional<VName> jvmNode =
164         kytheGraph.getJvmNode((Symbol) toJavac(element)).map(KytheNode::getVName);
165     if (!jvmNode.isPresent()) {
166       logger.warning(String.format("Missing JVM node for %s: %s", name, element));
167     }
168     return jvmNode;
169   }
170 
emitEdge(Optional<VName> source, String edgeName, Optional<VName> target)171   private void emitEdge(Optional<VName> source, String edgeName, Optional<VName> target) {
172     source.ifPresent(
173         s -> target.ifPresent(t -> new EntrySet.Builder(s, edgeName, t).build().emit(emitter)));
174   }
175 
176   @Override
run( JCCompilationUnit compilationUnit, KytheEntrySets entrySets, KytheGraph kytheGraph)177   public void run(
178       JCCompilationUnit compilationUnit, KytheEntrySets entrySets, KytheGraph kytheGraph) {
179     if (bindingGraphFactory == null) {
180       emitter = entrySets.getEmitter();
181       DaggerDaggerKythePlugin_PluginComponent.builder()
182           .javacPluginModule(new JavacPluginModule(kytheGraph.getJavaContext()))
183           .build()
184           .inject(this);
185     }
186     super.run(compilationUnit, entrySets, kytheGraph);
187   }
188 
189   @Singleton
190   @Component(modules = {InjectBindingRegistryModule.class, JavacPluginModule.class})
191   interface PluginComponent {
inject(DaggerKythePlugin plugin)192     void inject(DaggerKythePlugin plugin);
193   }
194 }
195