| dpb | 68c77d8 | 2017-08-15 15:23:35 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2016 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; |
| 18 | |
| dpb | 6486184 | 2017-12-12 08:17:18 -0800 | [diff] [blame] | 19 | import static com.google.common.base.Preconditions.checkArgument; |
| bcorso | f0a99fe | 2017-12-04 10:52:05 -0800 | [diff] [blame] | 20 | import static com.google.common.base.Preconditions.checkNotNull; |
| dpb | 3db68f0 | 2018-01-04 09:15:52 -0800 | [diff] [blame] | 21 | import static com.google.common.collect.Iterables.getOnlyElement; |
| dpb | 68c77d8 | 2017-08-15 15:23:35 -0700 | [diff] [blame] | 22 | import static dagger.internal.codegen.Accessibility.isRawTypeAccessible; |
| 23 | import static dagger.internal.codegen.Accessibility.isTypeAccessibleFrom; |
| dpb | 3db68f0 | 2018-01-04 09:15:52 -0800 | [diff] [blame] | 24 | import static dagger.internal.codegen.BindingType.MEMBERS_INJECTION; |
| 25 | import static dagger.internal.codegen.CodeBlocks.makeParametersCodeBlock; |
| dpb | 09fb2cb | 2018-01-29 08:39:33 -0800 | [diff] [blame] | 26 | import static dagger.internal.codegen.DelegateBindingExpression.isBindsScopeStrongerThanDependencyScope; |
| dpb | 356a684 | 2018-02-07 07:45:50 -0800 | [diff] [blame] | 27 | import static dagger.internal.codegen.MemberSelect.staticFactoryCreation; |
| dpb | 3db68f0 | 2018-01-04 09:15:52 -0800 | [diff] [blame] | 28 | import static dagger.internal.codegen.SourceFiles.membersInjectorNameForType; |
| 29 | import static dagger.internal.codegen.TypeNames.DOUBLE_CHECK; |
| 30 | import static dagger.internal.codegen.TypeNames.REFERENCE_RELEASING_PROVIDER; |
| 31 | import static dagger.internal.codegen.TypeNames.SINGLE_CHECK; |
| dpb | 09fb2cb | 2018-01-29 08:39:33 -0800 | [diff] [blame] | 32 | import static dagger.model.BindingKind.DELEGATE; |
| bcorso | af9e004 | 2018-04-16 14:12:23 -0700 | [diff] [blame] | 33 | import static dagger.model.BindingKind.MULTIBOUND_MAP; |
| 34 | import static dagger.model.BindingKind.MULTIBOUND_SET; |
| dpb | 68c77d8 | 2017-08-15 15:23:35 -0700 | [diff] [blame] | 35 | |
| dpb | 3db68f0 | 2018-01-04 09:15:52 -0800 | [diff] [blame] | 36 | import com.google.auto.common.MoreTypes; |
| bcorso | 8c3605d | 2017-12-13 13:36:49 -0800 | [diff] [blame] | 37 | import com.google.common.collect.HashBasedTable; |
| dpb | 3db68f0 | 2018-01-04 09:15:52 -0800 | [diff] [blame] | 38 | import com.google.common.collect.ImmutableList; |
| bcorso | 8c3605d | 2017-12-13 13:36:49 -0800 | [diff] [blame] | 39 | import com.google.common.collect.Table; |
| dpb | 68c77d8 | 2017-08-15 15:23:35 -0700 | [diff] [blame] | 40 | import com.squareup.javapoet.ClassName; |
| bcorso | e643cc6 | 2017-10-24 08:57:09 -0700 | [diff] [blame] | 41 | import com.squareup.javapoet.CodeBlock; |
| dpb | 02db213 | 2018-01-08 07:20:23 -0800 | [diff] [blame] | 42 | import com.squareup.javapoet.MethodSpec; |
| dpb | 3db68f0 | 2018-01-04 09:15:52 -0800 | [diff] [blame] | 43 | import dagger.internal.MembersInjectors; |
| bcorso | 2c27966 | 2017-12-01 09:51:18 -0800 | [diff] [blame] | 44 | import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor; |
| dpb | 3db68f0 | 2018-01-04 09:15:52 -0800 | [diff] [blame] | 45 | import dagger.internal.codegen.FrameworkFieldInitializer.FrameworkInstanceCreationExpression; |
| ronshapiro | a5023ca | 2018-01-02 12:55:01 -0800 | [diff] [blame] | 46 | import dagger.model.DependencyRequest; |
| ronshapiro | 0a277fd | 2017-12-22 08:30:49 -0800 | [diff] [blame] | 47 | import dagger.model.Key; |
| ronshapiro | 120abc6 | 2017-12-15 09:57:09 -0800 | [diff] [blame] | 48 | import dagger.model.RequestKind; |
| bcorso | f0a99fe | 2017-12-04 10:52:05 -0800 | [diff] [blame] | 49 | import java.util.Optional; |
| dpb | 356a684 | 2018-02-07 07:45:50 -0800 | [diff] [blame] | 50 | import javax.inject.Provider; |
| dpb | 68c77d8 | 2017-08-15 15:23:35 -0700 | [diff] [blame] | 51 | import javax.lang.model.type.TypeMirror; |
| 52 | |
| ronshapiro | dc07ed5 | 2017-08-23 08:52:10 -0700 | [diff] [blame] | 53 | /** A central repository of code expressions used to access any binding available to a component. */ |
| dpb | 68c77d8 | 2017-08-15 15:23:35 -0700 | [diff] [blame] | 54 | final class ComponentBindingExpressions { |
| 55 | |
| ronshapiro | dc07ed5 | 2017-08-23 08:52:10 -0700 | [diff] [blame] | 56 | // TODO(dpb,ronshapiro): refactor this and ComponentRequirementFields into a |
| 57 | // HierarchicalComponentMap<K, V>, or perhaps this use a flattened ImmutableMap, built from its |
| 58 | // parents? If so, maybe make BindingExpression.Factory create it. |
| dpb | 68c77d8 | 2017-08-15 15:23:35 -0700 | [diff] [blame] | 59 | |
| bcorso | f0a99fe | 2017-12-04 10:52:05 -0800 | [diff] [blame] | 60 | private final Optional<ComponentBindingExpressions> parent; |
| 61 | private final BindingGraph graph; |
| dpb | 02db213 | 2018-01-08 07:20:23 -0800 | [diff] [blame] | 62 | private final GeneratedComponentModel generatedComponentModel; |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 63 | private final SubcomponentNames subcomponentNames; |
| 64 | private final ComponentRequirementFields componentRequirementFields; |
| 65 | private final ReferenceReleasingManagerFields referenceReleasingManagerFields; |
| 66 | private final OptionalFactories optionalFactories; |
| bcorso | f0a99fe | 2017-12-04 10:52:05 -0800 | [diff] [blame] | 67 | private final DaggerTypes types; |
| dpb | e7b8958 | 2018-02-19 08:42:19 -0800 | [diff] [blame] | 68 | private final DaggerElements elements; |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 69 | private final CompilerOptions compilerOptions; |
| 70 | private final MembersInjectionMethods membersInjectionMethods; |
| bcorso | 8a8039a | 2018-03-19 09:36:26 -0700 | [diff] [blame] | 71 | private final SwitchingProviders switchingProviders; |
| ronshapiro | 0a277fd | 2017-12-22 08:30:49 -0800 | [diff] [blame] | 72 | private final Table<Key, RequestKind, BindingExpression> expressions = HashBasedTable.create(); |
| dpb | 68c77d8 | 2017-08-15 15:23:35 -0700 | [diff] [blame] | 73 | |
| bcorso | f0a99fe | 2017-12-04 10:52:05 -0800 | [diff] [blame] | 74 | ComponentBindingExpressions( |
| 75 | BindingGraph graph, |
| 76 | GeneratedComponentModel generatedComponentModel, |
| 77 | SubcomponentNames subcomponentNames, |
| 78 | ComponentRequirementFields componentRequirementFields, |
| 79 | OptionalFactories optionalFactories, |
| 80 | DaggerTypes types, |
| dpb | e7b8958 | 2018-02-19 08:42:19 -0800 | [diff] [blame] | 81 | DaggerElements elements, |
| bcorso | f0a99fe | 2017-12-04 10:52:05 -0800 | [diff] [blame] | 82 | CompilerOptions compilerOptions) { |
| 83 | this( |
| 84 | Optional.empty(), |
| 85 | graph, |
| 86 | generatedComponentModel, |
| 87 | subcomponentNames, |
| 88 | componentRequirementFields, |
| 89 | new ReferenceReleasingManagerFields(graph, generatedComponentModel), |
| 90 | optionalFactories, |
| 91 | types, |
| 92 | elements, |
| 93 | compilerOptions); |
| dpb | 68c77d8 | 2017-08-15 15:23:35 -0700 | [diff] [blame] | 94 | } |
| 95 | |
| bcorso | f0a99fe | 2017-12-04 10:52:05 -0800 | [diff] [blame] | 96 | private ComponentBindingExpressions( |
| 97 | Optional<ComponentBindingExpressions> parent, |
| 98 | BindingGraph graph, |
| 99 | GeneratedComponentModel generatedComponentModel, |
| 100 | SubcomponentNames subcomponentNames, |
| 101 | ComponentRequirementFields componentRequirementFields, |
| 102 | ReferenceReleasingManagerFields referenceReleasingManagerFields, |
| 103 | OptionalFactories optionalFactories, |
| 104 | DaggerTypes types, |
| dpb | e7b8958 | 2018-02-19 08:42:19 -0800 | [diff] [blame] | 105 | DaggerElements elements, |
| bcorso | f0a99fe | 2017-12-04 10:52:05 -0800 | [diff] [blame] | 106 | CompilerOptions compilerOptions) { |
| 107 | this.parent = parent; |
| 108 | this.graph = graph; |
| dpb | 02db213 | 2018-01-08 07:20:23 -0800 | [diff] [blame] | 109 | this.generatedComponentModel = generatedComponentModel; |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 110 | this.subcomponentNames = checkNotNull(subcomponentNames); |
| 111 | this.componentRequirementFields = checkNotNull(componentRequirementFields); |
| 112 | this.referenceReleasingManagerFields = checkNotNull(referenceReleasingManagerFields); |
| 113 | this.optionalFactories = checkNotNull(optionalFactories); |
| 114 | this.types = checkNotNull(types); |
| 115 | this.elements = checkNotNull(elements); |
| 116 | this.compilerOptions = checkNotNull(compilerOptions); |
| 117 | this.membersInjectionMethods = |
| 118 | new MembersInjectionMethods(generatedComponentModel, this, graph, elements, types); |
| bcorso | af9e004 | 2018-04-16 14:12:23 -0700 | [diff] [blame] | 119 | this.switchingProviders = new SwitchingProviders(generatedComponentModel, this, types); |
| bcorso | f0a99fe | 2017-12-04 10:52:05 -0800 | [diff] [blame] | 120 | } |
| 121 | |
| 122 | /** |
| 123 | * Returns a new object representing the bindings available from a child component of this one. |
| 124 | */ |
| 125 | ComponentBindingExpressions forChildComponent( |
| 126 | BindingGraph childGraph, |
| 127 | GeneratedComponentModel childComponentModel, |
| 128 | ComponentRequirementFields childComponentRequirementFields) { |
| 129 | return new ComponentBindingExpressions( |
| 130 | Optional.of(this), |
| 131 | childGraph, |
| 132 | childComponentModel, |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 133 | subcomponentNames, |
| bcorso | f0a99fe | 2017-12-04 10:52:05 -0800 | [diff] [blame] | 134 | childComponentRequirementFields, |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 135 | referenceReleasingManagerFields, |
| 136 | optionalFactories, |
| 137 | types, |
| 138 | elements, |
| 139 | compilerOptions); |
| dpb | 68c77d8 | 2017-08-15 15:23:35 -0700 | [diff] [blame] | 140 | } |
| 141 | |
| 142 | /** |
| 143 | * Returns an expression that evaluates to the value of a dependency request for a binding owned |
| 144 | * by this component or an ancestor. |
| 145 | * |
| 146 | * @param requestingClass the class that will contain the expression |
| 147 | * @throws IllegalStateException if there is no binding expression that satisfies the dependency |
| 148 | * request |
| 149 | */ |
| ronshapiro | 0a277fd | 2017-12-22 08:30:49 -0800 | [diff] [blame] | 150 | Expression getDependencyExpression(Key key, RequestKind requestKind, ClassName requestingClass) { |
| 151 | return getBindingExpression(key, requestKind).getDependencyExpression(requestingClass); |
| ronshapiro | ba79486 | 2017-09-28 11:39:34 -0700 | [diff] [blame] | 152 | } |
| 153 | |
| 154 | /** |
| 155 | * Returns an expression that evaluates to the value of a dependency request for a binding owned |
| 156 | * by this component or an ancestor. |
| 157 | * |
| 158 | * @param requestingClass the class that will contain the expression |
| 159 | * @throws IllegalStateException if there is no binding expression that satisfies the dependency |
| 160 | * request |
| 161 | */ |
| 162 | Expression getDependencyExpression(DependencyRequest request, ClassName requestingClass) { |
| ronshapiro | 0a277fd | 2017-12-22 08:30:49 -0800 | [diff] [blame] | 163 | return getDependencyExpression(request.key(), request.kind(), requestingClass); |
| dpb | 68c77d8 | 2017-08-15 15:23:35 -0700 | [diff] [blame] | 164 | } |
| 165 | |
| 166 | /** |
| 167 | * Returns an expression that evaluates to the value of a framework dependency for a binding owned |
| 168 | * in this component or an ancestor. |
| 169 | * |
| 170 | * @param requestingClass the class that will contain the expression |
| 171 | * @throws IllegalStateException if there is no binding expression that satisfies the dependency |
| 172 | * request |
| 173 | */ |
| ronshapiro | ba79486 | 2017-09-28 11:39:34 -0700 | [diff] [blame] | 174 | Expression getDependencyExpression( |
| dpb | 68c77d8 | 2017-08-15 15:23:35 -0700 | [diff] [blame] | 175 | FrameworkDependency frameworkDependency, ClassName requestingClass) { |
| ronshapiro | ba79486 | 2017-09-28 11:39:34 -0700 | [diff] [blame] | 176 | return getDependencyExpression( |
| ronshapiro | 0a277fd | 2017-12-22 08:30:49 -0800 | [diff] [blame] | 177 | frameworkDependency.key(), frameworkDependency.dependencyRequestKind(), requestingClass); |
| dpb | 68c77d8 | 2017-08-15 15:23:35 -0700 | [diff] [blame] | 178 | } |
| 179 | |
| 180 | /** |
| bcorso | 5db065d | 2018-04-03 13:49:31 -0700 | [diff] [blame] | 181 | * Returns the {@link CodeBlock} for the method argmuments used with the factory {@code create()} |
| 182 | * method for the given {@link ContributionBinding binding}. |
| dpb | 3db68f0 | 2018-01-04 09:15:52 -0800 | [diff] [blame] | 183 | */ |
| bcorso | 5db065d | 2018-04-03 13:49:31 -0700 | [diff] [blame] | 184 | CodeBlock getCreateMethodArgumentsCodeBlock(ContributionBinding binding) { |
| 185 | return makeParametersCodeBlock(getCreateMethodArgumentsCodeBlocks(binding)); |
| 186 | } |
| 187 | |
| 188 | private ImmutableList<CodeBlock> getCreateMethodArgumentsCodeBlocks(ContributionBinding binding) { |
| 189 | ImmutableList.Builder<CodeBlock> arguments = ImmutableList.builder(); |
| 190 | |
| 191 | if (binding.requiresModuleInstance()) { |
| 192 | arguments.add( |
| 193 | componentRequirementFields.getExpressionDuringInitialization( |
| 194 | ComponentRequirement.forModule(binding.contributingModule().get().asType()), |
| 195 | generatedComponentModel.name())); |
| 196 | } |
| 197 | |
| 198 | binding |
| 199 | .frameworkDependencies() |
| dpb | 3db68f0 | 2018-01-04 09:15:52 -0800 | [diff] [blame] | 200 | .stream() |
| bcorso | 5db065d | 2018-04-03 13:49:31 -0700 | [diff] [blame] | 201 | .map(dependency -> getDependencyExpression(dependency, generatedComponentModel.name())) |
| 202 | .map(Expression::codeBlock) |
| 203 | .forEach(arguments::add); |
| 204 | |
| 205 | return arguments.build(); |
| dpb | 3db68f0 | 2018-01-04 09:15:52 -0800 | [diff] [blame] | 206 | } |
| 207 | |
| 208 | /** |
| dpb | 68c77d8 | 2017-08-15 15:23:35 -0700 | [diff] [blame] | 209 | * Returns an expression that evaluates to the value of a dependency request, for passing to a |
| 210 | * binding method, an {@code @Inject}-annotated constructor or member, or a proxy for one. |
| 211 | * |
| 212 | * <p>If the method is a generated static {@link InjectionMethods injection method}, each |
| 213 | * parameter will be {@link Object} if the dependency's raw type is inaccessible. If that is the |
| 214 | * case for this dependency, the returned expression will use a cast to evaluate to the raw type. |
| 215 | * |
| 216 | * @param requestingClass the class that will contain the expression |
| 217 | */ |
| 218 | // TODO(b/64024402) Merge with getDependencyExpression(DependencyRequest, ClassName) if possible. |
| ronshapiro | ba79486 | 2017-09-28 11:39:34 -0700 | [diff] [blame] | 219 | Expression getDependencyArgumentExpression( |
| dpb | 68c77d8 | 2017-08-15 15:23:35 -0700 | [diff] [blame] | 220 | DependencyRequest dependencyRequest, ClassName requestingClass) { |
| dpb | 68c77d8 | 2017-08-15 15:23:35 -0700 | [diff] [blame] | 221 | |
| 222 | TypeMirror dependencyType = dependencyRequest.key().type(); |
| ronshapiro | ba79486 | 2017-09-28 11:39:34 -0700 | [diff] [blame] | 223 | Expression dependencyExpression = getDependencyExpression(dependencyRequest, requestingClass); |
| 224 | |
| bcorso | aaffd2d | 2018-04-16 14:44:42 -0700 | [diff] [blame^] | 225 | if (dependencyRequest.kind().equals(RequestKind.INSTANCE) |
| 226 | && !isTypeAccessibleFrom(dependencyType, requestingClass.packageName()) |
| dpb | 68c77d8 | 2017-08-15 15:23:35 -0700 | [diff] [blame] | 227 | && isRawTypeAccessible(dependencyType, requestingClass.packageName())) { |
| ronshapiro | ba79486 | 2017-09-28 11:39:34 -0700 | [diff] [blame] | 228 | return dependencyExpression.castTo(types.erasure(dependencyType)); |
| dpb | 68c77d8 | 2017-08-15 15:23:35 -0700 | [diff] [blame] | 229 | } |
| 230 | |
| ronshapiro | ba79486 | 2017-09-28 11:39:34 -0700 | [diff] [blame] | 231 | return dependencyExpression; |
| dpb | 68c77d8 | 2017-08-15 15:23:35 -0700 | [diff] [blame] | 232 | } |
| 233 | |
| dpb | 02db213 | 2018-01-08 07:20:23 -0800 | [diff] [blame] | 234 | /** Returns the implementation of a component method. */ |
| 235 | MethodSpec getComponentMethod(ComponentMethodDescriptor componentMethod) { |
| 236 | checkArgument(componentMethod.dependencyRequest().isPresent()); |
| 237 | DependencyRequest dependencyRequest = componentMethod.dependencyRequest().get(); |
| 238 | return MethodSpec.overriding( |
| 239 | componentMethod.methodElement(), |
| 240 | MoreTypes.asDeclared(graph.componentType().asType()), |
| 241 | types) |
| 242 | .addCode( |
| 243 | getBindingExpression(dependencyRequest.key(), dependencyRequest.kind()) |
| 244 | .getComponentMethodImplementation(componentMethod, generatedComponentModel.name())) |
| 245 | .build(); |
| bcorso | 11f9b87 | 2017-10-09 16:18:55 -0700 | [diff] [blame] | 246 | } |
| 247 | |
| ronshapiro | 0a277fd | 2017-12-22 08:30:49 -0800 | [diff] [blame] | 248 | private BindingExpression getBindingExpression(Key key, RequestKind requestKind) { |
| 249 | ResolvedBindings resolvedBindings = graph.resolvedBindings(requestKind, key); |
| 250 | if (resolvedBindings != null && !resolvedBindings.ownedBindings().isEmpty()) { |
| 251 | if (!expressions.contains(key, requestKind)) { |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 252 | expressions.put(key, requestKind, createBindingExpression(resolvedBindings, requestKind)); |
| bcorso | 8c3605d | 2017-12-13 13:36:49 -0800 | [diff] [blame] | 253 | } |
| ronshapiro | 0a277fd | 2017-12-22 08:30:49 -0800 | [diff] [blame] | 254 | return expressions.get(key, requestKind); |
| bcorso | f0a99fe | 2017-12-04 10:52:05 -0800 | [diff] [blame] | 255 | } |
| dpb | 80eda69 | 2018-03-05 11:31:42 -0800 | [diff] [blame] | 256 | checkArgument(parent.isPresent(), "no expression found for %s-%s", key, requestKind); |
| 257 | return parent.get().getBindingExpression(key, requestKind); |
| bcorso | f0a99fe | 2017-12-04 10:52:05 -0800 | [diff] [blame] | 258 | } |
| 259 | |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 260 | /** Creates a binding expression. */ |
| 261 | private BindingExpression createBindingExpression( |
| 262 | ResolvedBindings resolvedBindings, RequestKind requestKind) { |
| 263 | switch (resolvedBindings.bindingType()) { |
| 264 | case MEMBERS_INJECTION: |
| 265 | checkArgument(requestKind.equals(RequestKind.MEMBERS_INJECTION)); |
| ronshapiro | 9288a97 | 2018-02-14 06:02:12 -0800 | [diff] [blame] | 266 | return new MembersInjectionBindingExpression(resolvedBindings, membersInjectionMethods); |
| bcorso | f0a99fe | 2017-12-04 10:52:05 -0800 | [diff] [blame] | 267 | |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 268 | case PROVISION: |
| 269 | return provisionBindingExpression(resolvedBindings, requestKind); |
| 270 | |
| 271 | case PRODUCTION: |
| 272 | return frameworkInstanceBindingExpression(resolvedBindings, requestKind); |
| 273 | |
| 274 | default: |
| 275 | throw new AssertionError(resolvedBindings); |
| bcorso | f0a99fe | 2017-12-04 10:52:05 -0800 | [diff] [blame] | 276 | } |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 277 | } |
| bcorso | f0a99fe | 2017-12-04 10:52:05 -0800 | [diff] [blame] | 278 | |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 279 | /** |
| 280 | * Returns a binding expression that uses a {@link javax.inject.Provider} for provision bindings |
| 281 | * or a {@link dagger.producers.Producer} for production bindings. |
| 282 | */ |
| 283 | private FrameworkInstanceBindingExpression frameworkInstanceBindingExpression( |
| 284 | ResolvedBindings resolvedBindings, RequestKind requestKind) { |
| bcorso | af9e004 | 2018-04-16 14:12:23 -0700 | [diff] [blame] | 285 | // TODO(user): Consider merging the static factory creation logic into CreationExpressions? |
| 286 | Optional<MemberSelect> staticMethod = |
| 287 | useStaticFactoryCreation(resolvedBindings.contributionBinding()) |
| 288 | ? staticFactoryCreation(resolvedBindings) |
| 289 | : Optional.empty(); |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 290 | FrameworkInstanceCreationExpression frameworkInstanceCreationExpression = |
| 291 | resolvedBindings.scope().isPresent() |
| 292 | ? scope(resolvedBindings, frameworkInstanceCreationExpression(resolvedBindings)) |
| 293 | : frameworkInstanceCreationExpression(resolvedBindings); |
| 294 | return new FrameworkInstanceBindingExpression( |
| 295 | resolvedBindings, |
| 296 | requestKind, |
| 297 | this, |
| 298 | resolvedBindings.bindingType().frameworkType(), |
| 299 | staticMethod.isPresent() |
| 300 | ? staticMethod::get |
| 301 | : new FrameworkFieldInitializer( |
| 302 | generatedComponentModel, resolvedBindings, frameworkInstanceCreationExpression), |
| 303 | types, |
| 304 | elements); |
| 305 | } |
| dpb | 6486184 | 2017-12-12 08:17:18 -0800 | [diff] [blame] | 306 | |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 307 | private FrameworkInstanceCreationExpression scope( |
| 308 | ResolvedBindings resolvedBindings, FrameworkInstanceCreationExpression unscoped) { |
| 309 | if (requiresReleasableReferences(resolvedBindings)) { |
| 310 | return () -> |
| 311 | CodeBlock.of( |
| 312 | "$T.create($L, $L)", |
| 313 | REFERENCE_RELEASING_PROVIDER, |
| 314 | unscoped.creationExpression(), |
| 315 | referenceReleasingManagerFields.getExpression( |
| 316 | resolvedBindings.scope().get(), generatedComponentModel.name())); |
| 317 | } else { |
| 318 | return () -> |
| 319 | CodeBlock.of( |
| 320 | "$T.provider($L)", |
| 321 | resolvedBindings.scope().get().isReusable() ? SINGLE_CHECK : DOUBLE_CHECK, |
| 322 | unscoped.creationExpression()); |
| dpb | 6486184 | 2017-12-12 08:17:18 -0800 | [diff] [blame] | 323 | } |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 324 | } |
| dpb | 6486184 | 2017-12-12 08:17:18 -0800 | [diff] [blame] | 325 | |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 326 | /** |
| 327 | * Returns a creation expression for a {@link javax.inject.Provider} for provision bindings or a |
| 328 | * {@link dagger.producers.Producer} for production bindings. |
| 329 | */ |
| 330 | private FrameworkInstanceCreationExpression frameworkInstanceCreationExpression( |
| 331 | ResolvedBindings resolvedBindings) { |
| 332 | checkArgument(!resolvedBindings.bindingType().equals(MEMBERS_INJECTION)); |
| 333 | ContributionBinding binding = resolvedBindings.contributionBinding(); |
| 334 | switch (binding.kind()) { |
| 335 | case COMPONENT: |
| 336 | // The cast can be removed when we drop java 7 source support |
| 337 | return new InstanceFactoryCreationExpression( |
| 338 | () -> CodeBlock.of("($T) this", binding.key().type())); |
| dpb | 6486184 | 2017-12-12 08:17:18 -0800 | [diff] [blame] | 339 | |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 340 | case BOUND_INSTANCE: |
| 341 | return instanceFactoryCreationExpression( |
| 342 | binding, ComponentRequirement.forBoundInstance(binding)); |
| bcorso | f0a99fe | 2017-12-04 10:52:05 -0800 | [diff] [blame] | 343 | |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 344 | case COMPONENT_DEPENDENCY: |
| 345 | return instanceFactoryCreationExpression( |
| 346 | binding, ComponentRequirement.forDependency(binding.key().type())); |
| dpb | 3db68f0 | 2018-01-04 09:15:52 -0800 | [diff] [blame] | 347 | |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 348 | case COMPONENT_PROVISION: |
| 349 | return new DependencyMethodProviderCreationExpression( |
| 350 | binding, generatedComponentModel, componentRequirementFields, compilerOptions, graph); |
| dpb | 3db68f0 | 2018-01-04 09:15:52 -0800 | [diff] [blame] | 351 | |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 352 | case SUBCOMPONENT_BUILDER: |
| 353 | return new SubcomponentBuilderProviderCreationExpression( |
| 354 | binding.key().type(), subcomponentNames.get(binding.key())); |
| dpb | 356a684 | 2018-02-07 07:45:50 -0800 | [diff] [blame] | 355 | |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 356 | case INJECTION: |
| 357 | case PROVISION: |
| bcorso | 5db065d | 2018-04-03 13:49:31 -0700 | [diff] [blame] | 358 | return new InjectionOrProvisionProviderCreationExpression(binding, this); |
| dpb | 3db68f0 | 2018-01-04 09:15:52 -0800 | [diff] [blame] | 359 | |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 360 | case COMPONENT_PRODUCTION: |
| 361 | return new DependencyMethodProducerCreationExpression( |
| 362 | binding, generatedComponentModel, componentRequirementFields, graph); |
| dpb | 3db68f0 | 2018-01-04 09:15:52 -0800 | [diff] [blame] | 363 | |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 364 | case PRODUCTION: |
| bcorso | 5db065d | 2018-04-03 13:49:31 -0700 | [diff] [blame] | 365 | return new ProducerCreationExpression(binding, this); |
| dpb | 3db68f0 | 2018-01-04 09:15:52 -0800 | [diff] [blame] | 366 | |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 367 | case MULTIBOUND_SET: |
| 368 | return new SetFactoryCreationExpression(binding, generatedComponentModel, this, graph); |
| dpb | 3db68f0 | 2018-01-04 09:15:52 -0800 | [diff] [blame] | 369 | |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 370 | case MULTIBOUND_MAP: |
| 371 | return new MapFactoryCreationExpression(binding, generatedComponentModel, this, graph); |
| dpb | 3db68f0 | 2018-01-04 09:15:52 -0800 | [diff] [blame] | 372 | |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 373 | case RELEASABLE_REFERENCE_MANAGER: |
| 374 | return new ReleasableReferenceManagerProviderCreationExpression( |
| 375 | binding, generatedComponentModel, referenceReleasingManagerFields); |
| dpb | 3db68f0 | 2018-01-04 09:15:52 -0800 | [diff] [blame] | 376 | |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 377 | case RELEASABLE_REFERENCE_MANAGERS: |
| 378 | return new ReleasableReferenceManagerSetProviderCreationExpression( |
| 379 | binding, generatedComponentModel, referenceReleasingManagerFields, graph); |
| dpb | 3db68f0 | 2018-01-04 09:15:52 -0800 | [diff] [blame] | 380 | |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 381 | case DELEGATE: |
| 382 | return new DelegatingFrameworkInstanceCreationExpression( |
| 383 | binding, generatedComponentModel, this); |
| dpb | 3db68f0 | 2018-01-04 09:15:52 -0800 | [diff] [blame] | 384 | |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 385 | case OPTIONAL: |
| 386 | if (binding.explicitDependencies().isEmpty()) { |
| 387 | return () -> optionalFactories.absentOptionalProvider(binding); |
| 388 | } else { |
| 389 | return () -> |
| 390 | optionalFactories.presentOptionalFactory( |
| 391 | binding, |
| 392 | getDependencyExpression( |
| 393 | getOnlyElement(binding.frameworkDependencies()), |
| 394 | generatedComponentModel.name()) |
| 395 | .codeBlock()); |
| dpb | 356a684 | 2018-02-07 07:45:50 -0800 | [diff] [blame] | 396 | } |
| dpb | 356a684 | 2018-02-07 07:45:50 -0800 | [diff] [blame] | 397 | |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 398 | case MEMBERS_INJECTOR: |
| 399 | TypeMirror membersInjectedType = |
| 400 | getOnlyElement(MoreTypes.asDeclared(binding.key().type()).getTypeArguments()); |
| dpb | 356a684 | 2018-02-07 07:45:50 -0800 | [diff] [blame] | 401 | |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 402 | if (((ProvisionBinding) binding).injectionSites().isEmpty()) { |
| 403 | return new InstanceFactoryCreationExpression( |
| 404 | // The type parameter can be removed when we drop Java 7 source support. |
| 405 | () -> CodeBlock.of("$T.<$T>noOp()", MembersInjectors.class, membersInjectedType)); |
| 406 | } else { |
| 407 | return new InstanceFactoryCreationExpression( |
| dpb | 356a684 | 2018-02-07 07:45:50 -0800 | [diff] [blame] | 408 | () -> |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 409 | CodeBlock.of( |
| 410 | "$T.create($L)", |
| 411 | membersInjectorNameForType(MoreTypes.asTypeElement(membersInjectedType)), |
| bcorso | 5db065d | 2018-04-03 13:49:31 -0700 | [diff] [blame] | 412 | getCreateMethodArgumentsCodeBlock(binding))); |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 413 | } |
| 414 | |
| 415 | default: |
| 416 | throw new AssertionError(binding); |
| 417 | } |
| 418 | } |
| 419 | |
| 420 | private InstanceFactoryCreationExpression instanceFactoryCreationExpression( |
| 421 | ContributionBinding binding, ComponentRequirement componentRequirement) { |
| 422 | return new InstanceFactoryCreationExpression( |
| 423 | binding.nullableType().isPresent(), |
| 424 | () -> |
| 425 | componentRequirementFields.getExpressionDuringInitialization( |
| 426 | componentRequirement, generatedComponentModel.name())); |
| 427 | } |
| 428 | |
| 429 | /** Returns a binding expression for a provision binding. */ |
| 430 | private BindingExpression provisionBindingExpression( |
| 431 | ResolvedBindings resolvedBindings, RequestKind requestKind) { |
| 432 | switch (requestKind) { |
| 433 | case PRODUCER: |
| 434 | return producerFromProviderBindingExpression(resolvedBindings, requestKind); |
| 435 | |
| 436 | case INSTANCE: |
| 437 | return instanceBindingExpression(resolvedBindings); |
| 438 | |
| 439 | case FUTURE: |
| 440 | return new ImmediateFutureBindingExpression(resolvedBindings, this, types); |
| 441 | |
| 442 | case LAZY: |
| 443 | case PRODUCED: |
| 444 | case PROVIDER_OF_LAZY: |
| 445 | return new DerivedFromProviderBindingExpression(resolvedBindings, requestKind, this, types); |
| 446 | |
| 447 | case PROVIDER: |
| 448 | return providerBindingExpression(resolvedBindings); |
| 449 | |
| 450 | case MEMBERS_INJECTION: |
| 451 | throw new IllegalArgumentException(); |
| dpb | 356a684 | 2018-02-07 07:45:50 -0800 | [diff] [blame] | 452 | } |
| 453 | |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 454 | throw new AssertionError(); |
| 455 | } |
| dpb | 356a684 | 2018-02-07 07:45:50 -0800 | [diff] [blame] | 456 | |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 457 | /** |
| 458 | * Returns a binding expression that uses a {@link dagger.producers.Producer} field for a |
| 459 | * provision binding. |
| 460 | */ |
| 461 | private FrameworkInstanceBindingExpression producerFromProviderBindingExpression( |
| 462 | ResolvedBindings resolvedBindings, RequestKind requestKind) { |
| 463 | checkArgument(resolvedBindings.bindingType().frameworkType().equals(FrameworkType.PROVIDER)); |
| 464 | return new FrameworkInstanceBindingExpression( |
| 465 | resolvedBindings, |
| 466 | requestKind, |
| 467 | this, |
| 468 | FrameworkType.PRODUCER, |
| 469 | new FrameworkFieldInitializer( |
| 470 | generatedComponentModel, |
| 471 | resolvedBindings, |
| 472 | new ProducerFromProviderCreationExpression( |
| 473 | resolvedBindings.contributionBinding(), generatedComponentModel, this)), |
| 474 | types, |
| 475 | elements); |
| 476 | } |
| dpb | 356a684 | 2018-02-07 07:45:50 -0800 | [diff] [blame] | 477 | |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 478 | /** |
| 479 | * Returns a binding expression for {@link RequestKind#INSTANCE} requests. |
| 480 | * |
| 481 | * <p>If there is a direct expression (not calling {@link Provider#get()}) we can use for an |
| 482 | * instance of this binding, return it, wrapped in a method if the binding {@linkplain |
| 483 | * #needsCaching(ResolvedBindings) needs to be cached} or the expression has dependencies. |
| 484 | * |
| 485 | * <p>In default mode, we can use direct expressions for bindings that don't need to be cached in |
| 486 | * a reference-releasing scope. |
| 487 | * |
| 488 | * <p>In Android mode, we can use direct expressions unless the binding needs to be cached. |
| 489 | */ |
| 490 | private BindingExpression instanceBindingExpression(ResolvedBindings resolvedBindings) { |
| 491 | Optional<BindingExpression> maybeDirectInstanceExpression = |
| 492 | unscopedDirectInstanceExpression(resolvedBindings); |
| 493 | if (canUseDirectInstanceExpression(resolvedBindings) |
| 494 | && maybeDirectInstanceExpression.isPresent()) { |
| 495 | BindingExpression directInstanceExpression = maybeDirectInstanceExpression.get(); |
| 496 | return directInstanceExpression.requiresMethodEncapsulation() |
| 497 | || needsCaching(resolvedBindings) |
| 498 | ? wrapInMethod(resolvedBindings, RequestKind.INSTANCE, directInstanceExpression) |
| 499 | : directInstanceExpression; |
| dpb | 356a684 | 2018-02-07 07:45:50 -0800 | [diff] [blame] | 500 | } |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 501 | return new DerivedFromProviderBindingExpression( |
| 502 | resolvedBindings, RequestKind.INSTANCE, this, types); |
| 503 | } |
| dpb | 356a684 | 2018-02-07 07:45:50 -0800 | [diff] [blame] | 504 | |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 505 | /** |
| 506 | * Returns an unscoped binding expression for an {@link RequestKind#INSTANCE} that does not call |
| 507 | * {@code get()} on its provider, if there is one. |
| 508 | */ |
| 509 | private Optional<BindingExpression> unscopedDirectInstanceExpression( |
| 510 | ResolvedBindings resolvedBindings) { |
| 511 | switch (resolvedBindings.contributionBinding().kind()) { |
| 512 | case DELEGATE: |
| 513 | return Optional.of( |
| 514 | new DelegateBindingExpression( |
| 515 | resolvedBindings, RequestKind.INSTANCE, this, types, elements)); |
| 516 | |
| 517 | case COMPONENT: |
| 518 | return Optional.of( |
| 519 | new ComponentInstanceBindingExpression( |
| 520 | resolvedBindings, generatedComponentModel.name())); |
| 521 | |
| 522 | case COMPONENT_DEPENDENCY: |
| 523 | return Optional.of( |
| 524 | new ComponentRequirementBindingExpression( |
| 525 | resolvedBindings, |
| 526 | ComponentRequirement.forDependency(resolvedBindings.key().type()), |
| 527 | componentRequirementFields)); |
| 528 | |
| 529 | case COMPONENT_PROVISION: |
| 530 | return Optional.of( |
| 531 | new ComponentProvisionBindingExpression( |
| 532 | resolvedBindings, graph, componentRequirementFields, compilerOptions)); |
| 533 | |
| 534 | case SUBCOMPONENT_BUILDER: |
| 535 | return Optional.of( |
| 536 | new SubcomponentBuilderBindingExpression( |
| 537 | resolvedBindings, subcomponentNames.get(resolvedBindings.key()))); |
| 538 | |
| 539 | case MULTIBOUND_SET: |
| 540 | return Optional.of( |
| 541 | new SetBindingExpression(resolvedBindings, graph, this, types, elements)); |
| 542 | |
| 543 | case MULTIBOUND_MAP: |
| 544 | return Optional.of( |
| 545 | new MapBindingExpression(resolvedBindings, graph, this, types, elements)); |
| 546 | |
| 547 | case OPTIONAL: |
| 548 | return Optional.of(new OptionalBindingExpression(resolvedBindings, this, types)); |
| 549 | |
| 550 | case BOUND_INSTANCE: |
| 551 | return Optional.of( |
| 552 | new ComponentRequirementBindingExpression( |
| 553 | resolvedBindings, |
| 554 | ComponentRequirement.forBoundInstance(resolvedBindings.contributionBinding()), |
| 555 | componentRequirementFields)); |
| 556 | |
| 557 | case INJECTION: |
| 558 | case PROVISION: |
| 559 | return Optional.of( |
| 560 | new SimpleMethodBindingExpression( |
| 561 | resolvedBindings, |
| 562 | compilerOptions, |
| 563 | this, |
| 564 | membersInjectionMethods, |
| 565 | componentRequirementFields, |
| 566 | elements)); |
| 567 | |
| 568 | case MEMBERS_INJECTOR: |
| 569 | case RELEASABLE_REFERENCE_MANAGER: |
| 570 | case RELEASABLE_REFERENCE_MANAGERS: |
| 571 | // TODO(dpb): Implement direct expressions for these. |
| 572 | return Optional.empty(); |
| 573 | |
| 574 | case MEMBERS_INJECTION: |
| 575 | case COMPONENT_PRODUCTION: |
| 576 | case PRODUCTION: |
| 577 | throw new IllegalArgumentException( |
| 578 | resolvedBindings.contributionBinding().kind().toString()); |
| 579 | } |
| 580 | throw new AssertionError(); |
| 581 | } |
| 582 | |
| 583 | /** |
| bcorso | af9e004 | 2018-04-16 14:12:23 -0700 | [diff] [blame] | 584 | * Returns {@code true} if the binding should use the static factory creation strategy. |
| 585 | * |
| 586 | * In default mode, we always use the static factory creation strategy. In Android mode, we |
| 587 | * prefer to use the SwitchingProvider than the static factories to reduce class loading; however, |
| 588 | * we allow static factories that can reused across multiple bindings, e.g. {@code MapFactory} or |
| 589 | * {@code SetFactory}. |
| 590 | */ |
| 591 | private boolean useStaticFactoryCreation(ContributionBinding binding) { |
| 592 | return !compilerOptions.experimentalAndroidMode() |
| 593 | || binding.kind().equals(MULTIBOUND_MAP) |
| 594 | || binding.kind().equals(MULTIBOUND_SET); |
| 595 | } |
| 596 | |
| 597 | /** |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 598 | * Returns {@code true} if we can use a direct (not {@code Provider.get()}) expression for this |
| 599 | * binding. If the binding doesn't {@linkplain #needsCaching(ResolvedBindings) need to be cached}, |
| 600 | * we can. |
| 601 | * |
| 602 | * <p>In Android mode, we can use a direct expression even if the binding {@linkplain |
| 603 | * #needsCaching(ResolvedBindings) needs to be cached} as long as it's not in a |
| 604 | * reference-releasing scope. |
| 605 | */ |
| 606 | private boolean canUseDirectInstanceExpression(ResolvedBindings resolvedBindings) { |
| 607 | return !needsCaching(resolvedBindings) |
| 608 | || (compilerOptions.experimentalAndroidMode() |
| 609 | && !requiresReleasableReferences(resolvedBindings)); |
| 610 | } |
| 611 | |
| 612 | /** |
| 613 | * Returns a binding expression for {@link RequestKind#PROVIDER} requests. |
| 614 | * |
| 615 | * <p>In default mode, {@code @Binds} bindings that don't {@linkplain |
| 616 | * #needsCaching(ResolvedBindings) need to be cached} can use a {@link DelegateBindingExpression}. |
| 617 | * |
| 618 | * <p>In Android mode, if {@linkplain #instanceBindingExpression(ResolvedBindings) instance |
| 619 | * binding expressions} don't call {@code Provider.get()} on the provider binding expression, and |
| bcorso | 8a8039a | 2018-03-19 09:36:26 -0700 | [diff] [blame] | 620 | * there's no simple factory, then return a {@link SwitchingProviders} binding expression wrapped |
| 621 | * in a method. |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 622 | * |
| 623 | * <p>Otherwise, return a {@link FrameworkInstanceBindingExpression}. |
| 624 | */ |
| 625 | private BindingExpression providerBindingExpression(ResolvedBindings resolvedBindings) { |
| 626 | if (compilerOptions.experimentalAndroidMode()) { |
| bcorso | 8a8039a | 2018-03-19 09:36:26 -0700 | [diff] [blame] | 627 | if (!frameworkInstanceCreationExpression(resolvedBindings).isSimpleFactory() |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 628 | && !(instanceBindingExpression(resolvedBindings) |
| 629 | instanceof DerivedFromProviderBindingExpression)) { |
| 630 | return wrapInMethod( |
| 631 | resolvedBindings, |
| 632 | RequestKind.PROVIDER, |
| bcorso | af9e004 | 2018-04-16 14:12:23 -0700 | [diff] [blame] | 633 | switchingProviders.newBindingExpression(resolvedBindings.contributionBinding())); |
| bcorso | 3828ca2 | 2018-01-29 10:14:12 -0800 | [diff] [blame] | 634 | } |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 635 | } else if (resolvedBindings.contributionBinding().kind().equals(DELEGATE) |
| 636 | && !needsCaching(resolvedBindings)) { |
| 637 | return new DelegateBindingExpression( |
| 638 | resolvedBindings, RequestKind.PROVIDER, this, types, elements); |
| bcorso | f0a99fe | 2017-12-04 10:52:05 -0800 | [diff] [blame] | 639 | } |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 640 | return frameworkInstanceBindingExpression(resolvedBindings, RequestKind.PROVIDER); |
| 641 | } |
| bcorso | 3828ca2 | 2018-01-29 10:14:12 -0800 | [diff] [blame] | 642 | |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 643 | /** |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 644 | * Returns a binding expression that uses a given one as the body of a method that users call. If |
| 645 | * a component provision method matches it, it will be the method implemented. If not, a new |
| 646 | * private method will be written. |
| 647 | */ |
| 648 | private BindingExpression wrapInMethod( |
| 649 | ResolvedBindings resolvedBindings, |
| 650 | RequestKind requestKind, |
| 651 | BindingExpression bindingExpression) { |
| 652 | BindingMethodImplementation methodImplementation = |
| 653 | methodImplementation(resolvedBindings, requestKind, bindingExpression); |
| 654 | |
| 655 | return findMatchingComponentMethod(resolvedBindings.key(), requestKind) |
| 656 | .<BindingExpression>map( |
| 657 | componentMethod -> |
| 658 | new ComponentMethodBindingExpression( |
| dpb | d768576 | 2018-02-16 12:20:09 -0800 | [diff] [blame] | 659 | methodImplementation, generatedComponentModel, componentMethod)) |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 660 | .orElseGet( |
| 661 | () -> |
| 662 | new PrivateMethodBindingExpression( |
| 663 | resolvedBindings, requestKind, methodImplementation, generatedComponentModel)); |
| 664 | } |
| 665 | |
| 666 | /** Returns the first component method associated with this request kind, if one exists. */ |
| 667 | private Optional<ComponentMethodDescriptor> findMatchingComponentMethod( |
| 668 | Key key, RequestKind requestKind) { |
| 669 | return graph |
| 670 | .componentDescriptor() |
| 671 | .componentMethods() |
| 672 | .stream() |
| 673 | .filter(method -> doesComponentMethodMatch(method, key, requestKind)) |
| 674 | .findFirst(); |
| 675 | } |
| 676 | |
| 677 | /** Returns true if the component method matches the dependency request binding key and kind. */ |
| 678 | private boolean doesComponentMethodMatch( |
| 679 | ComponentMethodDescriptor componentMethod, Key key, RequestKind requestKind) { |
| 680 | return componentMethod |
| 681 | .dependencyRequest() |
| 682 | .filter(request -> request.key().equals(key)) |
| 683 | .filter(request -> request.kind().equals(requestKind)) |
| 684 | .isPresent(); |
| 685 | } |
| 686 | |
| 687 | private BindingMethodImplementation methodImplementation( |
| 688 | ResolvedBindings resolvedBindings, |
| 689 | RequestKind requestKind, |
| 690 | BindingExpression bindingExpression) { |
| bcorso | 567ff0e | 2018-03-21 13:49:09 -0700 | [diff] [blame] | 691 | if (compilerOptions.experimentalAndroidMode()) { |
| 692 | if (requestKind.equals(RequestKind.PROVIDER)) { |
| 693 | return new SingleCheckedMethodImplementation( |
| 694 | resolvedBindings, requestKind, bindingExpression, types, generatedComponentModel); |
| 695 | } else if (requestKind.equals(RequestKind.INSTANCE) && needsCaching(resolvedBindings)) { |
| 696 | return resolvedBindings.scope().get().isReusable() |
| 697 | ? new SingleCheckedMethodImplementation( |
| 698 | resolvedBindings, requestKind, bindingExpression, types, generatedComponentModel) |
| 699 | : new DoubleCheckedMethodImplementation( |
| 700 | resolvedBindings, requestKind, bindingExpression, types, generatedComponentModel); |
| 701 | } |
| 702 | } |
| 703 | |
| 704 | return new BindingMethodImplementation( |
| 705 | resolvedBindings, requestKind, bindingExpression, generatedComponentModel.name(), types); |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 706 | } |
| 707 | |
| 708 | /** |
| 709 | * Returns {@code true} if the component needs to make sure the provided value is cached. |
| 710 | * |
| 711 | * <p>The component needs to cache the value for scoped bindings except for {@code @Binds} |
| 712 | * bindings whose scope is no stronger than their delegate's. |
| 713 | */ |
| 714 | private boolean needsCaching(ResolvedBindings resolvedBindings) { |
| 715 | if (!resolvedBindings.scope().isPresent()) { |
| 716 | return false; |
| bcorso | 3828ca2 | 2018-01-29 10:14:12 -0800 | [diff] [blame] | 717 | } |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 718 | if (resolvedBindings.contributionBinding().kind().equals(DELEGATE)) { |
| 719 | return isBindsScopeStrongerThanDependencyScope(resolvedBindings, graph); |
| 720 | } |
| 721 | return true; |
| 722 | } |
| 723 | |
| 724 | // TODO(user): Enable releasable references in experimentalAndroidMode |
| 725 | private boolean requiresReleasableReferences(ResolvedBindings resolvedBindings) { |
| 726 | return resolvedBindings.scope().isPresent() |
| 727 | && referenceReleasingManagerFields.requiresReleasableReferences( |
| 728 | resolvedBindings.scope().get()); |
| dpb | 68c77d8 | 2017-08-15 15:23:35 -0700 | [diff] [blame] | 729 | } |
| 730 | } |