Skip to content

Commit

Permalink
Remove caching of @AssistedInject bindings to work around b/305748522.
Browse files Browse the repository at this point in the history
This is a short-term fix for b/305748522 so that we can push through our other fix for b/302199325. The long-term fix is outlined in the b/305748522 but probably requires more design discussion.

RELNOTES=N/A
PiperOrigin-RevId: 573855920
  • Loading branch information
bcorso authored and Dagger Team committed Oct 17, 2023
1 parent d7dd656 commit 95df373
Show file tree
Hide file tree
Showing 4 changed files with 361 additions and 3 deletions.
13 changes: 10 additions & 3 deletions java/dagger/internal/codegen/binding/BindingGraphFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
import dagger.internal.codegen.base.OptionalType;
import dagger.internal.codegen.compileroption.CompilerOptions;
import dagger.internal.codegen.javapoet.TypeNames;
import dagger.internal.codegen.model.BindingKind;
import dagger.internal.codegen.model.ComponentPath;
import dagger.internal.codegen.model.DaggerTypeElement;
import dagger.internal.codegen.model.DependencyRequest;
Expand Down Expand Up @@ -812,11 +813,17 @@ void resolve(Key key) {
/* Resolve in the parent in case there are multibinding contributions or conflicts in some
* component between this one and the previously-resolved one. */
parentResolver.get().resolve(key);
if (!new LocalDependencyChecker().dependsOnLocalBindings(key)
&& getLocalExplicitBindings(key).isEmpty()) {
ResolvedBindings previouslyResolvedBindings = getPreviouslyResolvedBindings(key).get();
// TODO(b/305748522): Allow caching for assisted injection bindings.
boolean isAssistedInjectionBinding =
previouslyResolvedBindings.bindings().stream()
.anyMatch(binding -> binding.kind() == BindingKind.ASSISTED_INJECTION);
if (!isAssistedInjectionBinding
&& !new LocalDependencyChecker().dependsOnLocalBindings(key)
&& getLocalExplicitBindings(key).isEmpty()) {
/* Cache the inherited parent component's bindings in case resolving at the parent found
* bindings in some component between this one and the previously-resolved one. */
resolvedContributionBindings.put(key, getPreviouslyResolvedBindings(key).get());
resolvedContributionBindings.put(key, previouslyResolvedBindings);
return;
}
}
Expand Down
106 changes: 106 additions & 0 deletions javatests/dagger/internal/codegen/AssistedFactoryTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -326,4 +326,110 @@ public void testParameterizedAssistParam() throws Exception {
subject.generatedSource(goldenFileRule.goldenSource("test/DaggerTestComponent"));
});
}

