• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 The Dagger Authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package dagger.internal.codegen.writing;
18 
19 import static dagger.internal.codegen.base.Util.reentrantComputeIfAbsent;
20 import static dagger.internal.codegen.binding.BindingRequest.bindingRequest;
21 
22 import dagger.assisted.Assisted;
23 import dagger.assisted.AssistedFactory;
24 import dagger.assisted.AssistedInject;
25 import dagger.internal.codegen.binding.BindingGraph;
26 import dagger.internal.codegen.binding.BindingRequest;
27 import dagger.internal.codegen.binding.ComponentDescriptor.ComponentMethodDescriptor;
28 import dagger.internal.codegen.binding.ProvisionBinding;
29 import dagger.internal.codegen.model.RequestKind;
30 import dagger.internal.codegen.writing.ComponentImplementation.ShardImplementation;
31 import java.util.HashMap;
32 import java.util.Map;
33 import java.util.Optional;
34 
35 /** Returns request representation based on a direct instance expression. */
36 final class DirectInstanceBindingRepresentation {
37   private final ProvisionBinding binding;
38   private final BindingGraph graph;
39   private final ComponentImplementation componentImplementation;
40   private final ComponentMethodRequestRepresentation.Factory
41       componentMethodRequestRepresentationFactory;
42   private final ImmediateFutureRequestRepresentation.Factory
43       immediateFutureRequestRepresentationFactory;
44   private final PrivateMethodRequestRepresentation.Factory
45       privateMethodRequestRepresentationFactory;
46   private final UnscopedDirectInstanceRequestRepresentationFactory
47       unscopedDirectInstanceRequestRepresentationFactory;
48   private final Map<BindingRequest, RequestRepresentation> requestRepresentations = new HashMap<>();
49 
50   @AssistedInject
DirectInstanceBindingRepresentation( @ssisted ProvisionBinding binding, BindingGraph graph, ComponentImplementation componentImplementation, ComponentMethodRequestRepresentation.Factory componentMethodRequestRepresentationFactory, ImmediateFutureRequestRepresentation.Factory immediateFutureRequestRepresentationFactory, PrivateMethodRequestRepresentation.Factory privateMethodRequestRepresentationFactory, UnscopedDirectInstanceRequestRepresentationFactory unscopedDirectInstanceRequestRepresentationFactory)51   DirectInstanceBindingRepresentation(
52       @Assisted ProvisionBinding binding,
53       BindingGraph graph,
54       ComponentImplementation componentImplementation,
55       ComponentMethodRequestRepresentation.Factory componentMethodRequestRepresentationFactory,
56       ImmediateFutureRequestRepresentation.Factory immediateFutureRequestRepresentationFactory,
57       PrivateMethodRequestRepresentation.Factory privateMethodRequestRepresentationFactory,
58       UnscopedDirectInstanceRequestRepresentationFactory
59           unscopedDirectInstanceRequestRepresentationFactory) {
60     this.binding = binding;
61     this.graph = graph;
62     this.componentImplementation = componentImplementation;
63     this.componentMethodRequestRepresentationFactory = componentMethodRequestRepresentationFactory;
64     this.immediateFutureRequestRepresentationFactory = immediateFutureRequestRepresentationFactory;
65     this.privateMethodRequestRepresentationFactory = privateMethodRequestRepresentationFactory;
66     this.unscopedDirectInstanceRequestRepresentationFactory =
67         unscopedDirectInstanceRequestRepresentationFactory;
68   }
69 
getRequestRepresentation(BindingRequest request)70   public RequestRepresentation getRequestRepresentation(BindingRequest request) {
71     return reentrantComputeIfAbsent(
72         requestRepresentations, request, this::getRequestRepresentationUncached);
73   }
74 
getRequestRepresentationUncached(BindingRequest request)75   private RequestRepresentation getRequestRepresentationUncached(BindingRequest request) {
76     switch (request.requestKind()) {
77       case INSTANCE:
78         return requiresMethodEncapsulation(binding)
79             ? wrapInMethod(unscopedDirectInstanceRequestRepresentationFactory.create(binding))
80             : unscopedDirectInstanceRequestRepresentationFactory.create(binding);
81 
82       case FUTURE:
83         return immediateFutureRequestRepresentationFactory.create(
84             getRequestRepresentation(bindingRequest(binding.key(), RequestKind.INSTANCE)),
85             binding.key().type().xprocessing());
86 
87       default:
88         throw new AssertionError(
89             String.format("Invalid binding request kind: %s", request.requestKind()));
90     }
91   }
92 
93   /**
94    * Returns a binding expression that uses a given one as the body of a method that users call. If
95    * a component provision method matches it, it will be the method implemented. If it does not
96    * match a component provision method and the binding is modifiable, then a new public modifiable
97    * binding method will be written. If the binding doesn't match a component method and is not
98    * modifiable, then a new private method will be written.
99    */
wrapInMethod(RequestRepresentation bindingExpression)100   RequestRepresentation wrapInMethod(RequestRepresentation bindingExpression) {
101     // If we've already wrapped the expression, then use the delegate.
102     if (bindingExpression instanceof MethodRequestRepresentation) {
103       return bindingExpression;
104     }
105 
106     BindingRequest request = bindingRequest(binding.key(), RequestKind.INSTANCE);
107     Optional<ComponentMethodDescriptor> matchingComponentMethod =
108         graph.componentDescriptor().firstMatchingComponentMethod(request);
109 
110     ShardImplementation shardImplementation = componentImplementation.shardImplementation(binding);
111 
112     // Consider the case of a request from a component method like:
113     //
114     //   DaggerMyComponent extends MyComponent {
115     //     @Overrides
116     //     Foo getFoo() {
117     //       <FOO_BINDING_REQUEST>
118     //     }
119     //   }
120     //
121     // Normally, in this case we would return a ComponentMethodRequestRepresentation rather than a
122     // PrivateMethodRequestRepresentation so that #getFoo() can inline the implementation rather
123     // than
124     // create an unnecessary private method and return that. However, with sharding we don't want to
125     // inline the implementation because that would defeat some of the class pool savings if those
126     // fields had to communicate across shards. Thus, when a key belongs to a separate shard use a
127     // PrivateMethodRequestRepresentation and put the private method in the shard.
128     if (matchingComponentMethod.isPresent() && shardImplementation.isComponentShard()) {
129       ComponentMethodDescriptor componentMethod = matchingComponentMethod.get();
130       return componentMethodRequestRepresentationFactory.create(bindingExpression, componentMethod);
131     } else {
132       return privateMethodRequestRepresentationFactory.create(request, binding, bindingExpression);
133     }
134   }
135 
requiresMethodEncapsulation(ProvisionBinding binding)136   private static boolean requiresMethodEncapsulation(ProvisionBinding binding) {
137     switch (binding.kind()) {
138       case COMPONENT:
139       case COMPONENT_PROVISION:
140       case SUBCOMPONENT_CREATOR:
141       case COMPONENT_DEPENDENCY:
142       case MULTIBOUND_SET:
143       case MULTIBOUND_MAP:
144       case BOUND_INSTANCE:
145       case ASSISTED_FACTORY:
146       case ASSISTED_INJECTION:
147       case INJECTION:
148       case PROVISION:
149         // These binding kinds satify a binding request with a component method or a private
150         // method when the requested binding has dependencies. The method will wrap the logic of
151         // creating the binding instance. Without the encapsulation, we might see many levels of
152         // nested instance creation code in a single statement to satisfy all dependencies of a
153         // binding request.
154         return !binding.dependencies().isEmpty();
155       case MEMBERS_INJECTOR:
156       case PRODUCTION:
157       case COMPONENT_PRODUCTION:
158       case OPTIONAL:
159       case DELEGATE:
160       case MEMBERS_INJECTION:
161         return false;
162     }
163     throw new AssertionError(String.format("No such binding kind: %s", binding.kind()));
164   }
165 
166   @AssistedFactory
167   static interface Factory {
create(ProvisionBinding binding)168     DirectInstanceBindingRepresentation create(ProvisionBinding binding);
169   }
170 }
171