Skip to content

Commit 86fcef5

Browse files
bcorsoDagger Team
authored andcommitted
Combine BindingGraphFactory.Resolver's caching logic into a single location.
Currently, there are two places where we try to cache a binding: 1. `Resolver#resolve(Key)` checks if there's a previously resolved binding for the given key in an ancestor resolver. 2. `Resolver#lookUpBindings(Key)` checks if a resolved binding should be owned by an ancestor component (e.g. because its scoped). Note that these two checks are slightly different. Case 1 only depends on the `Key` and is iteration-order dependent since it depends on whether the key has already been resolved in a ancestor binding. Case 2 depends on the binding rather than just the key (which is why it occurs at the end of `lookUpBindings`) and tries to determine if a binding could be resolved in a parent component (e.g. based on the scope or which component installs the module). Thus, case 2 may cache bindings that case 1 doesn't in cases where we know a binding is resolvable in an ancestor but hasn't yet been resolved (e.g. due to order or just because nothing actually requests it). In any case, this CL consolidates that logic into a single place. This should make it easier to extract the caching logic later from the intermediate graph creation. RELNOTES=N/A PiperOrigin-RevId: 661363402
1 parent 93201e0 commit 86fcef5

File tree

2 files changed

+106
-67
lines changed

2 files changed

+106
-67
lines changed

java/dagger/internal/codegen/binding/BindingGraphFactory.java

Lines changed: 36 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -335,13 +335,22 @@ && isAssistedFactoryType(requestKey.type().xprocessing().getTypeElement())) {
335335
requestKey,
336336
bindings.stream()
337337
.map(
338-
binding ->
339-
bindingNodeFactory.forContributionBindings(
340-
getOwningComponentPath(requestKey, binding),
341-
binding,
342-
multibindingDeclarations,
343-
optionalBindingDeclarations,
344-
subcomponentDeclarations))
338+
binding -> {
339+
Optional<BindingNode> bindingNodeOwnedByAncestor =
340+
getBindingNodeOwnedByAncestor(requestKey, binding);
341+
// If a binding is owned by an ancestor we use the corresponding BindingNode
342+
// instance directly rather than creating a new instance to avoid accidentally
343+
// including additional multi/optional/subcomponent declarations that don't
344+
// exist in the ancestor's BindingNode instance.
345+
return bindingNodeOwnedByAncestor.isPresent()
346+
? bindingNodeOwnedByAncestor.get()
347+
: bindingNodeFactory.forContributionBindings(
348+
componentPath,
349+
binding,
350+
multibindingDeclarations,
351+
optionalBindingDeclarations,
352+
subcomponentDeclarations);
353+
})
345354
.collect(toImmutableSet()));
346355
}
347356

@@ -443,38 +452,32 @@ private ContributionBinding createDelegateBinding(DelegateDeclaration delegateDe
443452
}
444453

