Skip to content

Commit

Permalink
Split FrameworkInstanceBindingRepresentation by if uses Switching P…
Browse files Browse the repository at this point in the history
…rovider.

RELNOTES=N/A
PiperOrigin-RevId: 398749684
  • Loading branch information
wanyingd1996 authored and Dagger Team committed Sep 24, 2021
1 parent 7e09cee commit b1ea93b
Show file tree
Hide file tree
Showing 6 changed files with 313 additions and 234 deletions.
Expand Up @@ -33,11 +33,8 @@
import java.util.Map;
import java.util.Optional;

/**
* A provision binding representation that returns expressions based on a direct instance
* expression.
*/
final class DirectInstanceBindingRepresentation implements BindingRepresentation {
/** Returns request representation based on a direct instance expression. */
final class DirectInstanceBindingRepresentation {
private final ProvisionBinding binding;
private final BindingGraph graph;
private final ComponentImplementation componentImplementation;
Expand Down Expand Up @@ -77,7 +74,6 @@ final class DirectInstanceBindingRepresentation implements BindingRepresentation
assistedPrivateMethodRequestRepresentationFactory;
}

@Override
public RequestRepresentation getRequestRepresentation(BindingRequest request) {
return reentrantComputeIfAbsent(
requestRepresentations, request, this::getRequestRepresentationUncached);
Expand Down
Expand Up @@ -18,11 +18,8 @@

import static dagger.internal.codegen.base.Util.reentrantComputeIfAbsent;
import static dagger.internal.codegen.binding.BindingRequest.bindingRequest;
import static dagger.internal.codegen.writing.BindingRepresentations.scope;
import static dagger.internal.codegen.writing.DelegateRequestRepresentation.isBindsScopeStrongerThanDependencyScope;
import static dagger.internal.codegen.writing.ProvisionBindingRepresentation.needsCaching;
import static dagger.spi.model.BindingKind.DELEGATE;
import static dagger.spi.model.BindingKind.MULTIBOUND_MAP;
import static dagger.spi.model.BindingKind.MULTIBOUND_SET;

import dagger.assisted.Assisted;
import dagger.assisted.AssistedFactory;
Expand All @@ -31,99 +28,55 @@
import dagger.internal.codegen.binding.BindingRequest;
import dagger.internal.codegen.binding.FrameworkType;
import dagger.internal.codegen.binding.ProvisionBinding;
import dagger.internal.codegen.compileroption.CompilerOptions;
import dagger.internal.codegen.langmodel.DaggerTypes;
import dagger.internal.codegen.writing.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
import dagger.spi.model.RequestKind;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import javax.lang.model.element.TypeElement;

/**
* A binding representation that returns expressions based on a framework instance expression. i.e.
* provider.get()
*/
final class FrameworkInstanceBindingRepresentation implements BindingRepresentation {
private final BindingGraph graph;
private final boolean isFastInit;
/** Returns request representation that wraps a framework instance expression */
final class FrameworkInstanceBindingRepresentation {
private final ProvisionBinding binding;
private final ComponentImplementation componentImplementation;
private final DelegateRequestRepresentation.Factory delegateRequestRepresentationFactory;
private final DerivedFromFrameworkInstanceRequestRepresentation.Factory
derivedFromFrameworkInstanceRequestRepresentationFactory;
private final ImmediateFutureRequestRepresentation.Factory
immediateFutureRequestRepresentationFactory;
private final PrivateMethodRequestRepresentation.Factory
privateMethodRequestRepresentationFactory;
private final ProducerNodeInstanceRequestRepresentation.Factory
producerNodeInstanceRequestRepresentationFactory;
private final ProviderInstanceRequestRepresentation.Factory
providerInstanceRequestRepresentationFactory;
private final UnscopedDirectInstanceRequestRepresentationFactory
unscopedDirectInstanceRequestRepresentationFactory;
private final UnscopedFrameworkInstanceCreationExpressionFactory
unscopedFrameworkInstanceCreationExpressionFactory;
private final DirectInstanceBindingRepresentation directInstanceBindingRepresentation;
private final SwitchingProviders switchingProviders;
private final Map<BindingRequest, RequestRepresentation> requestRepresentations = new HashMap<>();
private final FrameworkInstanceSupplier providerField;
private final FrameworkInstanceSupplier producerFromProviderField;
private final RequestRepresentation providerRequestRepresentation;
private final RequestRepresentation producerFromProviderRepresentation;

@AssistedInject
FrameworkInstanceBindingRepresentation(
@Assisted ProvisionBinding binding,
@Assisted DirectInstanceBindingRepresentation directInstanceBindingRepresentation,
SwitchingProviders switchingProviders,
BindingGraph graph,
@Assisted FrameworkInstanceSupplier providerField,
ComponentImplementation componentImplementation,
DelegateRequestRepresentation.Factory delegateRequestRepresentationFactory,
DerivedFromFrameworkInstanceRequestRepresentation.Factory
derivedFromFrameworkInstanceRequestRepresentationFactory,
ImmediateFutureRequestRepresentation.Factory immediateFutureRequestRepresentationFactory,
PrivateMethodRequestRepresentation.Factory privateMethodRequestRepresentationFactory,
ProducerNodeInstanceRequestRepresentation.Factory
producerNodeInstanceRequestRepresentationFactory,
ProviderInstanceRequestRepresentation.Factory providerInstanceRequestRepresentationFactory,
UnscopedDirectInstanceRequestRepresentationFactory
unscopedDirectInstanceRequestRepresentationFactory,
ProducerFromProviderCreationExpression.Factory producerFromProviderCreationExpressionFactory,
UnscopedFrameworkInstanceCreationExpressionFactory
unscopedFrameworkInstanceCreationExpressionFactory,
CompilerOptions compilerOptions,
DaggerTypes types) {
ProducerFromProviderCreationExpression.Factory
producerFromProviderCreationExpressionFactory) {
this.binding = binding;
this.switchingProviders = switchingProviders;
this.graph = graph;
this.componentImplementation = componentImplementation;
this.delegateRequestRepresentationFactory = delegateRequestRepresentationFactory;
this.derivedFromFrameworkInstanceRequestRepresentationFactory =
derivedFromFrameworkInstanceRequestRepresentationFactory;
this.immediateFutureRequestRepresentationFactory = immediateFutureRequestRepresentationFactory;
this.privateMethodRequestRepresentationFactory = privateMethodRequestRepresentationFactory;
this.producerNodeInstanceRequestRepresentationFactory =
producerNodeInstanceRequestRepresentationFactory;
this.providerInstanceRequestRepresentationFactory =
providerInstanceRequestRepresentationFactory;
this.unscopedDirectInstanceRequestRepresentationFactory =
unscopedDirectInstanceRequestRepresentationFactory;
this.unscopedFrameworkInstanceCreationExpressionFactory =
unscopedFrameworkInstanceCreationExpressionFactory;
this.directInstanceBindingRepresentation = directInstanceBindingRepresentation;
TypeElement rootComponent =
componentImplementation.rootComponentImplementation().componentDescriptor().typeElement();
this.isFastInit = compilerOptions.fastInit(rootComponent);
this.providerField = providerField();
this.producerFromProviderField =
new FrameworkFieldInitializer(
componentImplementation,
this.providerRequestRepresentation =
binding.kind().equals(DELEGATE) && !needsCaching(binding, graph)
? delegateRequestRepresentationFactory.create(binding, RequestKind.PROVIDER)
: providerInstanceRequestRepresentationFactory.create(binding, providerField);
this.producerFromProviderRepresentation =
producerNodeInstanceRequestRepresentationFactory.create(
binding,
producerFromProviderCreationExpressionFactory.create(
getRequestRepresentation(bindingRequest(binding.key(), FrameworkType.PROVIDER)),
componentImplementation.shardImplementation(binding).name()));
new FrameworkFieldInitializer(
componentImplementation,
binding,
producerFromProviderCreationExpressionFactory.create(
providerRequestRepresentation,
componentImplementation.shardImplementation(binding).name())));
}

@Override
public RequestRepresentation getRequestRepresentation(BindingRequest request) {
return reentrantComputeIfAbsent(
requestRepresentations, request, this::getRequestRepresentationUncached);
Expand All @@ -136,7 +89,7 @@ private RequestRepresentation getRequestRepresentationUncached(BindingRequest re
bindingRequest(binding.key(), RequestKind.INSTANCE), FrameworkType.PROVIDER);

case PROVIDER:
return providerRequestRepresentation();
return providerRequestRepresentation;

case LAZY:
case PRODUCED:
Expand All @@ -145,8 +98,7 @@ private RequestRepresentation getRequestRepresentationUncached(BindingRequest re
request, FrameworkType.PROVIDER);

case PRODUCER:
return producerNodeInstanceRequestRepresentationFactory.create(
binding, producerFromProviderField);
return producerFromProviderRepresentation;

case FUTURE:
return immediateFutureRequestRepresentationFactory.create(
Expand All @@ -159,155 +111,9 @@ private RequestRepresentation getRequestRepresentationUncached(BindingRequest re
}
}

/** Supplies a {@link MemberSelect} for a framework instance */
private FrameworkInstanceSupplier providerField() {
// In default mode, we always use the static factory creation strategy. In fastInit mode, we
// prefer to use a SwitchingProvider instead of static factories in order to reduce class
// loading; however, we allow static factories that can reused across multiple bindings, e.g.
// {@code MapFactory} or {@code SetFactory}.
// TODO(bcorso): Consider merging the static factory creation logic into CreationExpressions?
Optional<MemberSelect> staticMethod = staticFactoryCreation();
if (staticMethod.isPresent()) {
return staticMethod::get;
}

FrameworkInstanceCreationExpression frameworkInstanceCreationExpression =
unscopedFrameworkInstanceCreationExpressionFactory.create(binding);

if (useSwitchingProvider()) {
// First try to get the instance expression via getRequestRepresentation(). However, if that
// expression is a DerivedFromFrameworkInstanceRequestRepresentation (e.g. fooProvider.get()),
// then we can't use it to create an instance within the SwitchingProvider since that would
// cause a cycle. In such cases, we try to use the unscopedDirectInstanceRequestRepresentation
// directly, or else fall back to default mode.
BindingRequest instanceRequest = bindingRequest(binding.key(), RequestKind.INSTANCE);
if (ProvisionBindingRepresentation.usesDirectInstanceExpression(
RequestKind.INSTANCE, binding, graph, isFastInit)) {
frameworkInstanceCreationExpression =
switchingProviders.newFrameworkInstanceCreationExpression(
binding,
directInstanceBindingRepresentation.getRequestRepresentation(instanceRequest));
} else {
RequestRepresentation unscopedInstanceExpression =
unscopedDirectInstanceRequestRepresentationFactory.create(binding);
frameworkInstanceCreationExpression =
switchingProviders.newFrameworkInstanceCreationExpression(
binding,
ProvisionBindingRepresentation.requiresMethodEncapsulation(binding)
? privateMethodRequestRepresentationFactory.create(
instanceRequest, binding, unscopedInstanceExpression)
: unscopedInstanceExpression);
}
}

return new FrameworkFieldInitializer(
componentImplementation,
binding,
binding.scope().isPresent()
? scope(binding, frameworkInstanceCreationExpression)
: frameworkInstanceCreationExpression);
}

/**
* If {@code resolvedBindings} is an unscoped provision binding with no factory arguments, then we
* don't need a field to hold its factory. In that case, this method returns the static member
* select that returns the factory.
*/
// TODO(wanyingd): no-op members injector is currently handled in
// `MembersInjectorProviderCreationExpression`, we should inline the logic here so we won't create
// an extra field for it.
private Optional<MemberSelect> staticFactoryCreation() {
if (binding.dependencies().isEmpty() && !binding.scope().isPresent()) {
switch (binding.kind()) {
case MULTIBOUND_MAP:
return Optional.of(StaticMemberSelects.emptyMapFactory(binding));
case MULTIBOUND_SET:
return Optional.of(StaticMemberSelects.emptySetFactory(binding));
case PROVISION:
if (!isFastInit && !binding.requiresModuleInstance()) {
return Optional.of(StaticMemberSelects.factoryCreateNoArgumentMethod(binding));
}
break;
case INJECTION:
if (!isFastInit) {
return Optional.of(StaticMemberSelects.factoryCreateNoArgumentMethod(binding));
}
break;
default:
return Optional.empty();
}
}
return Optional.empty();
}

/**
* Returns a binding expression for {@link RequestKind#PROVIDER} requests.
*
* <p>{@code @Binds} bindings that don't {@linkplain #needsCaching() need to be cached} can use a
* {@link DelegateRequestRepresentation}.
*
* <p>Otherwise, return a {@link FrameworkInstanceRequestRepresentation}.
*/
private RequestRepresentation providerRequestRepresentation() {
if (binding.kind().equals(DELEGATE) && !needsCaching()) {
return delegateRequestRepresentationFactory.create(binding, RequestKind.PROVIDER);
}
return providerInstanceRequestRepresentationFactory.create(binding, providerField);
}

private boolean useSwitchingProvider() {
if (!isFastInit) {
return false;
}
switch (binding.kind()) {
case BOUND_INSTANCE:
case COMPONENT:
case COMPONENT_DEPENDENCY:
case DELEGATE:
case MEMBERS_INJECTOR: // TODO(b/199889259): Consider optimizing this for fastInit mode.
// These binding kinds avoid SwitchingProvider when the backing instance already exists,
// e.g. a component provider can use FactoryInstance.create(this).
return false;
case MULTIBOUND_SET:
case MULTIBOUND_MAP:
case OPTIONAL:
// These binding kinds avoid SwitchingProvider when their are no dependencies,
// e.g. a multibound set with no dependency can use a singleton, SetFactory.empty().
return !binding.dependencies().isEmpty();
case INJECTION:
case PROVISION:
case ASSISTED_INJECTION:
case ASSISTED_FACTORY:
case COMPONENT_PROVISION:
case SUBCOMPONENT_CREATOR:
case PRODUCTION:
case COMPONENT_PRODUCTION:
case MEMBERS_INJECTION:
return true;
}
throw new AssertionError(String.format("No such binding kind: %s", binding.kind()));
}

/**
* Returns {@code true} if the component needs to make sure the provided value is cached.
*
* <p>The component needs to cache the value for scoped bindings except for {@code @Binds}
* bindings whose scope is no stronger than their delegate's.
*/
private boolean needsCaching() {
if (!binding.scope().isPresent()) {
return false;
}
if (binding.kind().equals(DELEGATE)) {
return isBindsScopeStrongerThanDependencyScope(binding, graph);
}
return true;
}

@AssistedFactory
static interface Factory {
FrameworkInstanceBindingRepresentation create(
ProvisionBinding binding,
DirectInstanceBindingRepresentation directInstanceBindingRepresentation);
ProvisionBinding binding, FrameworkInstanceSupplier providerField);
}
}
58 changes: 58 additions & 0 deletions java/dagger/internal/codegen/writing/ProviderInstanceSupplier.java
@@ -0,0 +1,58 @@
/*
* 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.internal.codegen.writing;

import static dagger.internal.codegen.writing.BindingRepresentations.scope;

import dagger.assisted.Assisted;
import dagger.assisted.AssistedFactory;
import dagger.assisted.AssistedInject;
import dagger.internal.codegen.binding.ProvisionBinding;
import dagger.internal.codegen.writing.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;

/** An object that initializes a framework-type component field for a binding. */
final class ProviderInstanceSupplier implements FrameworkInstanceSupplier {
private final FrameworkInstanceSupplier frameworkInstanceSupplier;

@AssistedInject
ProviderInstanceSupplier(
@Assisted ProvisionBinding binding,
ComponentImplementation componentImplementation,
FrameworkInstanceBindingRepresentation.Factory frameworkInstanceBindingRepresentationFactory,
UnscopedFrameworkInstanceCreationExpressionFactory
unscopedFrameworkInstanceCreationExpressionFactory) {
FrameworkInstanceCreationExpression frameworkInstanceCreationExpression =
unscopedFrameworkInstanceCreationExpressionFactory.create(binding);
this.frameworkInstanceSupplier =
new FrameworkFieldInitializer(
componentImplementation,
binding,
binding.scope().isPresent()
? scope(binding, frameworkInstanceCreationExpression)
: frameworkInstanceCreationExpression);
}

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

@AssistedFactory
static interface Factory {
ProviderInstanceSupplier create(ProvisionBinding binding);
}
}

0 comments on commit b1ea93b

Please sign in to comment.