171 lines
7.6 KiB
Java
171 lines
7.6 KiB
Java
/*
|
|
* 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);
|
|
}
|
|
}
|