445454
/**
446-
* Returns the component that should contain the framework field for {@code binding}.
447-
*
448-
* <p>If {@code binding} is either not bound in an ancestor component or depends transitively on
449-
* bindings in this component, returns this component.
450-
*
451-
* <p>Otherwise, resolves {@code request} in this component's parent in order to resolve any
452-
* multibinding contributions in the parent, and returns the parent-resolved {@link
453-
* ResolvedBindings#owningComponent(ContributionBinding)}.
455+
* Returns a {@link BindingNode} for the given binding that is owned by an ancestor component,
456+
* if one exists. Otherwise returns {@link Optional#empty()}.
454457
*/
455-
private ComponentPath getOwningComponentPath(Key requestKey, ContributionBinding binding) {
456-
if (isResolvedInParent(requestKey, binding) && !requiresResolution(binding)) {
457-
ResolvedBindings parentResolvedBindings =
458-
parentResolver.get().resolvedContributionBindings.get(requestKey);
459-
return parentResolvedBindings.forBinding(binding).componentPath();
460-
} else {
461-
return componentPath;
458+
private Optional<BindingNode> getBindingNodeOwnedByAncestor(
459+
Key requestKey, ContributionBinding binding) {
460+
if (canBeResolvedInParent(requestKey, binding)) {
461+
// Resolve in the parent to make sure we have the most recent multi/optional contributions.
462+
parentResolver.get().resolve(requestKey);
463+
if (!requiresResolution(binding)) {
464+
return Optional.of(getPreviouslyResolvedBindings(requestKey).get().forBinding(binding));
465+
}
462466
}
467+
return Optional.empty();
463468
}
464469

465-
/**
466-
* Returns {@code true} if {@code binding} is owned by an ancestor. If so, {@linkplain #resolve
467-
* resolves} the {@link Key} in this component's parent. Don't resolve directly in the owning
468-
* component in case it depends on multibindings in any of its descendants.
469-
*/
470-
private boolean isResolvedInParent(Key requestKey, ContributionBinding binding) {
471-
Optional<Resolver> owningResolver = getOwningResolver(binding);
472-
if (owningResolver.isPresent() && !owningResolver.get().equals(this)) {
473-
parentResolver.get().resolve(requestKey);
474-
return true;
475-
} else {
470+
private boolean canBeResolvedInParent(Key requestKey, ContributionBinding binding) {
471+
if (parentResolver.isEmpty()) {
476472
return false;
477473
}
474+
Optional<Resolver> owningResolver = getOwningResolver(binding);
475+
return (owningResolver.isPresent() && !owningResolver.get().equals(this))
476+
|| (!Keys.isComponentOrCreator(requestKey)
477+
// TODO(b/305748522): Allow caching for assisted injection bindings.
478+
&& binding.kind() != BindingKind.ASSISTED_INJECTION
479+
&& getPreviouslyResolvedBindings(requestKey).isPresent()
480+
&& getPreviouslyResolvedBindings(requestKey).get().bindings().contains(binding));
478481
}
479482

480483
private Optional<Resolver> getOwningResolver(ContributionBinding binding) {
@@ -647,36 +650,6 @@ void resolve(Key key) {
647650
return;
648651
}
649652

650-
/*
651-
* If the binding was previously resolved in an ancestor component, then we may be able to
652-
* avoid resolving it here and just depend on the ancestor component resolution.
653-
*
654-
* 1. If it depends transitively on multibinding contributions or optional bindings with
655-
* bindings from this subcomponent, then we have to resolve it in this subcomponent so
656-
* that it sees the local bindings.
657-
*
658-
* 2. If there are any explicit bindings in this component, they may conflict with those in
659-
* the ancestor component, so resolve them here so that conflicts can be caught.
660-
*/
661-
if (getPreviouslyResolvedBindings(key).isPresent() && !Keys.isComponentOrCreator(key)) {
662-
/* Resolve in the parent in case there are multibinding contributions or conflicts in some
663-
* component between this one and the previously-resolved one. */
664-
parentResolver.get().resolve(key);
665-
ResolvedBindings previouslyResolvedBindings = getPreviouslyResolvedBindings(key).get();
666-
// TODO(b/305748522): Allow caching for assisted injection bindings.
667-
boolean isAssistedInjectionBinding =
668-
previouslyResolvedBindings.bindings().stream()
669-
.anyMatch(binding -> binding.kind() == BindingKind.ASSISTED_INJECTION);
670-
if (!isAssistedInjectionBinding
671-
&& !requiresResolution(key)
672-
&& !hasLocalExplicitBindings(key)) {
673-
/* Cache the inherited parent component's bindings in case resolving at the parent found
674-
* bindings in some component between this one and the previously-resolved one. */
675-
resolvedContributionBindings.put(key, previouslyResolvedBindings);
676-
return;
677-
}
678-
}
679-
680653
cycleStack.push(key);
681654
try {
682655
ResolvedBindings bindings = lookUpBindings(key);
@@ -713,10 +686,6 @@ private ResolvedBindings getResolvedMembersInjectionBindings(Key key) {
713686
return resolvedMembersInjectionBindings.get(key);
714687
}
715688

716-
private boolean requiresResolution(Key key) {
717-
return new LegacyRequiresResolutionChecker().requiresResolution(key);
718-
}
719-
720689
private boolean requiresResolution(Binding binding) {
721690
return new LegacyRequiresResolutionChecker().requiresResolution(binding);
722691
}

javatests/dagger/internal/codegen/OptionalBindingTest.java

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,4 +105,74 @@ public void provideExplicitOptionalInParent_AndBindsOptionalOfInChild() {
105105
.onLineContaining("interface Parent");
106106
});
107107
}
108+
109+
// Note: This is a regression test for an issue we ran into in CL/644086367, where an optional
110+
// binding owned by a parent component is also requested by a child component which declares an
111+
// additional @BindsOptionalOf declaration. In this case, we just want to make sure that the setup
112+
// builds successfully.
113+
@Test
114+
public void cachedInParent_succeeds() {
115+
Source parent =
116+
CompilerTests.javaSource(
117+
"test.Parent",
118+
"package test;",
119+
"",
120+
"import dagger.Component;",
121+
"import java.util.Optional;",
122+
"",
123+
"@Component(modules = ParentModule.class)",
124+
"interface Parent {",
125+
" Optional<String> optionalString();",
126+
" Child child();",
127+
"}");
128+
Source parentModule =
129+
CompilerTests.javaSource(
130+
"test.ParentModule",
131+
"package test;",
132+
"",
133+
"import dagger.BindsOptionalOf;",
134+
"import dagger.Module;",
135+
"import dagger.Provides;",
136+
"import java.util.Optional;",
137+
"",
138+
"@Module",
139+
"interface ParentModule {",
140+
" @BindsOptionalOf",
141+
" String optionalParentString();",
142+
"",
143+
" @Provides",
144+
" static String provideString() {",
145+
" return \"\";",
146+
" }",
147+
"}");
148+
Source child =
149+
CompilerTests.javaSource(
150+
"test.Child",
151+
"package test;",
152+
"",
153+
"import dagger.Subcomponent;",
154+
"import java.util.Optional;",
155+
"",
156+
"@Subcomponent(modules = ChildModule.class)",
157+
"interface Child {",
158+
" Optional<String> optionalString();",
159+
"}");
160+
Source childModule =
161+
CompilerTests.javaSource(
162+
"test.ChildModule",
163+
"package test;",
164+
"",
165+
"import dagger.BindsOptionalOf;",
166+
"import dagger.Module;",
167+
"",
168+
"@Module",
169+
"interface ChildModule {",
170+
" @BindsOptionalOf",
171+
" String optionalChildString();",
172+
"}");
173+
174+
CompilerTests.daggerCompiler(parent, parentModule, child, childModule)
175+
.withProcessingOptions(compilerMode.processorOptions())
176+
.compile(subject -> subject.hasErrorCount(0));
177+
}
108178
}

0 commit comments

Comments
 (0)