blob: c90d813484d2e35581b51eb5b2b0dfab4f740d00 [file] [log] [blame]
/*
* 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.base.Util.reentrantComputeIfAbsent;
import static dagger.internal.codegen.binding.BindingRequest.bindingRequest;
import dagger.assisted.Assisted;
import dagger.assisted.AssistedFactory;
import dagger.assisted.AssistedInject;
import dagger.internal.codegen.binding.BindingGraph;
import dagger.internal.codegen.binding.BindingRequest;
import dagger.internal.codegen.binding.ComponentDescriptor.ComponentMethodDescriptor;
import dagger.internal.codegen.binding.ProvisionBinding;
import dagger.internal.codegen.writing.ComponentImplementation.ShardImplementation;
import dagger.spi.model.RequestKind;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
/** Returns request representation based on a direct instance expression. */
final class DirectInstanceBindingRepresentation {
private final ProvisionBinding binding;
private final BindingGraph graph;
private final ComponentImplementation componentImplementation;
private final ComponentMethodRequestRepresentation.Factory
componentMethodRequestRepresentationFactory;
private final ImmediateFutureRequestRepresentation.Factory
immediateFutureRequestRepresentationFactory;
private final PrivateMethodRequestRepresentation.Factory
privateMethodRequestRepresentationFactory;
private final UnscopedDirectInstanceRequestRepresentationFactory
unscopedDirectInstanceRequestRepresentationFactory;
private final Map<BindingRequest, RequestRepresentation> requestRepresentations = new HashMap<>();
@AssistedInject
DirectInstanceBindingRepresentation(
@Assisted ProvisionBinding binding,
BindingGraph graph,
ComponentImplementation componentImplementation,
ComponentMethodRequestRepresentation.Factory componentMethodRequestRepresentationFactory,
ImmediateFutureRequestRepresentation.Factory immediateFutureRequestRepresentationFactory,
PrivateMethodRequestRepresentation.Factory privateMethodRequestRepresentationFactory,
UnscopedDirectInstanceRequestRepresentationFactory
unscopedDirectInstanceRequestRepresentationFactory) {
this.binding = binding;
this.graph = graph;
this.componentImplementation = componentImplementation;
this.componentMethodRequestRepresentationFactory = componentMethodRequestRepresentationFactory;
this.immediateFutureRequestRepresentationFactory = immediateFutureRequestRepresentationFactory;
this.privateMethodRequestRepresentationFactory = privateMethodRequestRepresentationFactory;
this.unscopedDirectInstanceRequestRepresentationFactory =
unscopedDirectInstanceRequestRepresentationFactory;
}
public RequestRepresentation getRequestRepresentation(BindingRequest request) {
return reentrantComputeIfAbsent(
requestRepresentations, request, this::getRequestRepresentationUncached);
}
private RequestRepresentation getRequestRepresentationUncached(BindingRequest request) {
switch (request.requestKind()) {
case INSTANCE:
return requiresMethodEncapsulation(binding)
? wrapInMethod(unscopedDirectInstanceRequestRepresentationFactory.create(binding))
: unscopedDirectInstanceRequestRepresentationFactory.create(binding);
case FUTURE:
return immediateFutureRequestRepresentationFactory.create(
getRequestRepresentation(bindingRequest(binding.key(), RequestKind.INSTANCE)),
binding.key().type().java());
default:
throw new AssertionError(
String.format("Invalid binding request kind: %s", request.requestKind()));
}
}
/**
* Returns a binding expression that uses a given one as the body of a method that users call. If
* a component provision method matches it, it will be the method implemented. If it does not
* match a component provision method and the binding is modifiable, then a new public modifiable
* binding method will be written. If the binding doesn't match a component method and is not
* modifiable, then a new private method will be written.
*/
RequestRepresentation wrapInMethod(RequestRepresentation bindingExpression) {
// If we've already wrapped the expression, then use the delegate.
if (bindingExpression instanceof MethodRequestRepresentation) {
return bindingExpression;
}
BindingRequest request = bindingRequest(binding.key(), RequestKind.INSTANCE);
Optional<ComponentMethodDescriptor> matchingComponentMethod =
graph.componentDescriptor().firstMatchingComponentMethod(request);
ShardImplementation shardImplementation = componentImplementation.shardImplementation(binding);
// Consider the case of a request from a component method like:
//
// DaggerMyComponent extends MyComponent {
// @Overrides
// Foo getFoo() {
// <FOO_BINDING_REQUEST>
// }
// }
//
// Normally, in this case we would return a ComponentMethodRequestRepresentation rather than a
// PrivateMethodRequestRepresentation so that #getFoo() can inline the implementation rather
// than
// create an unnecessary private method and return that. However, with sharding we don't want to
// inline the implementation because that would defeat some of the class pool savings if those
// fields had to communicate across shards. Thus, when a key belongs to a separate shard use a
// PrivateMethodRequestRepresentation and put the private method in the shard.
if (matchingComponentMethod.isPresent() && shardImplementation.isComponentShard()) {
ComponentMethodDescriptor componentMethod = matchingComponentMethod.get();
return componentMethodRequestRepresentationFactory.create(bindingExpression, componentMethod);
} else {
return privateMethodRequestRepresentationFactory.create(request, binding, bindingExpression);
}
}
private static boolean requiresMethodEncapsulation(ProvisionBinding binding) {
switch (binding.kind()) {
case COMPONENT:
case COMPONENT_PROVISION:
case SUBCOMPONENT_CREATOR:
case COMPONENT_DEPENDENCY:
case MULTIBOUND_SET:
case MULTIBOUND_MAP:
case BOUND_INSTANCE:
case ASSISTED_FACTORY:
case ASSISTED_INJECTION:
case INJECTION:
case PROVISION:
// These binding kinds satify a binding request with a component method or a private
// method when the requested binding has dependencies. The method will wrap the logic of
// creating the binding instance. Without the encapsulation, we might see many levels of
// nested instance creation code in a single statement to satisfy all dependencies of a
// binding request.
return !binding.dependencies().isEmpty();
case MEMBERS_INJECTOR:
case PRODUCTION:
case COMPONENT_PRODUCTION:
case OPTIONAL:
case DELEGATE:
case MEMBERS_INJECTION:
return false;
}
throw new AssertionError(String.format("No such binding kind: %s", binding.kind()));
}
@AssistedFactory
static interface Factory {
DirectInstanceBindingRepresentation create(ProvisionBinding binding);
}
}