// This is a regression test for b/305748522
// The important thing for this test is that we have two assisted factories for the same assisted
// injection class and that they are requested in different components.
@Test
public void testMultipleAssistedFactoryInDifferentComponents() throws Exception {
Source component =
CompilerTests.javaSource(
"test.MyComponent",
"package test;",
"",
"import dagger.Component;",
"",
"@Component",
"interface MyComponent {",
" MyComponentAssistedFactory myComponentAssistedFactory();",
" MySubcomponent mySubcomponent();",
"}");
Source subcomponent =
CompilerTests.javaSource(
"test.MySubcomponent",
"package test;",
"",
"import dagger.Subcomponent;",
"",
"@Subcomponent",
"interface MySubcomponent {",
" MySubcomponentAssistedFactory mySubcomponentAssistedFactory();",
"}");
Source assistedClass =
CompilerTests.javaSource(
"test.MyAssistedClass",
"package test;",
"",
"import dagger.assisted.Assisted;",
"import dagger.assisted.AssistedInject;",
"",
"final class MyAssistedClass {",
" private final Foo foo;",
" private final Bar bar;",
"",
" @AssistedInject",
" MyAssistedClass(@Assisted Foo foo, Baz baz, @Assisted Bar bar) {",
" this.foo = foo;",
" this.bar = bar;",
" }",
"}");
Source componentAssistedFactory =
CompilerTests.javaSource(
"test.MyComponentAssistedFactory",
"package test;",
"",
"import dagger.assisted.AssistedFactory;",
"",
"@AssistedFactory",
"interface MyComponentAssistedFactory {",
" MyAssistedClass create(Bar bar, Foo foo);",
"}");
Source subcomponentAssistedFactory =
CompilerTests.javaSource(
"test.MySubcomponentAssistedFactory",
"package test;",
"",
"import dagger.assisted.AssistedFactory;",
"",
"@AssistedFactory",
"interface MySubcomponentAssistedFactory {",
" MyAssistedClass create(Bar bar, Foo foo);",
"}");
Source foo =
CompilerTests.javaSource(
"test.Foo",
"package test;",
"final class Foo {}");
Source bar =
CompilerTests.javaSource(
"test.Bar",
"package test;",
"final class Bar {}");
Source baz =
CompilerTests.javaSource(
"test.Baz",
"package test;",
"",
"import javax.inject.Inject;",
"",
"final class Baz {",
" @Inject Baz() {}",
"}");

CompilerTests.daggerCompiler(
component,
subcomponent,
assistedClass,
componentAssistedFactory,
subcomponentAssistedFactory,
foo,
bar,
baz)
.withProcessingOptions(compilerMode.processorOptions())
.compile(
subject -> {
subject.hasErrorCount(0);
subject.generatedSource(goldenFileRule.goldenSource("test/DaggerMyComponent"));
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package test;

import dagger.internal.DaggerGenerated;
import javax.annotation.processing.Generated;
import javax.inject.Provider;

@DaggerGenerated
@Generated(
value = "dagger.internal.codegen.ComponentProcessor",
comments = "https://dagger.dev"
)
@SuppressWarnings({
"unchecked",
"rawtypes",
"KotlinInternal",
"KotlinInternalInJava"
})
final class DaggerMyComponent {
private DaggerMyComponent() {
}

public static Builder builder() {
return new Builder();
}

public static MyComponent create() {
return new Builder().build();
}

static final class Builder {
private Builder() {
}

public MyComponent build() {
return new MyComponentImpl();
}
}

private static final class MySubcomponentImpl implements MySubcomponent {
private final MyComponentImpl myComponentImpl;

private final MySubcomponentImpl mySubcomponentImpl = this;

private MyAssistedClass_Factory myAssistedClassProvider;

private Provider<MySubcomponentAssistedFactory> mySubcomponentAssistedFactoryProvider;

private MySubcomponentImpl(MyComponentImpl myComponentImpl) {
this.myComponentImpl = myComponentImpl;

initialize();

}

@SuppressWarnings("unchecked")
private void initialize() {
this.myAssistedClassProvider = MyAssistedClass_Factory.create(Baz_Factory.create());
this.mySubcomponentAssistedFactoryProvider = MySubcomponentAssistedFactory_Impl.create(myAssistedClassProvider);
}

@Override
public MySubcomponentAssistedFactory mySubcomponentAssistedFactory() {
return mySubcomponentAssistedFactoryProvider.get();
}
}

private static final class MyComponentImpl implements MyComponent {
private final MyComponentImpl myComponentImpl = this;

private MyAssistedClass_Factory myAssistedClassProvider;

private Provider<MyComponentAssistedFactory> myComponentAssistedFactoryProvider;

private MyComponentImpl() {

initialize();

}

@SuppressWarnings("unchecked")
private void initialize() {
this.myAssistedClassProvider = MyAssistedClass_Factory.create(Baz_Factory.create());
this.myComponentAssistedFactoryProvider = MyComponentAssistedFactory_Impl.create(myAssistedClassProvider);
}

@Override
public MyComponentAssistedFactory myComponentAssistedFactory() {
return myComponentAssistedFactoryProvider.get();
}

@Override
public MySubcomponent mySubcomponent() {
return new MySubcomponentImpl(myComponentImpl);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
package test;

import dagger.internal.DaggerGenerated;
import dagger.internal.SingleCheck;
import javax.annotation.processing.Generated;
import javax.inject.Provider;

@DaggerGenerated
@Generated(
value = "dagger.internal.codegen.ComponentProcessor",
comments = "https://dagger.dev"
)
@SuppressWarnings({
"unchecked",
"rawtypes",
"KotlinInternal",
"KotlinInternalInJava"
})
final class DaggerMyComponent {
private DaggerMyComponent() {
}

public static Builder builder() {
return new Builder();
}

public static MyComponent create() {
return new Builder().build();
}

static final class Builder {
private Builder() {
}

public MyComponent build() {
return new MyComponentImpl();
}
}

private static final class MySubcomponentImpl implements MySubcomponent {
private final MyComponentImpl myComponentImpl;

private final MySubcomponentImpl mySubcomponentImpl = this;

private Provider<MySubcomponentAssistedFactory> mySubcomponentAssistedFactoryProvider;

private MySubcomponentImpl(MyComponentImpl myComponentImpl) {
this.myComponentImpl = myComponentImpl;

initialize();

}

@SuppressWarnings("unchecked")
private void initialize() {
this.mySubcomponentAssistedFactoryProvider = SingleCheck.provider(new SwitchingProvider<MySubcomponentAssistedFactory>(myComponentImpl, mySubcomponentImpl, 0));
}

@Override
public MySubcomponentAssistedFactory mySubcomponentAssistedFactory() {
return mySubcomponentAssistedFactoryProvider.get();
}

private static final class SwitchingProvider<T> implements Provider<T> {
private final MyComponentImpl myComponentImpl;

private final MySubcomponentImpl mySubcomponentImpl;

private final int id;

SwitchingProvider(MyComponentImpl myComponentImpl, MySubcomponentImpl mySubcomponentImpl,
int id) {
this.myComponentImpl = myComponentImpl;
this.mySubcomponentImpl = mySubcomponentImpl;
this.id = id;
}

@SuppressWarnings("unchecked")
@Override
public T get() {
switch (id) {
case 0: // test.MySubcomponentAssistedFactory
return (T) new MySubcomponentAssistedFactory() {
@Override
public MyAssistedClass create(Bar bar, Foo foo) {
return new MyAssistedClass(foo, new Baz(), bar);
}
};

default: throw new AssertionError(id);
}
}
}
}

private static final class MyComponentImpl implements MyComponent {
private final MyComponentImpl myComponentImpl = this;

private Provider<MyComponentAssistedFactory> myComponentAssistedFactoryProvider;

private MyComponentImpl() {

initialize();

}

@SuppressWarnings("unchecked")
private void initialize() {
this.myComponentAssistedFactoryProvider = SingleCheck.provider(new SwitchingProvider<MyComponentAssistedFactory>(myComponentImpl, 0));
}

@Override
public MyComponentAssistedFactory myComponentAssistedFactory() {
return myComponentAssistedFactoryProvider.get();
}

@Override
public MySubcomponent mySubcomponent() {
return new MySubcomponentImpl(myComponentImpl);
}

private static final class SwitchingProvider<T> implements Provider<T> {
private final MyComponentImpl myComponentImpl;

private final int id;

SwitchingProvider(MyComponentImpl myComponentImpl, int id) {
this.myComponentImpl = myComponentImpl;
this.id = id;
}

@SuppressWarnings("unchecked")
@Override
public T get() {
switch (id) {
case 0: // test.MyComponentAssistedFactory
return (T) new MyComponentAssistedFactory() {
@Override
public MyAssistedClass create(Bar bar, Foo foo) {
return new MyAssistedClass(foo, new Baz(), bar);
}
};

default: throw new AssertionError(id);
}
}
}
}
}

0 comments on commit 95df373

Please sign in to comment.