Skip to content

Commit

Permalink
In fast init mode, generate assisted injection with framework instanc…
Browse files Browse the repository at this point in the history
…e binding representation.

RELNOTES=n/a
PiperOrigin-RevId: 407695077
  • Loading branch information
wanyingd1996 authored and Dagger Team committed Nov 4, 2021
1 parent 147b890 commit da79a12
Show file tree
Hide file tree
Showing 7 changed files with 305 additions and 77 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ private static ImmutableList<ImmutableList<Binding>> bindingPartitions(
private final KotlinMetadataUtil metadataUtil;
private final ImmutableMap<ComponentImplementation, FieldSpec> componentFieldsByImplementation;
private final XMessager messager;
private final boolean isFastInit;

@Inject
ComponentImplementation(
Expand Down Expand Up @@ -296,6 +297,8 @@ private static ImmutableList<ImmutableList<Binding>> bindingPartitions(
this.componentFieldsByImplementation =
createComponentFieldsByImplementation(this, compilerOptions);
this.messager = messager;
this.isFastInit =
compilerOptions.fastInit(rootComponentImplementation().componentDescriptor().typeElement());
}

/**
Expand Down Expand Up @@ -386,6 +389,11 @@ public ClassName name() {
return componentShard.name;
}

/** Returns if the current compile mode is fast init. */
public boolean isFastInit() {
return isFastInit;
}

/** Returns whether or not the implementation is nested within another class. */
private boolean isNested() {
return name().enclosingClassName() != null;
Expand Down Expand Up @@ -422,6 +430,7 @@ public final class ShardImplementation {
private final ClassName name;
private final UniqueNameSet componentFieldNames = new UniqueNameSet();
private final UniqueNameSet componentMethodNames = new UniqueNameSet();
private final UniqueNameSet componentClassNames = new UniqueNameSet();
private final List<CodeBlock> initializations = new ArrayList<>();
private final Map<Key, CodeBlock> cancellations = new LinkedHashMap<>();
private final Map<VariableElement, String> uniqueAssistedName = new LinkedHashMap<>();
Expand Down Expand Up @@ -580,7 +589,7 @@ public String getUniqueFieldNameForAssistedParam(VariableElement element) {
return name;
}

/** Returns a new, unique method name for the component based on the given name. */
/** Returns a new, unique nested class name for the component based on the given name. */
public String getUniqueMethodName(String name) {
return componentMethodNames.getUniqueName(name);
}
Expand All @@ -590,6 +599,11 @@ String getUniqueMethodName(BindingRequest request) {
return uniqueMethodName(request, KeyVariableNamer.name(request.key()));
}

/** Returns a new, unique method name for the component based on the given name. */
public String getUniqueClassName(String name) {
return componentClassNames.getUniqueName(name);
}

private String uniqueMethodName(BindingRequest request, String bindingName) {
// This name is intentionally made to match the name for fields in fastInit
// in order to reduce the constant pool size. b/162004246
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ default Optional<ClassName> alternativeFrameworkClass() {
}
}

private final boolean isFastInit;
private final ShardImplementation shardImplementation;
private final ContributionBinding binding;
private final FrameworkInstanceCreationExpression frameworkInstanceCreationExpression;
Expand All @@ -72,6 +73,7 @@ default Optional<ClassName> alternativeFrameworkClass() {
ContributionBinding binding,
FrameworkInstanceCreationExpression frameworkInstanceCreationExpression) {
this.binding = checkNotNull(binding);
this.isFastInit = componentImplementation.isFastInit();
this.shardImplementation = checkNotNull(componentImplementation).shardImplementation(binding);
this.frameworkInstanceCreationExpression = checkNotNull(frameworkInstanceCreationExpression);
}
Expand Down Expand Up @@ -108,10 +110,17 @@ private void initializeField() {
break;

case INITIALIZING:
fieldSpec = getOrCreateField();
// If this is an ASSISTED_FACTORY binding in fastInit, then we don't have to worry about
// cycles since the creation of the factory itself doesn't actually take any dependencies.
// TODO(wanyingd): all switching providers do not need the delegation
if (isFastInit && binding.kind().equals(BindingKind.ASSISTED_FACTORY)) {
break;
}
// We were recursively invoked, so create a delegate factory instead
fieldInitializationState = InitializationState.DELEGATED;
shardImplementation.addInitialization(
CodeBlock.of("this.$N = new $T<>();", getOrCreateField(), delegateType()));
CodeBlock.of("this.$N = new $T<>();", fieldSpec, delegateType()));
break;

case DELEGATED:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
import static dagger.internal.codegen.writing.StaticFactoryInstanceSupplier.usesStaticFactoryCreation;
import static dagger.spi.model.BindingKind.DELEGATE;

import androidx.room.compiler.processing.XTypeElement;
import dagger.assisted.Assisted;
import dagger.assisted.AssistedFactory;
import dagger.assisted.AssistedInject;
Expand Down Expand Up @@ -56,9 +55,7 @@ final class ProvisionBindingRepresentation implements BindingRepresentation {
DaggerTypes types) {
this.binding = binding;
this.graph = graph;
XTypeElement rootComponent =
componentImplementation.rootComponentImplementation().componentDescriptor().typeElement();
this.isFastInit = compilerOptions.fastInit(rootComponent);
this.isFastInit = componentImplementation.isFastInit();
this.directInstanceBindingRepresentation =
directInstanceBindingRepresentationFactory.create(binding);
FrameworkInstanceSupplier frameworkInstanceSupplier = null;
Expand Down Expand Up @@ -92,14 +89,15 @@ static boolean usesDirectInstanceExpression(
// Currently, we always use a framework instance for MembersInjectors, e.g.
// InstanceFactory.create(Foo_MembersInjector.create(...)).
// TODO(b/199889259): Consider optimizing this for fastInit mode.
case ASSISTED_FACTORY:
return false;
// Assisted factory binding can be requested with framework request, and it is essentially a
// provider for assisted injection binding. So we will always return framework instance for
// assisted factory bindings.
case ASSISTED_INJECTION:
case ASSISTED_FACTORY:
// We choose not to use a direct expression for assisted injection/factory in default mode
// because they technically act more similar to a Provider than an instance, so we cache
// them using a field in the component similar to Provider requests. This should also be the
// case in FastInit, but it hasn't been implemented yet. We also don't need to check for
// caching since assisted bindings can't be scoped.
// We still return direct instance for assisted injection binding in fast init mode, because
// we want to avoid requiring dependencies as providers in fast init for now. This might
// change after we merge fast init mode and default mode.
return isFastInit;
default:
// We don't need to use Provider#get() if there's no caching, so use a direct instance.
Expand All @@ -114,6 +112,7 @@ private boolean usesSwitchingProvider() {
return false;
}
switch (binding.kind()) {
case ASSISTED_INJECTION:
case BOUND_INSTANCE:
case COMPONENT:
case COMPONENT_DEPENDENCY:
Expand All @@ -130,7 +129,6 @@ private boolean usesSwitchingProvider() {
return !binding.dependencies().isEmpty();
case INJECTION:
case PROVISION:
case ASSISTED_INJECTION:
case ASSISTED_FACTORY:
case COMPONENT_PROVISION:
case SUBCOMPONENT_CREATOR:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,20 @@
package dagger.internal.codegen.writing;

import static dagger.internal.codegen.binding.BindingRequest.bindingRequest;
import static dagger.internal.codegen.writing.BindingRepresentations.scope;
import static dagger.internal.codegen.javapoet.TypeNames.DOUBLE_CHECK;
import static dagger.internal.codegen.javapoet.TypeNames.SINGLE_CHECK;
import static dagger.internal.codegen.writing.ProvisionBindingRepresentation.usesDirectInstanceExpression;

import com.squareup.javapoet.CodeBlock;
import dagger.assisted.Assisted;
import dagger.assisted.AssistedFactory;
import dagger.assisted.AssistedInject;
import dagger.internal.codegen.binding.Binding;
import dagger.internal.codegen.binding.BindingGraph;
import dagger.internal.codegen.binding.BindingRequest;
import dagger.internal.codegen.binding.ProvisionBinding;
import dagger.internal.codegen.writing.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
import dagger.spi.model.BindingKind;
import dagger.spi.model.RequestKind;

/**
Expand Down Expand Up @@ -61,18 +65,30 @@ final class SwitchingProviderInstanceSupplier implements FrameworkInstanceSuppli
: unscopedDirectInstanceRequestRepresentationFactory.create(binding));
this.frameworkInstanceSupplier =
new FrameworkFieldInitializer(
componentImplementation,
binding,
binding.scope().isPresent()
? scope(binding, frameworkInstanceCreationExpression)
: frameworkInstanceCreationExpression);
componentImplementation, binding, scope(binding, frameworkInstanceCreationExpression));
}

@Override
public MemberSelect memberSelect() {
return frameworkInstanceSupplier.memberSelect();
}

private FrameworkInstanceCreationExpression scope(
Binding binding, FrameworkInstanceCreationExpression unscoped) {
// Caching assisted factory provider, so that there won't be new factory created for each
// provider.get() call.
if (!binding.scope().isPresent() && !binding.kind().equals(BindingKind.ASSISTED_FACTORY)) {
return unscoped;
}
return () ->
CodeBlock.of(
"$T.provider($L)",
binding.scope().isPresent()
? (binding.scope().get().isReusable() ? SINGLE_CHECK : DOUBLE_CHECK)
: SINGLE_CHECK,
unscoped.creationExpression());
}

@AssistedFactory
static interface Factory {
SwitchingProviderInstanceSupplier create(
Expand Down
3 changes: 2 additions & 1 deletion java/dagger/internal/codegen/writing/SwitchingProviders.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
import dagger.internal.codegen.langmodel.DaggerTypes;
import dagger.internal.codegen.writing.ComponentImplementation.ShardImplementation;
import dagger.internal.codegen.writing.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
import dagger.spi.model.BindingKind;
import dagger.spi.model.Key;
import java.util.HashMap;
import java.util.LinkedHashMap;
Expand Down Expand Up @@ -141,7 +142,7 @@ private CodeBlock getNewInstanceCodeBlock(
// Add the type parameter explicitly when the binding is scoped because Java can't resolve
// the type when wrapped. For example, the following will error:
// fooProvider = DoubleCheck.provider(new SwitchingProvider<>(1));
binding.scope().isPresent()
(binding.scope().isPresent() || binding.kind().equals(BindingKind.ASSISTED_FACTORY))
? CodeBlock.of(
"$T", types.accessibleType(binding.contributedType(), switchingProviderType))
: "",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*
* Copyright (C) 2021 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.functional.assisted;

import static com.google.common.truth.Truth.assertThat;

import dagger.Component;
import dagger.Module;
import dagger.Provides;
import dagger.Subcomponent;
import dagger.assisted.Assisted;
import dagger.assisted.AssistedFactory;
import dagger.assisted.AssistedInject;
import dagger.multibindings.IntoSet;
import java.util.Set;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

@RunWith(JUnit4.class)
public final class AssistedFactoryWithMultibindingsTest {
@Component(modules = ParentModule.class)
interface ParentComponent {
// Factory for assisted injection binding with multi binding contribution.
MultibindingFooFactory multibindingFooFactory();

ChildComponent.Builder childComponent();
}

static final class AssistedDep {}

static final class MultibindingFoo {
private final AssistedDep assistedDep;
private final Set<String> stringSet;

@AssistedInject
MultibindingFoo(@Assisted AssistedDep assistedDep, Set<String> stringSet) {
this.assistedDep = assistedDep;
this.stringSet = stringSet;
}

AssistedDep assistedDep() {
return assistedDep;
}

Set<String> stringSet() {
return stringSet;
}
}

@Subcomponent(modules = ChildModule.class)
static interface ChildComponent {
MultibindingFooFactory multibindingFooFactory();

@Subcomponent.Builder
interface Builder {
ChildComponent build();
}
}

@Module(subcomponents = ChildComponent.class)
static class ParentModule {
@Provides
@IntoSet
String parentString() {
return "parent";
}
}

@Module
static class ChildModule {
@Provides
@IntoSet
String childString() {
return "child";
}
}

@AssistedFactory
interface MultibindingFooFactory {
MultibindingFoo createFoo(AssistedDep factoryAssistedDep1);
}

@Test
public void testAssistedFactoryWithMultibinding() {
AssistedDep assistedDep1 = new AssistedDep();
ParentComponent parent = DaggerAssistedFactoryWithMultibindingsTest_ParentComponent.create();
ChildComponent child = parent.childComponent().build();
MultibindingFoo foo1 = parent.multibindingFooFactory().createFoo(assistedDep1);
MultibindingFoo foo2 = child.multibindingFooFactory().createFoo(assistedDep1);
assertThat(foo1.assistedDep()).isEqualTo(foo2.assistedDep);
assertThat(foo1.stringSet()).containsExactly("parent");
assertThat(foo2.stringSet()).containsExactly("child", "parent");
}
}
Loading

0 comments on commit da79a12

Please sign in to comment.