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