/* * Copyright (C) 2023 The Dagger Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package dagger.internal.codegen.validation; import static androidx.room.compiler.processing.compat.XConverters.getProcessingEnv; import static androidx.room.compiler.processing.compat.XConverters.toJavac; import static androidx.room.compiler.processing.compat.XConverters.toKS; import static androidx.room.compiler.processing.compat.XConverters.toKSResolver; import static com.google.common.base.Preconditions.checkState; import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList; import static dagger.internal.codegen.extension.DaggerStreams.toImmutableMap; import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet; import androidx.room.compiler.processing.XAnnotation; import androidx.room.compiler.processing.XElement; import androidx.room.compiler.processing.XExecutableElement; import androidx.room.compiler.processing.XProcessingEnv; import androidx.room.compiler.processing.XType; import androidx.room.compiler.processing.XTypeElement; import com.google.auto.value.AutoValue; import com.google.auto.value.extension.memoized.Memoized; import com.google.common.base.Equivalence; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSetMultimap; import com.google.common.graph.EndpointPair; import com.google.common.graph.ImmutableNetwork; import com.google.common.graph.MutableNetwork; import com.google.common.graph.Network; import com.google.common.graph.NetworkBuilder; import com.google.devtools.ksp.processing.Resolver; import com.google.devtools.ksp.processing.SymbolProcessorEnvironment; import com.google.devtools.ksp.symbol.KSAnnotated; import com.google.devtools.ksp.symbol.KSAnnotation; import com.google.devtools.ksp.symbol.KSClassDeclaration; import com.google.devtools.ksp.symbol.KSFunctionDeclaration; import com.google.devtools.ksp.symbol.KSType; import dagger.internal.codegen.xprocessing.XAnnotations; import dagger.internal.codegen.xprocessing.XElements; import dagger.internal.codegen.xprocessing.XTypes; import dagger.spi.model.Binding; import dagger.spi.model.BindingGraph; import dagger.spi.model.BindingGraph.ChildFactoryMethodEdge; import dagger.spi.model.BindingGraph.ComponentNode; import dagger.spi.model.BindingGraph.DependencyEdge; import dagger.spi.model.BindingGraph.Edge; import dagger.spi.model.BindingGraph.MaybeBinding; import dagger.spi.model.BindingGraph.MissingBinding; import dagger.spi.model.BindingGraph.Node; import dagger.spi.model.BindingGraph.SubcomponentCreatorBindingEdge; import dagger.spi.model.BindingKind; import dagger.spi.model.ComponentPath; import dagger.spi.model.DaggerAnnotation; import dagger.spi.model.DaggerElement; import dagger.spi.model.DaggerExecutableElement; import dagger.spi.model.DaggerProcessingEnv; import dagger.spi.model.DaggerProcessingEnv.Backend; import dagger.spi.model.DaggerType; import dagger.spi.model.DaggerTypeElement; import dagger.spi.model.DependencyRequest; import dagger.spi.model.DiagnosticReporter; import dagger.spi.model.Key; import dagger.spi.model.RequestKind; import dagger.spi.model.Scope; import java.util.Optional; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; import javax.lang.model.type.TypeMirror; import javax.tools.Diagnostic; /** A Utility class for converting to the {@link BindingGraph} used by external plugins. */ public final class SpiModelBindingGraphConverter { private SpiModelBindingGraphConverter() {} public static DiagnosticReporter toSpiModel( dagger.internal.codegen.model.DiagnosticReporter reporter) { return DiagnosticReporterImpl.create(reporter); } public static BindingGraph toSpiModel( dagger.internal.codegen.model.BindingGraph graph, XProcessingEnv env) { return BindingGraphImpl.create(graph, env); } private static ImmutableNetwork toSpiModel( Network< dagger.internal.codegen.model.BindingGraph.Node, dagger.internal.codegen.model.BindingGraph.Edge> internalNetwork, XProcessingEnv env) { MutableNetwork network = NetworkBuilder.directed().allowsParallelEdges(true).allowsSelfLoops(true).build(); ImmutableMap fromInternalNodes = internalNetwork.nodes().stream() .collect( toImmutableMap( node -> node, node -> SpiModelBindingGraphConverter.toSpiModel(node, env))); for (Node node : fromInternalNodes.values()) { network.addNode(node); } for (dagger.internal.codegen.model.BindingGraph.Edge edge : internalNetwork.edges()) { EndpointPair edgePair = internalNetwork.incidentNodes(edge); network.addEdge( fromInternalNodes.get(edgePair.source()), fromInternalNodes.get(edgePair.target()), toSpiModel(edge, env)); } return ImmutableNetwork.copyOf(network); } private static Node toSpiModel( dagger.internal.codegen.model.BindingGraph.Node node, XProcessingEnv env) { if (node instanceof dagger.internal.codegen.model.Binding) { return BindingNodeImpl.create((dagger.internal.codegen.model.Binding) node, env); } else if (node instanceof dagger.internal.codegen.model.BindingGraph.ComponentNode) { return ComponentNodeImpl.create( (dagger.internal.codegen.model.BindingGraph.ComponentNode) node, env); } else if (node instanceof dagger.internal.codegen.model.BindingGraph.MissingBinding) { return MissingBindingImpl.create( (dagger.internal.codegen.model.BindingGraph.MissingBinding) node, env); } else { throw new IllegalStateException("Unhandled node type: " + node.getClass()); } } private static Edge toSpiModel( dagger.internal.codegen.model.BindingGraph.Edge edge, XProcessingEnv env) { if (edge instanceof dagger.internal.codegen.model.BindingGraph.DependencyEdge) { return DependencyEdgeImpl.create( (dagger.internal.codegen.model.BindingGraph.DependencyEdge) edge, env); } else if (edge instanceof dagger.internal.codegen.model.BindingGraph.ChildFactoryMethodEdge) { return ChildFactoryMethodEdgeImpl.create( (dagger.internal.codegen.model.BindingGraph.ChildFactoryMethodEdge) edge, env); } else if (edge instanceof dagger.internal.codegen.model.BindingGraph.SubcomponentCreatorBindingEdge) { return SubcomponentCreatorBindingEdgeImpl.create( (dagger.internal.codegen.model.BindingGraph.SubcomponentCreatorBindingEdge) edge, env); } else { throw new IllegalStateException("Unhandled edge type: " + edge.getClass()); } } private static Key toSpiModel(dagger.internal.codegen.model.Key key) { Key.Builder builder = Key.builder(toSpiModel(key.type().xprocessing())) .qualifier(key.qualifier().map(qualifier -> toSpiModel(qualifier.xprocessing()))); if (key.multibindingContributionIdentifier().isPresent()) { return builder .multibindingContributionIdentifier( toSpiModel( key.multibindingContributionIdentifier() .get() .contributingModule() .xprocessing()), toSpiModel( key.multibindingContributionIdentifier().get().bindingMethod().xprocessing())) .build(); } return builder.build().withoutMultibindingContributionIdentifier(); } private static BindingKind toSpiModel(dagger.internal.codegen.model.BindingKind bindingKind) { return BindingKind.valueOf(bindingKind.name()); } private static RequestKind toSpiModel(dagger.internal.codegen.model.RequestKind requestKind) { return RequestKind.valueOf(requestKind.name()); } @SuppressWarnings("CheckReturnValue") private static DependencyRequest toSpiModel( dagger.internal.codegen.model.DependencyRequest request) { DependencyRequest.Builder builder = DependencyRequest.builder() .kind(toSpiModel(request.kind())) .key(toSpiModel(request.key())) .isNullable(request.isNullable()); request.requestElement().ifPresent(e -> builder.requestElement(toSpiModel(e.xprocessing()))); return builder.build(); } private static Scope toSpiModel(dagger.internal.codegen.model.Scope scope) { return Scope.scope(toSpiModel(scope.scopeAnnotation().xprocessing())); } private static ComponentPath toSpiModel(dagger.internal.codegen.model.ComponentPath path) { return ComponentPath.create( path.components().stream() .map(component -> toSpiModel(component.xprocessing())) .collect(toImmutableList())); } private static DaggerTypeElement toSpiModel(XTypeElement typeElement) { return DaggerTypeElementImpl.from(typeElement); } private static DaggerType toSpiModel(XType type) { return DaggerTypeImpl.from(type); } static DaggerAnnotation toSpiModel(XAnnotation annotation) { return DaggerAnnotationImpl.from(annotation); } private static DaggerElement toSpiModel(XElement element) { return DaggerElementImpl.from(element); } private static DaggerExecutableElement toSpiModel(XExecutableElement executableElement) { return DaggerExecutableElementImpl.from(executableElement); } static DaggerProcessingEnv toSpiModel(XProcessingEnv env) { return DaggerProcessingEnvImpl.from(env); } private static dagger.internal.codegen.model.BindingGraph.ComponentNode toInternal( ComponentNode componentNode) { return ((ComponentNodeImpl) componentNode).internalDelegate(); } private static dagger.internal.codegen.model.BindingGraph.MaybeBinding toInternal( MaybeBinding maybeBinding) { if (maybeBinding instanceof MissingBindingImpl) { return ((MissingBindingImpl) maybeBinding).internalDelegate(); } else if (maybeBinding instanceof BindingNodeImpl) { return ((BindingNodeImpl) maybeBinding).internalDelegate(); } else { throw new IllegalStateException("Unhandled binding type: " + maybeBinding.getClass()); } } private static dagger.internal.codegen.model.BindingGraph.DependencyEdge toInternal( DependencyEdge dependencyEdge) { return ((DependencyEdgeImpl) dependencyEdge).internalDelegate(); } private static dagger.internal.codegen.model.BindingGraph.ChildFactoryMethodEdge toInternal( ChildFactoryMethodEdge childFactoryMethodEdge) { return ((ChildFactoryMethodEdgeImpl) childFactoryMethodEdge).internalDelegate(); } @AutoValue abstract static class ComponentNodeImpl implements ComponentNode { static ComponentNode create( dagger.internal.codegen.model.BindingGraph.ComponentNode componentNode, XProcessingEnv env) { return new AutoValue_SpiModelBindingGraphConverter_ComponentNodeImpl( toSpiModel(componentNode.componentPath()), componentNode.isSubcomponent(), componentNode.isRealComponent(), componentNode.entryPoints().stream() .map(SpiModelBindingGraphConverter::toSpiModel) .collect(toImmutableSet()), componentNode.scopes().stream() .map(SpiModelBindingGraphConverter::toSpiModel) .collect(toImmutableSet()), componentNode); } abstract dagger.internal.codegen.model.BindingGraph.ComponentNode internalDelegate(); @Override public final String toString() { return internalDelegate().toString(); } } @AutoValue abstract static class BindingNodeImpl implements Binding { static Binding create(dagger.internal.codegen.model.Binding binding, XProcessingEnv env) { return new AutoValue_SpiModelBindingGraphConverter_BindingNodeImpl( toSpiModel(binding.key()), toSpiModel(binding.componentPath()), binding.dependencies().stream() .map(SpiModelBindingGraphConverter::toSpiModel) .collect(toImmutableSet()), binding.bindingElement().map(element -> toSpiModel(element.xprocessing())), binding.contributingModule().map(module -> toSpiModel(module.xprocessing())), binding.requiresModuleInstance(), binding.scope().map(SpiModelBindingGraphConverter::toSpiModel), binding.isNullable(), binding.isProduction(), toSpiModel(binding.kind()), binding); } abstract dagger.internal.codegen.model.Binding internalDelegate(); @Override public final String toString() { return internalDelegate().toString(); } } @AutoValue abstract static class MissingBindingImpl extends MissingBinding { static MissingBinding create( dagger.internal.codegen.model.BindingGraph.MissingBinding missingBinding, XProcessingEnv env) { return new AutoValue_SpiModelBindingGraphConverter_MissingBindingImpl( toSpiModel(missingBinding.componentPath()), toSpiModel(missingBinding.key()), missingBinding); } abstract dagger.internal.codegen.model.BindingGraph.MissingBinding internalDelegate(); @Memoized @Override public abstract int hashCode(); @Override public abstract boolean equals(Object o); } @AutoValue abstract static class DependencyEdgeImpl implements DependencyEdge { static DependencyEdge create( dagger.internal.codegen.model.BindingGraph.DependencyEdge dependencyEdge, XProcessingEnv env) { return new AutoValue_SpiModelBindingGraphConverter_DependencyEdgeImpl( toSpiModel(dependencyEdge.dependencyRequest()), dependencyEdge.isEntryPoint(), dependencyEdge); } abstract dagger.internal.codegen.model.BindingGraph.DependencyEdge internalDelegate(); @Override public final String toString() { return internalDelegate().toString(); } } @AutoValue abstract static class ChildFactoryMethodEdgeImpl implements ChildFactoryMethodEdge { static ChildFactoryMethodEdge create( dagger.internal.codegen.model.BindingGraph.ChildFactoryMethodEdge childFactoryMethodEdge, XProcessingEnv env) { return new AutoValue_SpiModelBindingGraphConverter_ChildFactoryMethodEdgeImpl( toSpiModel(childFactoryMethodEdge.factoryMethod().xprocessing()), childFactoryMethodEdge); } abstract dagger.internal.codegen.model.BindingGraph.ChildFactoryMethodEdge internalDelegate(); @Override public final String toString() { return internalDelegate().toString(); } } @AutoValue abstract static class SubcomponentCreatorBindingEdgeImpl implements SubcomponentCreatorBindingEdge { static SubcomponentCreatorBindingEdge create( dagger.internal.codegen.model.BindingGraph.SubcomponentCreatorBindingEdge subcomponentCreatorBindingEdge, XProcessingEnv env) { return new AutoValue_SpiModelBindingGraphConverter_SubcomponentCreatorBindingEdgeImpl( subcomponentCreatorBindingEdge.declaringModules().stream() .map(module -> toSpiModel(module.xprocessing())) .collect(toImmutableSet()), subcomponentCreatorBindingEdge); } abstract dagger.internal.codegen.model.BindingGraph.SubcomponentCreatorBindingEdge internalDelegate(); @Override public final String toString() { return internalDelegate().toString(); } } @AutoValue abstract static class BindingGraphImpl extends BindingGraph { static BindingGraph create( dagger.internal.codegen.model.BindingGraph bindingGraph, XProcessingEnv env) { BindingGraphImpl bindingGraphImpl = new AutoValue_SpiModelBindingGraphConverter_BindingGraphImpl( toSpiModel(bindingGraph.network(), env), bindingGraph.isFullBindingGraph(), Backend.valueOf(env.getBackend().name())); bindingGraphImpl.componentNodesByPath = bindingGraphImpl.componentNodes().stream() .collect(toImmutableMap(ComponentNode::componentPath, node -> node)); return bindingGraphImpl; } private ImmutableMap componentNodesByPath; // This overrides dagger.model.BindingGraph with a more efficient implementation. @Override public Optional componentNode(ComponentPath componentPath) { return componentNodesByPath.containsKey(componentPath) ? Optional.of(componentNodesByPath.get(componentPath)) : Optional.empty(); } // This overrides dagger.model.BindingGraph to memoize the output. @Override @Memoized public ImmutableSetMultimap, ? extends Node> nodesByClass() { return super.nodesByClass(); } } @AutoValue abstract static class DaggerElementImpl extends DaggerElement { public static DaggerElement from(XElement element) { return new AutoValue_SpiModelBindingGraphConverter_DaggerElementImpl(element); } abstract XElement element(); @Override public Element javac() { checkIsJavac(backend()); return toJavac(element()); } @Override public KSAnnotated ksp() { checkIsKsp(backend()); return toKS(element()); } @Override public DaggerProcessingEnv.Backend backend() { return getBackend(getProcessingEnv(element())); } @Override public final String toString() { return XElements.toStableString(element()); } } @AutoValue abstract static class DaggerTypeElementImpl extends DaggerTypeElement { public static DaggerTypeElement from(XTypeElement element) { return new AutoValue_SpiModelBindingGraphConverter_DaggerTypeElementImpl(element); } abstract XTypeElement element(); @Override public TypeElement javac() { checkIsJavac(backend()); return toJavac(element()); } @Override public KSClassDeclaration ksp() { checkIsKsp(backend()); return toKS(element()); } @Override public DaggerProcessingEnv.Backend backend() { return getBackend(getProcessingEnv(element())); } @Override public final String toString() { return XElements.toStableString(element()); } } @AutoValue abstract static class DaggerTypeImpl extends DaggerType { public static DaggerType from(XType type) { return new AutoValue_SpiModelBindingGraphConverter_DaggerTypeImpl( XTypes.equivalence().wrap(type)); } abstract Equivalence.Wrapper type(); @Override public TypeMirror javac() { checkIsJavac(backend()); return toJavac(type().get()); } @Override public KSType ksp() { checkIsKsp(backend()); return toKS(type().get()); } @Override public DaggerProcessingEnv.Backend backend() { return getBackend(getProcessingEnv(type().get())); } @Override public final String toString() { return XTypes.toStableString(type().get()); } } @AutoValue abstract static class DaggerAnnotationImpl extends DaggerAnnotation { public static DaggerAnnotation from(XAnnotation annotation) { return new AutoValue_SpiModelBindingGraphConverter_DaggerAnnotationImpl( XAnnotations.equivalence().wrap(annotation)); } abstract Equivalence.Wrapper annotation(); @Override public DaggerTypeElement annotationTypeElement() { return DaggerTypeElementImpl.from(annotation().get().getTypeElement()); } @Override public AnnotationMirror javac() { checkIsJavac(backend()); return toJavac(annotation().get()); } @Override public KSAnnotation ksp() { checkIsKsp(backend()); return toKS(annotation().get()); } @Override public DaggerProcessingEnv.Backend backend() { return getBackend(getProcessingEnv(annotation().get())); } @Override public final String toString() { return XAnnotations.toStableString(annotation().get()); } } @AutoValue abstract static class DaggerExecutableElementImpl extends DaggerExecutableElement { public static DaggerExecutableElement from(XExecutableElement executableElement) { return new AutoValue_SpiModelBindingGraphConverter_DaggerExecutableElementImpl( executableElement); } abstract XExecutableElement executableElement(); @Override public ExecutableElement javac() { checkIsJavac(backend()); return toJavac(executableElement()); } @Override public KSFunctionDeclaration ksp() { checkIsKsp(backend()); return toKS(executableElement()); } @Override public DaggerProcessingEnv.Backend backend() { return getBackend(getProcessingEnv(executableElement())); } @Override public final String toString() { return XElements.toStableString(executableElement()); } } private static class DaggerProcessingEnvImpl extends DaggerProcessingEnv { private final XProcessingEnv env; public static DaggerProcessingEnv from(XProcessingEnv env) { return new DaggerProcessingEnvImpl(env); } DaggerProcessingEnvImpl(XProcessingEnv env) { this.env = env; } @Override public ProcessingEnvironment javac() { checkIsJavac(backend()); return toJavac(env); } @Override public SymbolProcessorEnvironment ksp() { checkIsKsp(backend()); return toKS(env); } @Override public Resolver resolver() { return toKSResolver(env); } @Override public DaggerProcessingEnv.Backend backend() { return getBackend(env); } } private static void checkIsJavac(DaggerProcessingEnv.Backend backend) { checkState( backend == DaggerProcessingEnv.Backend.JAVAC, "Expected JAVAC backend but was: %s", backend); } private static void checkIsKsp(DaggerProcessingEnv.Backend backend) { checkState( backend == DaggerProcessingEnv.Backend.KSP, "Expected KSP backend but was: %s", backend); } private static DaggerProcessingEnv.Backend getBackend(XProcessingEnv env) { switch (env.getBackend()) { case JAVAC: return DaggerProcessingEnv.Backend.JAVAC; case KSP: return DaggerProcessingEnv.Backend.KSP; } throw new AssertionError(String.format("Unexpected backend %s", env.getBackend())); } private static final class DiagnosticReporterImpl extends DiagnosticReporter { static DiagnosticReporterImpl create( dagger.internal.codegen.model.DiagnosticReporter reporter) { return new DiagnosticReporterImpl(reporter); } private final dagger.internal.codegen.model.DiagnosticReporter delegate; DiagnosticReporterImpl(dagger.internal.codegen.model.DiagnosticReporter delegate) { this.delegate = delegate; } @Override public void reportComponent( Diagnostic.Kind diagnosticKind, ComponentNode componentNode, String message) { delegate.reportComponent(diagnosticKind, toInternal(componentNode), message); } @Override public void reportBinding( Diagnostic.Kind diagnosticKind, MaybeBinding binding, String message) { delegate.reportBinding(diagnosticKind, toInternal(binding), message); } @Override public void reportDependency( Diagnostic.Kind diagnosticKind, DependencyEdge dependencyEdge, String message) { delegate.reportDependency(diagnosticKind, toInternal(dependencyEdge), message); } @Override public void reportSubcomponentFactoryMethod( Diagnostic.Kind diagnosticKind, ChildFactoryMethodEdge childFactoryMethodEdge, String message) { delegate.reportSubcomponentFactoryMethod( diagnosticKind, toInternal(childFactoryMethodEdge), message); } } }