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