• 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 dagger.internal.codegen.langmodel.DaggerElements.isAnyAnnotationPresent;
23 
24 import com.google.auto.service.AutoService;
25 import com.google.common.collect.Iterables;
26 import com.google.devtools.kythe.analyzers.base.EntrySet;
27 import com.google.devtools.kythe.analyzers.base.FactEmitter;
28 import com.google.devtools.kythe.analyzers.base.KytheEntrySets;
29 import com.google.devtools.kythe.analyzers.java.Plugin;
30 import com.google.devtools.kythe.proto.Storage.VName;
31 import com.sun.tools.javac.code.Symbol;
32 import com.sun.tools.javac.tree.JCTree.JCClassDecl;
33 import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
34 import com.sun.tools.javac.util.Context;
35 import dagger.BindsInstance;
36 import dagger.Component;
37 import dagger.internal.codegen.binding.Binding;
38 import dagger.internal.codegen.binding.BindingDeclaration;
39 import dagger.internal.codegen.binding.BindingGraphFactory;
40 import dagger.internal.codegen.binding.BindingNode;
41 import dagger.internal.codegen.binding.ComponentDescriptorFactory;
42 import dagger.internal.codegen.binding.ModuleDescriptor;
43 import dagger.internal.codegen.javac.JavacPluginModule;
44 import dagger.internal.codegen.validation.InjectBindingRegistryModule;
45 import dagger.model.BindingGraph;
46 import dagger.model.BindingGraph.DependencyEdge;
47 import dagger.model.BindingGraph.Edge;
48 import dagger.model.BindingGraph.Node;
49 import dagger.model.DependencyRequest;
50 import dagger.producers.ProductionComponent;
51 import java.util.Optional;
52 import java.util.logging.Logger;
53 import javax.inject.Inject;
54 import javax.inject.Singleton;
55 import javax.lang.model.element.Element;
56 
57 /**
58  * A plugin which emits nodes and edges for <a href="https://github.com/google/dagger">Dagger</a>
59  * specific code.
60  */
61 @AutoService(Plugin.class)
62 public class DaggerKythePlugin extends Plugin.Scanner<Void, Void> {
63   // TODO(ronshapiro): use flogger
64   private static final Logger logger = Logger.getLogger(DaggerKythePlugin.class.getCanonicalName());
65   private FactEmitter emitter;
66   @Inject ComponentDescriptorFactory componentDescriptorFactory;
67   @Inject BindingGraphFactory bindingGraphFactory;
68 
69   @Override
visitClassDef(JCClassDecl tree, Void p)70   public Void visitClassDef(JCClassDecl tree, Void p) {
71     if (tree.sym != null
72         && isAnyAnnotationPresent(tree.sym, Component.class, ProductionComponent.class)) {
73       addNodesForGraph(
74           bindingGraphFactory.create(
75               componentDescriptorFactory.rootComponentDescriptor(tree.sym), false));
76     }
77     return super.visitClassDef(tree, p);
78   }
79 
addNodesForGraph(dagger.internal.codegen.binding.BindingGraph graph)80   private void addNodesForGraph(dagger.internal.codegen.binding.BindingGraph graph) {
81     addDependencyEdges(graph.topLevelBindingGraph());
82 
83     // TODO(bcorso): Convert these to use the new BindingGraph
84     addModuleEdges(graph);
85     addChildComponentEdges(graph);
86   }
87 
addDependencyEdges(BindingGraph graph)88   private void addDependencyEdges(BindingGraph graph) {
89     for (DependencyEdge dependencyEdge : graph.dependencyEdges()) {
90       DependencyRequest dependency = dependencyEdge.dependencyRequest();
91       Node node = graph.network().incidentNodes(dependencyEdge).target();
92       addEdgesForDependencyRequest(dependency, (BindingNode) node, graph);
93     }
94   }
95 
96   /**
97    * Add {@code /inject/satisfiedby} edges from {@code dependency}'s {@link
98    * DependencyRequest#requestElement()} to any {@link BindingDeclaration#bindingElement() binding
99    * elements} that satisfy the request.
100    *
101    * <p>This collapses requests for synthetic bindings so that a request for a multibound key
102    * points to all of the contributions for the multibound object. It does so by recursively calling
103    * this method, with each dependency's key as the {@code targetKey}.
104    */
addEdgesForDependencyRequest( DependencyRequest dependency, BindingNode bindingNode, BindingGraph graph)105   private void addEdgesForDependencyRequest(
106       DependencyRequest dependency, BindingNode bindingNode, BindingGraph graph) {
107     if (!dependency.requestElement().isPresent()) {
108       return;
109     }
110     Binding binding = bindingNode.delegate();
111     if (binding.bindingElement().isPresent()) {
112       addDependencyEdge(dependency, binding);
113     } else {
114       for (Edge outEdge : graph.network().outEdges(bindingNode)) {
115         if (outEdge instanceof DependencyEdge) {
116           Node outNode = graph.network().incidentNodes(outEdge).target();
117           addEdgesForDependencyRequest(dependency, (BindingNode) outNode, graph);
118         }
119       }
120     }
121     for (BindingDeclaration bindingDeclaration :
122         Iterables.concat(
123             bindingNode.multibindingDeclarations(),
124             bindingNode.optionalBindingDeclarations())) {
125       addDependencyEdge(dependency, bindingDeclaration);
126     }
127   }
128 
addDependencyEdge( DependencyRequest dependency, BindingDeclaration bindingDeclaration)129   private void addDependencyEdge(
130       DependencyRequest dependency, BindingDeclaration bindingDeclaration) {
131     Element requestElement = dependency.requestElement().get();
132     Element bindingElement = bindingDeclaration.bindingElement().get();
133     Optional<VName> requestElementNode = jvmNode(requestElement, "request element");
134     Optional<VName> bindingElementNode = jvmNode(bindingElement, "binding element");
135     emitEdge(requestElementNode, "/inject/satisfiedby", bindingElementNode);
136     // TODO(ronshapiro): emit facts about the component that satisfies the edge
137   }
138 
addModuleEdges(dagger.internal.codegen.binding.BindingGraph graph)139   private void addModuleEdges(dagger.internal.codegen.binding.BindingGraph graph) {
140     Optional<VName> componentNode = jvmNode(graph.componentTypeElement(), "component");
141     for (ModuleDescriptor module : graph.componentDescriptor().modules()) {
142       Optional<VName> moduleNode = jvmNode(module.moduleElement(), "module");
143       emitEdge(componentNode, "/inject/installsmodule", moduleNode);
144     }
145     graph.subgraphs().forEach(this::addModuleEdges);
146   }
147 
addChildComponentEdges(dagger.internal.codegen.binding.BindingGraph graph)148   private void addChildComponentEdges(dagger.internal.codegen.binding.BindingGraph graph) {
149     Optional<VName> componentNode = jvmNode(graph.componentTypeElement(), "component");
150     for (dagger.internal.codegen.binding.BindingGraph subgraph : graph.subgraphs()) {
151       Optional<VName> subcomponentNode =
152           jvmNode(subgraph.componentTypeElement(), "child component");
153       emitEdge(componentNode, "/inject/childcomponent", subcomponentNode);
154     }
155     graph.subgraphs().forEach(this::addChildComponentEdges);
156   }
157 
jvmNode(Element element, String name)158   private Optional<VName> jvmNode(Element element, String name) {
159     Optional<VName> jvmNode = kytheGraph.getJvmNode((Symbol) element).map(KytheNode::getVName);
160     if (!jvmNode.isPresent()) {
161       logger.warning(String.format("Missing JVM node for %s: %s", name, element));
162     }
163     return jvmNode;
164   }
165 
emitEdge(Optional<VName> source, String edgeName, Optional<VName> target)166   private void emitEdge(Optional<VName> source, String edgeName, Optional<VName> target) {
167     source.ifPresent(
168         s -> target.ifPresent(t -> new EntrySet.Builder(s, edgeName, t).build().emit(emitter)));
169   }
170 
171   @Override
run( JCCompilationUnit compilationUnit, KytheEntrySets entrySets, KytheGraph kytheGraph)172   public void run(
173       JCCompilationUnit compilationUnit, KytheEntrySets entrySets, KytheGraph kytheGraph) {
174     if (bindingGraphFactory == null) {
175       emitter = entrySets.getEmitter();
176       DaggerDaggerKythePlugin_PluginComponent.builder()
177           .context(kytheGraph.getJavaContext())
178           .build()
179           .inject(this);
180     }
181     super.run(compilationUnit, entrySets, kytheGraph);
182   }
183 
184   @Singleton
185   @Component(modules = {InjectBindingRegistryModule.class, JavacPluginModule.class})
186   interface PluginComponent {
inject(DaggerKythePlugin plugin)187     void inject(DaggerKythePlugin plugin);
188 
189     @Component.Builder
190     interface Builder {
191       @BindsInstance
context(Context context)192       Builder context(Context context);
193 
build()194       PluginComponent build();
195     }
196   }
197 }
198