| 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; |
| dstrasburg | 34eeece | 2018-08-22 07:49:08 -0700 | [diff] [blame] | 21 | import static com.google.common.base.Preconditions.checkState; |
| 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.TypeNames.DOUBLE_CHECK; |
| 29 | import static dagger.internal.codegen.TypeNames.REFERENCE_RELEASING_PROVIDER; |
| 30 | import static dagger.internal.codegen.TypeNames.SINGLE_CHECK; |
| dpb | 09fb2cb | 2018-01-29 08:39:33 -0800 | [diff] [blame] | 31 | import static dagger.model.BindingKind.DELEGATE; |
| bcorso | af9e004 | 2018-04-16 14:12:23 -0700 | [diff] [blame] | 32 | import static dagger.model.BindingKind.MULTIBOUND_MAP; |
| 33 | import static dagger.model.BindingKind.MULTIBOUND_SET; |
| dstrasburg | cf5e1bc | 2018-08-09 09:03:38 -0700 | [diff] [blame] | 34 | import static javax.lang.model.element.Modifier.PUBLIC; |
| 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; |
| dpb | 3db68f0 | 2018-01-04 09:15:52 -0800 | [diff] [blame] | 37 | import com.google.common.collect.ImmutableList; |
| dpb | 68c77d8 | 2017-08-15 15:23:35 -0700 | [diff] [blame] | 38 | import com.squareup.javapoet.ClassName; |
| bcorso | e643cc6 | 2017-10-24 08:57:09 -0700 | [diff] [blame] | 39 | import com.squareup.javapoet.CodeBlock; |
| dpb | 02db213 | 2018-01-08 07:20:23 -0800 | [diff] [blame] | 40 | import com.squareup.javapoet.MethodSpec; |
| bcorso | 2c27966 | 2017-12-01 09:51:18 -0800 | [diff] [blame] | 41 | import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor; |
| dpb | 3db68f0 | 2018-01-04 09:15:52 -0800 | [diff] [blame] | 42 | import dagger.internal.codegen.FrameworkFieldInitializer.FrameworkInstanceCreationExpression; |
| dstrasburg | 2e206ec | 2018-08-21 13:00:27 -0700 | [diff] [blame] | 43 | import dagger.internal.codegen.ModifiableBindingMethods.ModifiableBindingMethod; |
| dstrasburg | 6d2a7ad | 2018-09-05 08:05:51 -0700 | [diff] [blame] | 44 | import dagger.model.BindingKind; |
| 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; |
| cgdecker | 60a5dde | 2018-09-07 08:44:34 -0700 | [diff] [blame] | 48 | import java.util.HashMap; |
| 49 | import java.util.Map; |
| bcorso | f0a99fe | 2017-12-04 10:52:05 -0800 | [diff] [blame] | 50 | import java.util.Optional; |
| dpb | 356a684 | 2018-02-07 07:45:50 -0800 | [diff] [blame] | 51 | import javax.inject.Provider; |
| dpb | 68c77d8 | 2017-08-15 15:23:35 -0700 | [diff] [blame] | 52 | import javax.lang.model.type.TypeMirror; |
| 53 | |
| ronshapiro | dc07ed5 | 2017-08-23 08:52:10 -0700 | [diff] [blame] | 54 | /** 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] | 55 | final class ComponentBindingExpressions { |
| 56 | |
| ronshapiro | dc07ed5 | 2017-08-23 08:52:10 -0700 | [diff] [blame] | 57 | // TODO(dpb,ronshapiro): refactor this and ComponentRequirementFields into a |
| 58 | // HierarchicalComponentMap<K, V>, or perhaps this use a flattened ImmutableMap, built from its |
| 59 | // parents? If so, maybe make BindingExpression.Factory create it. |
| dpb | 68c77d8 | 2017-08-15 15:23:35 -0700 | [diff] [blame] | 60 | |
| bcorso | f0a99fe | 2017-12-04 10:52:05 -0800 | [diff] [blame] | 61 | private final Optional<ComponentBindingExpressions> parent; |
| 62 | private final BindingGraph graph; |
| dpb | 02db213 | 2018-01-08 07:20:23 -0800 | [diff] [blame] | 63 | private final GeneratedComponentModel generatedComponentModel; |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 64 | private final SubcomponentNames subcomponentNames; |
| 65 | private final ComponentRequirementFields componentRequirementFields; |
| 66 | private final ReferenceReleasingManagerFields referenceReleasingManagerFields; |
| 67 | private final OptionalFactories optionalFactories; |
| bcorso | f0a99fe | 2017-12-04 10:52:05 -0800 | [diff] [blame] | 68 | private final DaggerTypes types; |
| dpb | e7b8958 | 2018-02-19 08:42:19 -0800 | [diff] [blame] | 69 | private final DaggerElements elements; |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 70 | private final CompilerOptions compilerOptions; |
| 71 | private final MembersInjectionMethods membersInjectionMethods; |
| bcorso | cc739fe | 2018-04-23 09:16:12 -0700 | [diff] [blame] | 72 | private final InnerSwitchingProviders innerSwitchingProviders; |
| 73 | private final StaticSwitchingProviders staticSwitchingProviders; |
| cgdecker | 60a5dde | 2018-09-07 08:44:34 -0700 | [diff] [blame] | 74 | private final Map<BindingRequest, BindingExpression> expressions = new HashMap<>(); |
| dpb | 68c77d8 | 2017-08-15 15:23:35 -0700 | [diff] [blame] | 75 | |
| bcorso | f0a99fe | 2017-12-04 10:52:05 -0800 | [diff] [blame] | 76 | ComponentBindingExpressions( |
| 77 | BindingGraph graph, |
| 78 | GeneratedComponentModel generatedComponentModel, |
| 79 | SubcomponentNames subcomponentNames, |
| 80 | ComponentRequirementFields componentRequirementFields, |
| 81 | OptionalFactories optionalFactories, |
| 82 | DaggerTypes types, |
| dpb | e7b8958 | 2018-02-19 08:42:19 -0800 | [diff] [blame] | 83 | DaggerElements elements, |
| bcorso | f0a99fe | 2017-12-04 10:52:05 -0800 | [diff] [blame] | 84 | CompilerOptions compilerOptions) { |
| 85 | this( |
| 86 | Optional.empty(), |
| 87 | graph, |
| 88 | generatedComponentModel, |
| 89 | subcomponentNames, |
| 90 | componentRequirementFields, |
| dstrasburg | dd03682 | 2018-07-23 08:00:37 -0700 | [diff] [blame] | 91 | new ReferenceReleasingManagerFields(graph, generatedComponentModel, compilerOptions), |
| bcorso | cc739fe | 2018-04-23 09:16:12 -0700 | [diff] [blame] | 92 | new StaticSwitchingProviders(generatedComponentModel, types), |
| bcorso | f0a99fe | 2017-12-04 10:52:05 -0800 | [diff] [blame] | 93 | optionalFactories, |
| 94 | types, |
| 95 | elements, |
| 96 | compilerOptions); |
| dpb | 68c77d8 | 2017-08-15 15:23:35 -0700 | [diff] [blame] | 97 | } |
| 98 | |
| bcorso | f0a99fe | 2017-12-04 10:52:05 -0800 | [diff] [blame] | 99 | private ComponentBindingExpressions( |
| 100 | Optional<ComponentBindingExpressions> parent, |
| 101 | BindingGraph graph, |
| 102 | GeneratedComponentModel generatedComponentModel, |
| 103 | SubcomponentNames subcomponentNames, |
| 104 | ComponentRequirementFields componentRequirementFields, |
| 105 | ReferenceReleasingManagerFields referenceReleasingManagerFields, |
| bcorso | cc739fe | 2018-04-23 09:16:12 -0700 | [diff] [blame] | 106 | StaticSwitchingProviders staticSwitchingProviders, |
| bcorso | f0a99fe | 2017-12-04 10:52:05 -0800 | [diff] [blame] | 107 | OptionalFactories optionalFactories, |
| 108 | DaggerTypes types, |
| dpb | e7b8958 | 2018-02-19 08:42:19 -0800 | [diff] [blame] | 109 | DaggerElements elements, |
| bcorso | f0a99fe | 2017-12-04 10:52:05 -0800 | [diff] [blame] | 110 | CompilerOptions compilerOptions) { |
| 111 | this.parent = parent; |
| 112 | this.graph = graph; |
| dpb | 02db213 | 2018-01-08 07:20:23 -0800 | [diff] [blame] | 113 | this.generatedComponentModel = generatedComponentModel; |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 114 | this.subcomponentNames = checkNotNull(subcomponentNames); |
| 115 | this.componentRequirementFields = checkNotNull(componentRequirementFields); |
| 116 | this.referenceReleasingManagerFields = checkNotNull(referenceReleasingManagerFields); |
| 117 | this.optionalFactories = checkNotNull(optionalFactories); |
| 118 | this.types = checkNotNull(types); |
| 119 | this.elements = checkNotNull(elements); |
| 120 | this.compilerOptions = checkNotNull(compilerOptions); |
| 121 | this.membersInjectionMethods = |
| 122 | new MembersInjectionMethods(generatedComponentModel, this, graph, elements, types); |
| bcorso | cc739fe | 2018-04-23 09:16:12 -0700 | [diff] [blame] | 123 | this.innerSwitchingProviders = |
| 124 | new InnerSwitchingProviders(generatedComponentModel, this, types); |
| 125 | this.staticSwitchingProviders = staticSwitchingProviders; |
| bcorso | f0a99fe | 2017-12-04 10:52:05 -0800 | [diff] [blame] | 126 | } |
| 127 | |
| 128 | /** |
| 129 | * Returns a new object representing the bindings available from a child component of this one. |
| 130 | */ |
| 131 | ComponentBindingExpressions forChildComponent( |
| 132 | BindingGraph childGraph, |
| 133 | GeneratedComponentModel childComponentModel, |
| 134 | ComponentRequirementFields childComponentRequirementFields) { |
| 135 | return new ComponentBindingExpressions( |
| 136 | Optional.of(this), |
| 137 | childGraph, |
| 138 | childComponentModel, |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 139 | subcomponentNames, |
| bcorso | f0a99fe | 2017-12-04 10:52:05 -0800 | [diff] [blame] | 140 | childComponentRequirementFields, |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 141 | referenceReleasingManagerFields, |
| bcorso | cc739fe | 2018-04-23 09:16:12 -0700 | [diff] [blame] | 142 | staticSwitchingProviders, |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 143 | optionalFactories, |
| 144 | types, |
| 145 | elements, |
| 146 | compilerOptions); |
| dpb | 68c77d8 | 2017-08-15 15:23:35 -0700 | [diff] [blame] | 147 | } |
| 148 | |
| 149 | /** |
| 150 | * Returns an expression that evaluates to the value of a dependency request for a binding owned |
| 151 | * by this component or an ancestor. |
| 152 | * |
| 153 | * @param requestingClass the class that will contain the expression |
| 154 | * @throws IllegalStateException if there is no binding expression that satisfies the dependency |
| 155 | * request |
| 156 | */ |
| ronshapiro | 0a277fd | 2017-12-22 08:30:49 -0800 | [diff] [blame] | 157 | Expression getDependencyExpression(Key key, RequestKind requestKind, ClassName requestingClass) { |
| cgdecker | 60a5dde | 2018-09-07 08:44:34 -0700 | [diff] [blame] | 158 | return getDependencyExpression( |
| 159 | BindingRequest.forDependencyRequest(key, requestKind), requestingClass); |
| ronshapiro | ba79486 | 2017-09-28 11:39:34 -0700 | [diff] [blame] | 160 | } |
| 161 | |
| 162 | /** |
| 163 | * Returns an expression that evaluates to the value of a dependency request for a binding owned |
| 164 | * by this component or an ancestor. |
| 165 | * |
| 166 | * @param requestingClass the class that will contain the expression |
| 167 | * @throws IllegalStateException if there is no binding expression that satisfies the dependency |
| 168 | * request |
| 169 | */ |
| 170 | Expression getDependencyExpression(DependencyRequest request, ClassName requestingClass) { |
| cgdecker | 60a5dde | 2018-09-07 08:44:34 -0700 | [diff] [blame] | 171 | return getDependencyExpression(BindingRequest.forDependencyRequest(request), requestingClass); |
| dpb | 68c77d8 | 2017-08-15 15:23:35 -0700 | [diff] [blame] | 172 | } |
| 173 | |
| 174 | /** |
| 175 | * Returns an expression that evaluates to the value of a framework dependency for a binding owned |
| 176 | * in this component or an ancestor. |
| 177 | * |
| 178 | * @param requestingClass the class that will contain the expression |
| 179 | * @throws IllegalStateException if there is no binding expression that satisfies the dependency |
| 180 | * request |
| 181 | */ |
| ronshapiro | ba79486 | 2017-09-28 11:39:34 -0700 | [diff] [blame] | 182 | Expression getDependencyExpression( |
| dpb | 68c77d8 | 2017-08-15 15:23:35 -0700 | [diff] [blame] | 183 | FrameworkDependency frameworkDependency, ClassName requestingClass) { |
| ronshapiro | ba79486 | 2017-09-28 11:39:34 -0700 | [diff] [blame] | 184 | return getDependencyExpression( |
| cgdecker | 60a5dde | 2018-09-07 08:44:34 -0700 | [diff] [blame] | 185 | BindingRequest.forFrameworkDependency(frameworkDependency), requestingClass); |
| 186 | } |
| 187 | |
| 188 | /** |
| 189 | * Returns an expression that evaluates to the value of a framework request for a binding owned by |
| 190 | * this component or an ancestor. |
| 191 | * |
| 192 | * @param requestingClass the class that will contain the expression |
| 193 | * @throws IllegalStateException if there is no binding expression that satisfies the framework |
| 194 | * request |
| 195 | */ |
| 196 | Expression getDependencyExpression( |
| 197 | Key key, FrameworkType frameworkType, ClassName requestingClass) { |
| 198 | return getDependencyExpression( |
| 199 | BindingRequest.forFrameworkDependency(key, frameworkType), requestingClass); |
| 200 | } |
| 201 | |
| 202 | private Expression getDependencyExpression(BindingRequest request, ClassName requestingClass) { |
| 203 | return getBindingExpression(request).getDependencyExpression(requestingClass); |
| dpb | 68c77d8 | 2017-08-15 15:23:35 -0700 | [diff] [blame] | 204 | } |
| 205 | |
| 206 | /** |
| bcorso | 5db065d | 2018-04-03 13:49:31 -0700 | [diff] [blame] | 207 | * Returns the {@link CodeBlock} for the method argmuments used with the factory {@code create()} |
| 208 | * method for the given {@link ContributionBinding binding}. |
| dpb | 3db68f0 | 2018-01-04 09:15:52 -0800 | [diff] [blame] | 209 | */ |
| bcorso | 5db065d | 2018-04-03 13:49:31 -0700 | [diff] [blame] | 210 | CodeBlock getCreateMethodArgumentsCodeBlock(ContributionBinding binding) { |
| 211 | return makeParametersCodeBlock(getCreateMethodArgumentsCodeBlocks(binding)); |
| 212 | } |
| 213 | |
| 214 | private ImmutableList<CodeBlock> getCreateMethodArgumentsCodeBlocks(ContributionBinding binding) { |
| 215 | ImmutableList.Builder<CodeBlock> arguments = ImmutableList.builder(); |
| 216 | |
| 217 | if (binding.requiresModuleInstance()) { |
| 218 | arguments.add( |
| 219 | componentRequirementFields.getExpressionDuringInitialization( |
| 220 | ComponentRequirement.forModule(binding.contributingModule().get().asType()), |
| 221 | generatedComponentModel.name())); |
| 222 | } |
| 223 | |
| 224 | binding |
| 225 | .frameworkDependencies() |
| dpb | 3db68f0 | 2018-01-04 09:15:52 -0800 | [diff] [blame] | 226 | .stream() |
| bcorso | 5db065d | 2018-04-03 13:49:31 -0700 | [diff] [blame] | 227 | .map(dependency -> getDependencyExpression(dependency, generatedComponentModel.name())) |
| 228 | .map(Expression::codeBlock) |
| 229 | .forEach(arguments::add); |
| 230 | |
| 231 | return arguments.build(); |
| dpb | 3db68f0 | 2018-01-04 09:15:52 -0800 | [diff] [blame] | 232 | } |
| 233 | |
| 234 | /** |
| dpb | 68c77d8 | 2017-08-15 15:23:35 -0700 | [diff] [blame] | 235 | * Returns an expression that evaluates to the value of a dependency request, for passing to a |
| 236 | * binding method, an {@code @Inject}-annotated constructor or member, or a proxy for one. |
| 237 | * |
| 238 | * <p>If the method is a generated static {@link InjectionMethods injection method}, each |
| 239 | * parameter will be {@link Object} if the dependency's raw type is inaccessible. If that is the |
| 240 | * case for this dependency, the returned expression will use a cast to evaluate to the raw type. |
| 241 | * |
| 242 | * @param requestingClass the class that will contain the expression |
| 243 | */ |
| 244 | // TODO(b/64024402) Merge with getDependencyExpression(DependencyRequest, ClassName) if possible. |
| ronshapiro | ba79486 | 2017-09-28 11:39:34 -0700 | [diff] [blame] | 245 | Expression getDependencyArgumentExpression( |
| dpb | 68c77d8 | 2017-08-15 15:23:35 -0700 | [diff] [blame] | 246 | DependencyRequest dependencyRequest, ClassName requestingClass) { |
| dpb | 68c77d8 | 2017-08-15 15:23:35 -0700 | [diff] [blame] | 247 | |
| 248 | TypeMirror dependencyType = dependencyRequest.key().type(); |
| ronshapiro | ba79486 | 2017-09-28 11:39:34 -0700 | [diff] [blame] | 249 | Expression dependencyExpression = getDependencyExpression(dependencyRequest, requestingClass); |
| 250 | |
| bcorso | aaffd2d | 2018-04-16 14:44:42 -0700 | [diff] [blame] | 251 | if (dependencyRequest.kind().equals(RequestKind.INSTANCE) |
| 252 | && !isTypeAccessibleFrom(dependencyType, requestingClass.packageName()) |
| dpb | 68c77d8 | 2017-08-15 15:23:35 -0700 | [diff] [blame] | 253 | && isRawTypeAccessible(dependencyType, requestingClass.packageName())) { |
| ronshapiro | ba79486 | 2017-09-28 11:39:34 -0700 | [diff] [blame] | 254 | return dependencyExpression.castTo(types.erasure(dependencyType)); |
| dpb | 68c77d8 | 2017-08-15 15:23:35 -0700 | [diff] [blame] | 255 | } |
| 256 | |
| ronshapiro | ba79486 | 2017-09-28 11:39:34 -0700 | [diff] [blame] | 257 | return dependencyExpression; |
| dpb | 68c77d8 | 2017-08-15 15:23:35 -0700 | [diff] [blame] | 258 | } |
| 259 | |
| dstrasburg | 34eeece | 2018-08-22 07:49:08 -0700 | [diff] [blame] | 260 | /** |
| 261 | * Returns the implementation of a component method. Returns {@link Optional#empty} if the |
| 262 | * component method implementation should not be emitted. |
| 263 | */ |
| 264 | Optional<MethodSpec> getComponentMethod(ComponentMethodDescriptor componentMethod) { |
| dpb | 02db213 | 2018-01-08 07:20:23 -0800 | [diff] [blame] | 265 | checkArgument(componentMethod.dependencyRequest().isPresent()); |
| cgdecker | 60a5dde | 2018-09-07 08:44:34 -0700 | [diff] [blame] | 266 | BindingRequest request = |
| 267 | BindingRequest.forDependencyRequest(componentMethod.dependencyRequest().get()); |
| dstrasburg | 6d2a7ad | 2018-09-05 08:05:51 -0700 | [diff] [blame] | 268 | MethodSpec method = |
| dstrasburg | 34eeece | 2018-08-22 07:49:08 -0700 | [diff] [blame] | 269 | MethodSpec.overriding( |
| dstrasburg | 6d2a7ad | 2018-09-05 08:05:51 -0700 | [diff] [blame] | 270 | componentMethod.methodElement(), |
| 271 | MoreTypes.asDeclared(graph.componentType().asType()), |
| 272 | types) |
| 273 | .addCode( |
| cgdecker | 60a5dde | 2018-09-07 08:44:34 -0700 | [diff] [blame] | 274 | getBindingExpression(request) |
| dstrasburg | 6d2a7ad | 2018-09-05 08:05:51 -0700 | [diff] [blame] | 275 | .getComponentMethodImplementation(componentMethod, generatedComponentModel)) |
| 276 | .build(); |
| dstrasburg | 34eeece | 2018-08-22 07:49:08 -0700 | [diff] [blame] | 277 | |
| cgdecker | 60a5dde | 2018-09-07 08:44:34 -0700 | [diff] [blame] | 278 | ModifiableBindingType modifiableBindingType = getModifiableBindingType(request); |
| dstrasburg | 6d2a7ad | 2018-09-05 08:05:51 -0700 | [diff] [blame] | 279 | if (modifiableBindingType.isModifiable()) { |
| dstrasburg | 34eeece | 2018-08-22 07:49:08 -0700 | [diff] [blame] | 280 | generatedComponentModel.registerModifiableBindingMethod( |
| dstrasburg | 6d2a7ad | 2018-09-05 08:05:51 -0700 | [diff] [blame] | 281 | modifiableBindingType, |
| cgdecker | 60a5dde | 2018-09-07 08:44:34 -0700 | [diff] [blame] | 282 | request, |
| dstrasburg | 6d2a7ad | 2018-09-05 08:05:51 -0700 | [diff] [blame] | 283 | method, |
| cgdecker | 60a5dde | 2018-09-07 08:44:34 -0700 | [diff] [blame] | 284 | newModifiableBindingWillBeFinalized(modifiableBindingType, request)); |
| dstrasburg | 6d2a7ad | 2018-09-05 08:05:51 -0700 | [diff] [blame] | 285 | if (!modifiableBindingType.hasBaseClassImplementation()) { |
| dstrasburg | 34eeece | 2018-08-22 07:49:08 -0700 | [diff] [blame] | 286 | // A component method should not be emitted if it encapsulates a modifiable binding that |
| 287 | // cannot be satisfied by the abstract base class implementation of a subcomponent. |
| 288 | checkState( |
| 289 | !generatedComponentModel.supermodel().isPresent(), |
| 290 | "Attempting to generate a component method in a subtype of the abstract subcomponent " |
| 291 | + "base class."); |
| 292 | return Optional.empty(); |
| 293 | } |
| 294 | } |
| 295 | |
| dstrasburg | 6d2a7ad | 2018-09-05 08:05:51 -0700 | [diff] [blame] | 296 | return Optional.of(method); |
| bcorso | 11f9b87 | 2017-10-09 16:18:55 -0700 | [diff] [blame] | 297 | } |
| 298 | |
| dstrasburg | cf5e1bc | 2018-08-09 09:03:38 -0700 | [diff] [blame] | 299 | /** |
| dstrasburg | 6d2a7ad | 2018-09-05 08:05:51 -0700 | [diff] [blame] | 300 | * Returns the implementation of a modifiable binding method originally defined in a supertype |
| dstrasburg | cf5e1bc | 2018-08-09 09:03:38 -0700 | [diff] [blame] | 301 | * implementation of this subcomponent. Returns {@link Optional#empty()} when the binding cannot |
| dstrasburg | 2e206ec | 2018-08-21 13:00:27 -0700 | [diff] [blame] | 302 | * or should not be modified by the current binding graph. This is only relevant for ahead-of-time |
| dstrasburg | cf5e1bc | 2018-08-09 09:03:38 -0700 | [diff] [blame] | 303 | * subcomponents. |
| 304 | */ |
| dstrasburg | 6d2a7ad | 2018-09-05 08:05:51 -0700 | [diff] [blame] | 305 | Optional<ModifiableBindingMethod> getModifiableBindingMethod( |
| 306 | ModifiableBindingMethod modifiableBindingMethod) { |
| 307 | if (shouldModifyKnownBinding(modifiableBindingMethod)) { |
| dstrasburg | 6d2a7ad | 2018-09-05 08:05:51 -0700 | [diff] [blame] | 308 | MethodSpec baseMethod = modifiableBindingMethod.methodSpec(); |
| dstrasburg | cf5e1bc | 2018-08-09 09:03:38 -0700 | [diff] [blame] | 309 | return Optional.of( |
| dstrasburg | 6d2a7ad | 2018-09-05 08:05:51 -0700 | [diff] [blame] | 310 | ModifiableBindingMethod.implement( |
| 311 | modifiableBindingMethod, |
| 312 | MethodSpec.methodBuilder(baseMethod.name) |
| 313 | .addModifiers(PUBLIC) |
| 314 | .returns(baseMethod.returnType) |
| 315 | .addAnnotation(Override.class) |
| 316 | .addCode( |
| cgdecker | 60a5dde | 2018-09-07 08:44:34 -0700 | [diff] [blame] | 317 | getBindingExpression(modifiableBindingMethod.request()) |
| dstrasburg | 6d2a7ad | 2018-09-05 08:05:51 -0700 | [diff] [blame] | 318 | .getModifiableBindingMethodImplementation( |
| 319 | modifiableBindingMethod, generatedComponentModel)) |
| 320 | .build(), |
| 321 | knownModifiableBindingWillBeFinalized(modifiableBindingMethod))); |
| dstrasburg | cf5e1bc | 2018-08-09 09:03:38 -0700 | [diff] [blame] | 322 | } |
| 323 | return Optional.empty(); |
| 324 | } |
| 325 | |
| dstrasburg | 6d2a7ad | 2018-09-05 08:05:51 -0700 | [diff] [blame] | 326 | /** |
| 327 | * Returns true if a modifiable binding method that was registered in a superclass implementation |
| 328 | * of this subcomponent should be marked as "finalized" if it is being overridden by this |
| 329 | * subcomponent implementation. "Finalized" means we should not attempt to modify the binding in |
| 330 | * any subcomponent subclass. This is only relevant for ahead-of-time subcomponents. |
| 331 | */ |
| 332 | // TODO(user): extract a ModifiableBindingExpressions class? This may need some dependencies |
| 333 | // (like the GCM) but could remove some concerns from this class |
| 334 | private boolean knownModifiableBindingWillBeFinalized( |
| 335 | ModifiableBindingMethod modifiableBindingMethod) { |
| 336 | ModifiableBindingType newModifiableBindingType = |
| cgdecker | 60a5dde | 2018-09-07 08:44:34 -0700 | [diff] [blame] | 337 | getModifiableBindingType(modifiableBindingMethod.request()); |
| dstrasburg | 6d2a7ad | 2018-09-05 08:05:51 -0700 | [diff] [blame] | 338 | if (!newModifiableBindingType.isModifiable()) { |
| 339 | // If a modifiable binding has become non-modifiable it is final by definition. |
| 340 | return true; |
| 341 | } |
| 342 | // All currently supported modifiable types are finalized upon modification. |
| dstrasburg | 09adeae | 2018-09-07 12:11:06 -0700 | [diff] [blame^] | 343 | return modifiableBindingWillBeFinalized( |
| 344 | newModifiableBindingType, |
| 345 | shouldModifyBinding(newModifiableBindingType, modifiableBindingMethod.request())); |
| dstrasburg | 6d2a7ad | 2018-09-05 08:05:51 -0700 | [diff] [blame] | 346 | } |
| 347 | |
| 348 | /** |
| 349 | * Returns true if a newly discovered modifiable binding method, once it is defined in this |
| 350 | * subcomponent implementation, should be marked as "finalized", meaning we should not attempt to |
| 351 | * modify the binding in any subcomponent subclass. This is only relevant for ahead-of-time |
| 352 | * subcomponents. |
| 353 | */ |
| 354 | private boolean newModifiableBindingWillBeFinalized( |
| cgdecker | 60a5dde | 2018-09-07 08:44:34 -0700 | [diff] [blame] | 355 | ModifiableBindingType modifiableBindingType, BindingRequest request) { |
| dstrasburg | 09adeae | 2018-09-07 12:11:06 -0700 | [diff] [blame^] | 356 | return modifiableBindingWillBeFinalized( |
| 357 | modifiableBindingType, shouldModifyBinding(modifiableBindingType, request)); |
| 358 | } |
| 359 | |
| 360 | /** |
| 361 | * Returns true if we shouldn't attempt to further modify a modifiable binding once we complete |
| 362 | * the implementation for the current subcomponent. |
| 363 | */ |
| 364 | private boolean modifiableBindingWillBeFinalized( |
| 365 | ModifiableBindingType modifiableBindingType, boolean modifyingBinding) { |
| 366 | switch (modifiableBindingType) { |
| 367 | case MISSING: |
| 368 | case GENERATED_INSTANCE: |
| 369 | case OPTIONAL: |
| 370 | // Once we modify any of the above a single time, then they are finalized. |
| 371 | return modifyingBinding; |
| 372 | case MULTIBINDING: |
| 373 | return false; |
| 374 | default: |
| 375 | throw new IllegalStateException( |
| 376 | String.format( |
| 377 | "Building binding expression for unsupported ModifiableBindingType [%s].", |
| 378 | modifiableBindingType)); |
| 379 | } |
| dstrasburg | 6d2a7ad | 2018-09-05 08:05:51 -0700 | [diff] [blame] | 380 | } |
| 381 | |
| cgdecker | 60a5dde | 2018-09-07 08:44:34 -0700 | [diff] [blame] | 382 | private BindingExpression getBindingExpression(BindingRequest request) { |
| 383 | if (expressions.containsKey(request)) { |
| 384 | return expressions.get(request); |
| bcorso | f0a99fe | 2017-12-04 10:52:05 -0800 | [diff] [blame] | 385 | } |
| dstrasburg | 4f902cf | 2018-07-30 08:42:57 -0700 | [diff] [blame] | 386 | Optional<BindingExpression> expression = Optional.empty(); |
| cgdecker | 60a5dde | 2018-09-07 08:44:34 -0700 | [diff] [blame] | 387 | ModifiableBindingType modifiableBindingType = getModifiableBindingType(request); |
| dstrasburg | 2e206ec | 2018-08-21 13:00:27 -0700 | [diff] [blame] | 388 | if (modifiableBindingType.isModifiable()) { |
| cgdecker | 60a5dde | 2018-09-07 08:44:34 -0700 | [diff] [blame] | 389 | expression = Optional.of(createModifiableBindingExpression(modifiableBindingType, request)); |
| 390 | } else if (resolvedInThisComponent(request)) { |
| 391 | ResolvedBindings resolvedBindings = graph.resolvedBindings(request); |
| 392 | expression = Optional.of(createBindingExpression(resolvedBindings, request)); |
| dstrasburg | 4f902cf | 2018-07-30 08:42:57 -0700 | [diff] [blame] | 393 | } |
| 394 | if (expression.isPresent()) { |
| cgdecker | 60a5dde | 2018-09-07 08:44:34 -0700 | [diff] [blame] | 395 | expressions.put(request, expression.get()); |
| dstrasburg | 4f902cf | 2018-07-30 08:42:57 -0700 | [diff] [blame] | 396 | return expression.get(); |
| 397 | } |
| cgdecker | 60a5dde | 2018-09-07 08:44:34 -0700 | [diff] [blame] | 398 | checkArgument(parent.isPresent(), "no expression found for %s", request); |
| 399 | return parent.get().getBindingExpression(request); |
| bcorso | f0a99fe | 2017-12-04 10:52:05 -0800 | [diff] [blame] | 400 | } |
| 401 | |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 402 | /** Creates a binding expression. */ |
| 403 | private BindingExpression createBindingExpression( |
| cgdecker | 60a5dde | 2018-09-07 08:44:34 -0700 | [diff] [blame] | 404 | ResolvedBindings resolvedBindings, BindingRequest request) { |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 405 | switch (resolvedBindings.bindingType()) { |
| 406 | case MEMBERS_INJECTION: |
| cgdecker | 60a5dde | 2018-09-07 08:44:34 -0700 | [diff] [blame] | 407 | checkArgument(request.isRequestKind(RequestKind.MEMBERS_INJECTION)); |
| ronshapiro | 9288a97 | 2018-02-14 06:02:12 -0800 | [diff] [blame] | 408 | return new MembersInjectionBindingExpression(resolvedBindings, membersInjectionMethods); |
| bcorso | f0a99fe | 2017-12-04 10:52:05 -0800 | [diff] [blame] | 409 | |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 410 | case PROVISION: |
| cgdecker | 60a5dde | 2018-09-07 08:44:34 -0700 | [diff] [blame] | 411 | return provisionBindingExpression(resolvedBindings, request); |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 412 | |
| 413 | case PRODUCTION: |
| cgdecker | 60a5dde | 2018-09-07 08:44:34 -0700 | [diff] [blame] | 414 | return productionBindingExpression(resolvedBindings, request); |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 415 | |
| 416 | default: |
| 417 | throw new AssertionError(resolvedBindings); |
| bcorso | f0a99fe | 2017-12-04 10:52:05 -0800 | [diff] [blame] | 418 | } |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 419 | } |
| bcorso | f0a99fe | 2017-12-04 10:52:05 -0800 | [diff] [blame] | 420 | |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 421 | /** |
| dstrasburg | 2e206ec | 2018-08-21 13:00:27 -0700 | [diff] [blame] | 422 | * Creates a binding expression for a binding that may be modified across implementations of a |
| 423 | * subcomponent. This is only relevant for ahead-of-time subcomponents. |
| dstrasburg | 4f902cf | 2018-07-30 08:42:57 -0700 | [diff] [blame] | 424 | */ |
| dstrasburg | 2e206ec | 2018-08-21 13:00:27 -0700 | [diff] [blame] | 425 | private BindingExpression createModifiableBindingExpression( |
| cgdecker | 60a5dde | 2018-09-07 08:44:34 -0700 | [diff] [blame] | 426 | ModifiableBindingType type, BindingRequest request) { |
| 427 | ResolvedBindings resolvedBindings = graph.resolvedBindings(request); |
| dstrasburg | 6d2a7ad | 2018-09-05 08:05:51 -0700 | [diff] [blame] | 428 | Optional<ModifiableBindingMethod> matchingModifiableBindingMethod = |
| cgdecker | 60a5dde | 2018-09-07 08:44:34 -0700 | [diff] [blame] | 429 | generatedComponentModel.getModifiableBindingMethod(request); |
| dstrasburg | 6d2a7ad | 2018-09-05 08:05:51 -0700 | [diff] [blame] | 430 | Optional<ComponentMethodDescriptor> matchingComponentMethod = |
| cgdecker | 60a5dde | 2018-09-07 08:44:34 -0700 | [diff] [blame] | 431 | findMatchingComponentMethod(request); |
| dstrasburg | 2e206ec | 2018-08-21 13:00:27 -0700 | [diff] [blame] | 432 | switch (type) { |
| 433 | case GENERATED_INSTANCE: |
| dstrasburg | 2e206ec | 2018-08-21 13:00:27 -0700 | [diff] [blame] | 434 | return new GeneratedInstanceBindingExpression( |
| dstrasburg | 6d2a7ad | 2018-09-05 08:05:51 -0700 | [diff] [blame] | 435 | generatedComponentModel, |
| 436 | resolvedBindings, |
| cgdecker | 60a5dde | 2018-09-07 08:44:34 -0700 | [diff] [blame] | 437 | request, |
| dstrasburg | 6d2a7ad | 2018-09-05 08:05:51 -0700 | [diff] [blame] | 438 | matchingModifiableBindingMethod, |
| 439 | matchingComponentMethod); |
| dstrasburg | 2e206ec | 2018-08-21 13:00:27 -0700 | [diff] [blame] | 440 | case MISSING: |
| dstrasburg | 6d2a7ad | 2018-09-05 08:05:51 -0700 | [diff] [blame] | 441 | return new MissingBindingExpression( |
| 442 | generatedComponentModel, |
| cgdecker | 60a5dde | 2018-09-07 08:44:34 -0700 | [diff] [blame] | 443 | request, |
| dstrasburg | 6d2a7ad | 2018-09-05 08:05:51 -0700 | [diff] [blame] | 444 | matchingModifiableBindingMethod, |
| 445 | matchingComponentMethod); |
| 446 | case OPTIONAL: |
| dstrasburg | 09adeae | 2018-09-07 12:11:06 -0700 | [diff] [blame^] | 447 | case MULTIBINDING: |
| 448 | return wrapInMethod( |
| 449 | resolvedBindings, request, createBindingExpression(resolvedBindings, request)); |
| dstrasburg | 2e206ec | 2018-08-21 13:00:27 -0700 | [diff] [blame] | 450 | default: |
| 451 | throw new IllegalStateException( |
| 452 | String.format( |
| 453 | "Building binding expression for unsupported ModifiableBindingType [%s].", type)); |
| 454 | } |
| 455 | } |
| 456 | |
| 457 | /** |
| 458 | * The reason why a binding may need to be modified across implementations of a subcomponent, if |
| 459 | * at all. This is only relevant for ahead-of-time subcomponents. |
| 460 | */ |
| cgdecker | 60a5dde | 2018-09-07 08:44:34 -0700 | [diff] [blame] | 461 | private ModifiableBindingType getModifiableBindingType(BindingRequest request) { |
| dstrasburg | 2e206ec | 2018-08-21 13:00:27 -0700 | [diff] [blame] | 462 | if (!compilerOptions.aheadOfTimeSubcomponents()) { |
| 463 | return ModifiableBindingType.NONE; |
| 464 | } |
| 465 | |
| 466 | // When generating a final (concrete) implementation of a (sub)component the binding is no |
| 467 | // longer considered modifiable. It cannot be further modified by a subclass implementation. |
| 468 | if (!generatedComponentModel.isAbstract()) { |
| 469 | return ModifiableBindingType.NONE; |
| 470 | } |
| 471 | |
| cgdecker | 60a5dde | 2018-09-07 08:44:34 -0700 | [diff] [blame] | 472 | if (resolvedInThisComponent(request)) { |
| 473 | ResolvedBindings resolvedBindings = graph.resolvedBindings(request); |
| dstrasburg | 2e206ec | 2018-08-21 13:00:27 -0700 | [diff] [blame] | 474 | if (resolvedBindings.contributionBindings().isEmpty()) { |
| 475 | // TODO(ronshapiro): Confirm whether a resolved binding must have a single contribution |
| 476 | // binding. |
| 477 | return ModifiableBindingType.NONE; |
| 478 | } |
| 479 | |
| 480 | ContributionBinding binding = resolvedBindings.contributionBinding(); |
| 481 | if (binding.requiresGeneratedInstance()) { |
| 482 | return ModifiableBindingType.GENERATED_INSTANCE; |
| 483 | } |
| dstrasburg | 6d2a7ad | 2018-09-05 08:05:51 -0700 | [diff] [blame] | 484 | |
| 485 | if (binding.kind().equals(BindingKind.OPTIONAL)) { |
| 486 | return ModifiableBindingType.OPTIONAL; |
| 487 | } |
| dstrasburg | 09adeae | 2018-09-07 12:11:06 -0700 | [diff] [blame^] | 488 | |
| 489 | if (resolvedBindings.bindingType().equals(BindingType.PROVISION) |
| 490 | && binding.isSyntheticMultibinding()) { |
| 491 | return ModifiableBindingType.MULTIBINDING; |
| 492 | } |
| cgdecker | 60a5dde | 2018-09-07 08:44:34 -0700 | [diff] [blame] | 493 | } else if (!resolvableBinding(request)) { |
| dstrasburg | 2e206ec | 2018-08-21 13:00:27 -0700 | [diff] [blame] | 494 | return ModifiableBindingType.MISSING; |
| 495 | } |
| 496 | |
| 497 | // TODO(b/72748365): Add support for remaining types. |
| 498 | return ModifiableBindingType.NONE; |
| 499 | } |
| 500 | |
| 501 | /** |
| 502 | * Returns true if the current binding graph can, and should, modify a binding by overriding a |
| dstrasburg | 6d2a7ad | 2018-09-05 08:05:51 -0700 | [diff] [blame] | 503 | * modifiable binding method. This is only relevant for ahead-of-time subcomponents. |
| dstrasburg | 2e206ec | 2018-08-21 13:00:27 -0700 | [diff] [blame] | 504 | */ |
| dstrasburg | 6d2a7ad | 2018-09-05 08:05:51 -0700 | [diff] [blame] | 505 | private boolean shouldModifyKnownBinding(ModifiableBindingMethod modifiableBindingMethod) { |
| 506 | ModifiableBindingType newModifiableBindingType = |
| cgdecker | 60a5dde | 2018-09-07 08:44:34 -0700 | [diff] [blame] | 507 | getModifiableBindingType(modifiableBindingMethod.request()); |
| dstrasburg | 6d2a7ad | 2018-09-05 08:05:51 -0700 | [diff] [blame] | 508 | if (!newModifiableBindingType.equals(modifiableBindingMethod.type())) { |
| 509 | // It is possible that a binding can change types, in which case we should always modify the |
| 510 | // binding. |
| 511 | return true; |
| 512 | } |
| cgdecker | 60a5dde | 2018-09-07 08:44:34 -0700 | [diff] [blame] | 513 | return shouldModifyBinding(modifiableBindingMethod.type(), modifiableBindingMethod.request()); |
| dstrasburg | 6d2a7ad | 2018-09-05 08:05:51 -0700 | [diff] [blame] | 514 | } |
| 515 | |
| 516 | /** |
| 517 | * Returns true if the current binding graph can, and should, modify a binding by overriding a |
| 518 | * modifiable binding method. This is only relevant for ahead-of-time subcomponents. |
| 519 | */ |
| 520 | private boolean shouldModifyBinding( |
| cgdecker | 60a5dde | 2018-09-07 08:44:34 -0700 | [diff] [blame] | 521 | ModifiableBindingType modifiableBindingType, BindingRequest request) { |
| dstrasburg | 09adeae | 2018-09-07 12:11:06 -0700 | [diff] [blame^] | 522 | ResolvedBindings resolvedBindings = graph.resolvedBindings(request); |
| dstrasburg | 6d2a7ad | 2018-09-05 08:05:51 -0700 | [diff] [blame] | 523 | switch (modifiableBindingType) { |
| dstrasburg | 2e206ec | 2018-08-21 13:00:27 -0700 | [diff] [blame] | 524 | case GENERATED_INSTANCE: |
| 525 | return !generatedComponentModel.isAbstract(); |
| 526 | case MISSING: |
| 527 | // TODO(b/72748365): investigate beder@'s comment about having intermediate component |
| 528 | // ancestors satisfy missing bindings of their children with their own missing binding |
| 529 | // methods so that we can minimize the cases where we need to reach into doubly-nested |
| 530 | // descendant component implementations |
| cgdecker | 60a5dde | 2018-09-07 08:44:34 -0700 | [diff] [blame] | 531 | return resolvableBinding(request); |
| dstrasburg | 6d2a7ad | 2018-09-05 08:05:51 -0700 | [diff] [blame] | 532 | case OPTIONAL: |
| 533 | // Only override optional binding methods if we have a non-empty binding. |
| dstrasburg | 6d2a7ad | 2018-09-05 08:05:51 -0700 | [diff] [blame] | 534 | return !resolvedBindings.contributionBinding().dependencies().isEmpty(); |
| dstrasburg | 09adeae | 2018-09-07 12:11:06 -0700 | [diff] [blame^] | 535 | case MULTIBINDING: |
| 536 | // Only modify a multibinding if there are new contributions. |
| 537 | return !generatedComponentModel |
| 538 | .superclassContributionsMade(request.key()) |
| 539 | .containsAll(resolvedBindings.contributionBinding().dependencies()); |
| dstrasburg | 2e206ec | 2018-08-21 13:00:27 -0700 | [diff] [blame] | 540 | default: |
| 541 | throw new IllegalStateException( |
| 542 | String.format( |
| 543 | "Overriding modifiable binding method with unsupported ModifiableBindingType [%s].", |
| dstrasburg | 6d2a7ad | 2018-09-05 08:05:51 -0700 | [diff] [blame] | 544 | modifiableBindingType)); |
| dstrasburg | 2e206ec | 2018-08-21 13:00:27 -0700 | [diff] [blame] | 545 | } |
| dstrasburg | 4f902cf | 2018-07-30 08:42:57 -0700 | [diff] [blame] | 546 | } |
| 547 | |
| 548 | /** |
| 549 | * Returns true if the binding can be resolved by the graph for this component or any parent |
| 550 | * component. |
| 551 | */ |
| cgdecker | 60a5dde | 2018-09-07 08:44:34 -0700 | [diff] [blame] | 552 | private boolean resolvableBinding(BindingRequest request) { |
| dstrasburg | 4f902cf | 2018-07-30 08:42:57 -0700 | [diff] [blame] | 553 | for (ComponentBindingExpressions expressions = this; |
| 554 | expressions != null; |
| 555 | expressions = expressions.parent.orElse(null)) { |
| cgdecker | 60a5dde | 2018-09-07 08:44:34 -0700 | [diff] [blame] | 556 | if (expressions.resolvedInThisComponent(request)) { |
| dstrasburg | 4f902cf | 2018-07-30 08:42:57 -0700 | [diff] [blame] | 557 | return true; |
| 558 | } |
| 559 | } |
| 560 | return false; |
| 561 | } |
| 562 | |
| 563 | /** Returns true if the binding can be resolved by the graph for this component. */ |
| cgdecker | 60a5dde | 2018-09-07 08:44:34 -0700 | [diff] [blame] | 564 | private boolean resolvedInThisComponent(BindingRequest request) { |
| 565 | ResolvedBindings resolvedBindings = graph.resolvedBindings(request); |
| dstrasburg | 4f902cf | 2018-07-30 08:42:57 -0700 | [diff] [blame] | 566 | return resolvedBindings != null && !resolvedBindings.ownedBindings().isEmpty(); |
| 567 | } |
| 568 | |
| 569 | /** |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 570 | * Returns a binding expression that uses a {@link javax.inject.Provider} for provision bindings |
| 571 | * or a {@link dagger.producers.Producer} for production bindings. |
| 572 | */ |
| 573 | private FrameworkInstanceBindingExpression frameworkInstanceBindingExpression( |
| dpb | 5e03bd5 | 2018-08-10 14:21:15 -0700 | [diff] [blame] | 574 | ResolvedBindings resolvedBindings) { |
| bcorso | af9e004 | 2018-04-16 14:12:23 -0700 | [diff] [blame] | 575 | // TODO(user): Consider merging the static factory creation logic into CreationExpressions? |
| 576 | Optional<MemberSelect> staticMethod = |
| 577 | useStaticFactoryCreation(resolvedBindings.contributionBinding()) |
| 578 | ? staticFactoryCreation(resolvedBindings) |
| 579 | : Optional.empty(); |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 580 | FrameworkInstanceCreationExpression frameworkInstanceCreationExpression = |
| 581 | resolvedBindings.scope().isPresent() |
| 582 | ? scope(resolvedBindings, frameworkInstanceCreationExpression(resolvedBindings)) |
| 583 | : frameworkInstanceCreationExpression(resolvedBindings); |
| cgdecker | 0bc05be | 2018-08-10 12:37:36 -0700 | [diff] [blame] | 584 | FrameworkInstanceSupplier frameworkInstanceSupplier = |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 585 | staticMethod.isPresent() |
| 586 | ? staticMethod::get |
| 587 | : new FrameworkFieldInitializer( |
| cgdecker | 0bc05be | 2018-08-10 12:37:36 -0700 | [diff] [blame] | 588 | generatedComponentModel, resolvedBindings, frameworkInstanceCreationExpression); |
| 589 | |
| dpb | db19750 | 2018-08-27 14:29:47 -0700 | [diff] [blame] | 590 | switch (resolvedBindings.bindingType()) { |
| 591 | case PROVISION: |
| cgdecker | 0bc05be | 2018-08-10 12:37:36 -0700 | [diff] [blame] | 592 | return new ProviderInstanceBindingExpression( |
| dpb | 5e03bd5 | 2018-08-10 14:21:15 -0700 | [diff] [blame] | 593 | resolvedBindings, frameworkInstanceSupplier, types, elements); |
| dpb | db19750 | 2018-08-27 14:29:47 -0700 | [diff] [blame] | 594 | case PRODUCTION: |
| cgdecker | 0bc05be | 2018-08-10 12:37:36 -0700 | [diff] [blame] | 595 | return new ProducerInstanceBindingExpression( |
| dpb | 5e03bd5 | 2018-08-10 14:21:15 -0700 | [diff] [blame] | 596 | resolvedBindings, frameworkInstanceSupplier, types, elements); |
| cgdecker | 0bc05be | 2018-08-10 12:37:36 -0700 | [diff] [blame] | 597 | default: |
| dpb | db19750 | 2018-08-27 14:29:47 -0700 | [diff] [blame] | 598 | throw new AssertionError("invalid binding type: " + resolvedBindings.bindingType()); |
| cgdecker | 0bc05be | 2018-08-10 12:37:36 -0700 | [diff] [blame] | 599 | } |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 600 | } |
| dpb | 6486184 | 2017-12-12 08:17:18 -0800 | [diff] [blame] | 601 | |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 602 | private FrameworkInstanceCreationExpression scope( |
| 603 | ResolvedBindings resolvedBindings, FrameworkInstanceCreationExpression unscoped) { |
| 604 | if (requiresReleasableReferences(resolvedBindings)) { |
| 605 | return () -> |
| 606 | CodeBlock.of( |
| 607 | "$T.create($L, $L)", |
| 608 | REFERENCE_RELEASING_PROVIDER, |
| 609 | unscoped.creationExpression(), |
| 610 | referenceReleasingManagerFields.getExpression( |
| 611 | resolvedBindings.scope().get(), generatedComponentModel.name())); |
| 612 | } else { |
| 613 | return () -> |
| 614 | CodeBlock.of( |
| 615 | "$T.provider($L)", |
| 616 | resolvedBindings.scope().get().isReusable() ? SINGLE_CHECK : DOUBLE_CHECK, |
| 617 | unscoped.creationExpression()); |
| dpb | 6486184 | 2017-12-12 08:17:18 -0800 | [diff] [blame] | 618 | } |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 619 | } |
| dpb | 6486184 | 2017-12-12 08:17:18 -0800 | [diff] [blame] | 620 | |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 621 | /** |
| 622 | * Returns a creation expression for a {@link javax.inject.Provider} for provision bindings or a |
| 623 | * {@link dagger.producers.Producer} for production bindings. |
| 624 | */ |
| 625 | private FrameworkInstanceCreationExpression frameworkInstanceCreationExpression( |
| 626 | ResolvedBindings resolvedBindings) { |
| 627 | checkArgument(!resolvedBindings.bindingType().equals(MEMBERS_INJECTION)); |
| 628 | ContributionBinding binding = resolvedBindings.contributionBinding(); |
| 629 | switch (binding.kind()) { |
| 630 | case COMPONENT: |
| 631 | // The cast can be removed when we drop java 7 source support |
| 632 | return new InstanceFactoryCreationExpression( |
| 633 | () -> CodeBlock.of("($T) this", binding.key().type())); |
| dpb | 6486184 | 2017-12-12 08:17:18 -0800 | [diff] [blame] | 634 | |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 635 | case BOUND_INSTANCE: |
| 636 | return instanceFactoryCreationExpression( |
| 637 | binding, ComponentRequirement.forBoundInstance(binding)); |
| bcorso | f0a99fe | 2017-12-04 10:52:05 -0800 | [diff] [blame] | 638 | |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 639 | case COMPONENT_DEPENDENCY: |
| 640 | return instanceFactoryCreationExpression( |
| 641 | binding, ComponentRequirement.forDependency(binding.key().type())); |
| dpb | 3db68f0 | 2018-01-04 09:15:52 -0800 | [diff] [blame] | 642 | |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 643 | case COMPONENT_PROVISION: |
| 644 | return new DependencyMethodProviderCreationExpression( |
| 645 | binding, generatedComponentModel, componentRequirementFields, compilerOptions, graph); |
| dpb | 3db68f0 | 2018-01-04 09:15:52 -0800 | [diff] [blame] | 646 | |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 647 | case SUBCOMPONENT_BUILDER: |
| 648 | return new SubcomponentBuilderProviderCreationExpression( |
| 649 | binding.key().type(), subcomponentNames.get(binding.key())); |
| dpb | 356a684 | 2018-02-07 07:45:50 -0800 | [diff] [blame] | 650 | |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 651 | case INJECTION: |
| 652 | case PROVISION: |
| bcorso | cc739fe | 2018-04-23 09:16:12 -0700 | [diff] [blame] | 653 | return compilerOptions.experimentalAndroidMode2() |
| 654 | ? staticSwitchingProviders.newCreationExpression(binding, this) |
| 655 | : new InjectionOrProvisionProviderCreationExpression(binding, this); |
| dpb | 3db68f0 | 2018-01-04 09:15:52 -0800 | [diff] [blame] | 656 | |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 657 | case COMPONENT_PRODUCTION: |
| 658 | return new DependencyMethodProducerCreationExpression( |
| 659 | binding, generatedComponentModel, componentRequirementFields, graph); |
| dpb | 3db68f0 | 2018-01-04 09:15:52 -0800 | [diff] [blame] | 660 | |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 661 | case PRODUCTION: |
| bcorso | 5db065d | 2018-04-03 13:49:31 -0700 | [diff] [blame] | 662 | return new ProducerCreationExpression(binding, this); |
| dpb | 3db68f0 | 2018-01-04 09:15:52 -0800 | [diff] [blame] | 663 | |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 664 | case MULTIBOUND_SET: |
| 665 | return new SetFactoryCreationExpression(binding, generatedComponentModel, this, graph); |
| dpb | 3db68f0 | 2018-01-04 09:15:52 -0800 | [diff] [blame] | 666 | |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 667 | case MULTIBOUND_MAP: |
| ronshapiro | 3a08964 | 2018-07-17 15:39:40 -0700 | [diff] [blame] | 668 | return new MapFactoryCreationExpression( |
| 669 | binding, generatedComponentModel, this, graph, elements); |
| dpb | 3db68f0 | 2018-01-04 09:15:52 -0800 | [diff] [blame] | 670 | |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 671 | case RELEASABLE_REFERENCE_MANAGER: |
| 672 | return new ReleasableReferenceManagerProviderCreationExpression( |
| 673 | binding, generatedComponentModel, referenceReleasingManagerFields); |
| dpb | 3db68f0 | 2018-01-04 09:15:52 -0800 | [diff] [blame] | 674 | |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 675 | case RELEASABLE_REFERENCE_MANAGERS: |
| 676 | return new ReleasableReferenceManagerSetProviderCreationExpression( |
| 677 | binding, generatedComponentModel, referenceReleasingManagerFields, graph); |
| dpb | 3db68f0 | 2018-01-04 09:15:52 -0800 | [diff] [blame] | 678 | |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 679 | case DELEGATE: |
| 680 | return new DelegatingFrameworkInstanceCreationExpression( |
| 681 | binding, generatedComponentModel, this); |
| dpb | 3db68f0 | 2018-01-04 09:15:52 -0800 | [diff] [blame] | 682 | |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 683 | case OPTIONAL: |
| ronshapiro | e674076 | 2018-05-01 08:37:26 -0700 | [diff] [blame] | 684 | return new OptionalFactoryInstanceCreationExpression( |
| 685 | optionalFactories, binding, generatedComponentModel, this); |
| dpb | 356a684 | 2018-02-07 07:45:50 -0800 | [diff] [blame] | 686 | |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 687 | case MEMBERS_INJECTOR: |
| ronshapiro | e674076 | 2018-05-01 08:37:26 -0700 | [diff] [blame] | 688 | return new MembersInjectorProviderCreationExpression((ProvisionBinding) binding, this); |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 689 | |
| 690 | default: |
| 691 | throw new AssertionError(binding); |
| 692 | } |
| 693 | } |
| 694 | |
| 695 | private InstanceFactoryCreationExpression instanceFactoryCreationExpression( |
| 696 | ContributionBinding binding, ComponentRequirement componentRequirement) { |
| 697 | return new InstanceFactoryCreationExpression( |
| 698 | binding.nullableType().isPresent(), |
| 699 | () -> |
| 700 | componentRequirementFields.getExpressionDuringInitialization( |
| 701 | componentRequirement, generatedComponentModel.name())); |
| 702 | } |
| 703 | |
| 704 | /** Returns a binding expression for a provision binding. */ |
| 705 | private BindingExpression provisionBindingExpression( |
| cgdecker | 60a5dde | 2018-09-07 08:44:34 -0700 | [diff] [blame] | 706 | ResolvedBindings resolvedBindings, BindingRequest request) { |
| 707 | // All provision requests should have an associated RequestKind, even if they're a framework |
| 708 | // request. |
| 709 | checkArgument(request.requestKind().isPresent()); |
| 710 | RequestKind requestKind = request.requestKind().get(); |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 711 | switch (requestKind) { |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 712 | case INSTANCE: |
| 713 | return instanceBindingExpression(resolvedBindings); |
| 714 | |
| ronshapiro | 62c8d5c | 2018-05-10 09:06:48 -0700 | [diff] [blame] | 715 | case PROVIDER: |
| 716 | return providerBindingExpression(resolvedBindings); |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 717 | |
| 718 | case LAZY: |
| 719 | case PRODUCED: |
| 720 | case PROVIDER_OF_LAZY: |
| dpb | 5e03bd5 | 2018-08-10 14:21:15 -0700 | [diff] [blame] | 721 | return new DerivedFromFrameworkInstanceBindingExpression( |
| dpb | db19750 | 2018-08-27 14:29:47 -0700 | [diff] [blame] | 722 | resolvedBindings, FrameworkType.PROVIDER, requestKind, this, types); |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 723 | |
| ronshapiro | 62c8d5c | 2018-05-10 09:06:48 -0700 | [diff] [blame] | 724 | case PRODUCER: |
| dpb | 5e03bd5 | 2018-08-10 14:21:15 -0700 | [diff] [blame] | 725 | return producerFromProviderBindingExpression(resolvedBindings); |
| ronshapiro | 62c8d5c | 2018-05-10 09:06:48 -0700 | [diff] [blame] | 726 | |
| 727 | case FUTURE: |
| 728 | return new ImmediateFutureBindingExpression(resolvedBindings, this, types); |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 729 | |
| 730 | case MEMBERS_INJECTION: |
| 731 | throw new IllegalArgumentException(); |
| dpb | 356a684 | 2018-02-07 07:45:50 -0800 | [diff] [blame] | 732 | } |
| 733 | |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 734 | throw new AssertionError(); |
| 735 | } |
| dpb | 356a684 | 2018-02-07 07:45:50 -0800 | [diff] [blame] | 736 | |
| cgdecker | 60a5dde | 2018-09-07 08:44:34 -0700 | [diff] [blame] | 737 | /** Returns a binding expression for a production binding. */ |
| 738 | private BindingExpression productionBindingExpression( |
| 739 | ResolvedBindings resolvedBindings, BindingRequest request) { |
| 740 | if (request.frameworkType().isPresent()) { |
| 741 | return frameworkInstanceBindingExpression(resolvedBindings); |
| 742 | } else { |
| 743 | // If no FrameworkType is present, a RequestKind is guaranteed to be present. |
| 744 | return new DerivedFromFrameworkInstanceBindingExpression( |
| 745 | resolvedBindings, FrameworkType.PRODUCER, request.requestKind().get(), this, types); |
| 746 | } |
| 747 | } |
| 748 | |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 749 | /** |
| ronshapiro | 62c8d5c | 2018-05-10 09:06:48 -0700 | [diff] [blame] | 750 | * Returns a binding expression for {@link RequestKind#PROVIDER} requests. |
| 751 | * |
| 752 | * <p>{@code @Binds} bindings that don't {@linkplain #needsCaching(ResolvedBindings) need to be |
| 753 | * cached} can use a {@link DelegateBindingExpression}. |
| 754 | * |
| bcorso | d55c02d | 2018-06-06 15:02:34 -0700 | [diff] [blame] | 755 | * <p>In fastInit mode, use an {@link InnerSwitchingProviders inner switching provider} unless |
| 756 | * that provider's case statement will simply call {@code get()} on another {@link Provider} (in |
| 757 | * which case, just use that Provider directly). |
| ronshapiro | 62c8d5c | 2018-05-10 09:06:48 -0700 | [diff] [blame] | 758 | * |
| 759 | * <p>Otherwise, return a {@link FrameworkInstanceBindingExpression}. |
| 760 | */ |
| 761 | private BindingExpression providerBindingExpression(ResolvedBindings resolvedBindings) { |
| 762 | if (resolvedBindings.contributionBinding().kind().equals(DELEGATE) |
| 763 | && !needsCaching(resolvedBindings)) { |
| 764 | return new DelegateBindingExpression( |
| 765 | resolvedBindings, RequestKind.PROVIDER, this, types, elements); |
| bcorso | d55c02d | 2018-06-06 15:02:34 -0700 | [diff] [blame] | 766 | } else if (compilerOptions.fastInit() |
| ronshapiro | 62c8d5c | 2018-05-10 09:06:48 -0700 | [diff] [blame] | 767 | && frameworkInstanceCreationExpression(resolvedBindings).useInnerSwitchingProvider() |
| 768 | && !(instanceBindingExpression(resolvedBindings) |
| dpb | 5e03bd5 | 2018-08-10 14:21:15 -0700 | [diff] [blame] | 769 | instanceof DerivedFromFrameworkInstanceBindingExpression)) { |
| ronshapiro | 62c8d5c | 2018-05-10 09:06:48 -0700 | [diff] [blame] | 770 | return wrapInMethod( |
| 771 | resolvedBindings, |
| cgdecker | 60a5dde | 2018-09-07 08:44:34 -0700 | [diff] [blame] | 772 | BindingRequest.forDependencyRequest(resolvedBindings.key(), RequestKind.PROVIDER), |
| ronshapiro | 62c8d5c | 2018-05-10 09:06:48 -0700 | [diff] [blame] | 773 | innerSwitchingProviders.newBindingExpression(resolvedBindings.contributionBinding())); |
| 774 | } |
| dpb | 5e03bd5 | 2018-08-10 14:21:15 -0700 | [diff] [blame] | 775 | return frameworkInstanceBindingExpression(resolvedBindings); |
| ronshapiro | 62c8d5c | 2018-05-10 09:06:48 -0700 | [diff] [blame] | 776 | } |
| 777 | |
| 778 | /** |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 779 | * Returns a binding expression that uses a {@link dagger.producers.Producer} field for a |
| 780 | * provision binding. |
| 781 | */ |
| 782 | private FrameworkInstanceBindingExpression producerFromProviderBindingExpression( |
| dpb | 5e03bd5 | 2018-08-10 14:21:15 -0700 | [diff] [blame] | 783 | ResolvedBindings resolvedBindings) { |
| dpb | db19750 | 2018-08-27 14:29:47 -0700 | [diff] [blame] | 784 | checkArgument(resolvedBindings.bindingType().equals(BindingType.PROVISION)); |
| cgdecker | 0bc05be | 2018-08-10 12:37:36 -0700 | [diff] [blame] | 785 | return new ProducerInstanceBindingExpression( |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 786 | resolvedBindings, |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 787 | new FrameworkFieldInitializer( |
| 788 | generatedComponentModel, |
| 789 | resolvedBindings, |
| 790 | new ProducerFromProviderCreationExpression( |
| 791 | resolvedBindings.contributionBinding(), generatedComponentModel, this)), |
| 792 | types, |
| 793 | elements); |
| 794 | } |
| dpb | 356a684 | 2018-02-07 07:45:50 -0800 | [diff] [blame] | 795 | |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 796 | /** |
| 797 | * Returns a binding expression for {@link RequestKind#INSTANCE} requests. |
| 798 | * |
| 799 | * <p>If there is a direct expression (not calling {@link Provider#get()}) we can use for an |
| 800 | * instance of this binding, return it, wrapped in a method if the binding {@linkplain |
| 801 | * #needsCaching(ResolvedBindings) needs to be cached} or the expression has dependencies. |
| 802 | * |
| 803 | * <p>In default mode, we can use direct expressions for bindings that don't need to be cached in |
| 804 | * a reference-releasing scope. |
| 805 | * |
| bcorso | d55c02d | 2018-06-06 15:02:34 -0700 | [diff] [blame] | 806 | * <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] | 807 | */ |
| 808 | private BindingExpression instanceBindingExpression(ResolvedBindings resolvedBindings) { |
| 809 | Optional<BindingExpression> maybeDirectInstanceExpression = |
| 810 | unscopedDirectInstanceExpression(resolvedBindings); |
| 811 | if (canUseDirectInstanceExpression(resolvedBindings) |
| 812 | && maybeDirectInstanceExpression.isPresent()) { |
| 813 | BindingExpression directInstanceExpression = maybeDirectInstanceExpression.get(); |
| 814 | return directInstanceExpression.requiresMethodEncapsulation() |
| 815 | || needsCaching(resolvedBindings) |
| cgdecker | 60a5dde | 2018-09-07 08:44:34 -0700 | [diff] [blame] | 816 | ? wrapInMethod( |
| 817 | resolvedBindings, |
| 818 | BindingRequest.forDependencyRequest(resolvedBindings.key(), RequestKind.INSTANCE), |
| 819 | directInstanceExpression) |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 820 | : directInstanceExpression; |
| dpb | 356a684 | 2018-02-07 07:45:50 -0800 | [diff] [blame] | 821 | } |
| dpb | 5e03bd5 | 2018-08-10 14:21:15 -0700 | [diff] [blame] | 822 | return new DerivedFromFrameworkInstanceBindingExpression( |
| dpb | db19750 | 2018-08-27 14:29:47 -0700 | [diff] [blame] | 823 | resolvedBindings, FrameworkType.PROVIDER, RequestKind.INSTANCE, this, types); |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 824 | } |
| dpb | 356a684 | 2018-02-07 07:45:50 -0800 | [diff] [blame] | 825 | |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 826 | /** |
| 827 | * Returns an unscoped binding expression for an {@link RequestKind#INSTANCE} that does not call |
| 828 | * {@code get()} on its provider, if there is one. |
| 829 | */ |
| 830 | private Optional<BindingExpression> unscopedDirectInstanceExpression( |
| 831 | ResolvedBindings resolvedBindings) { |
| 832 | switch (resolvedBindings.contributionBinding().kind()) { |
| 833 | case DELEGATE: |
| 834 | return Optional.of( |
| 835 | new DelegateBindingExpression( |
| 836 | resolvedBindings, RequestKind.INSTANCE, this, types, elements)); |
| 837 | |
| 838 | case COMPONENT: |
| 839 | return Optional.of( |
| 840 | new ComponentInstanceBindingExpression( |
| 841 | resolvedBindings, generatedComponentModel.name())); |
| 842 | |
| 843 | case COMPONENT_DEPENDENCY: |
| 844 | return Optional.of( |
| 845 | new ComponentRequirementBindingExpression( |
| 846 | resolvedBindings, |
| 847 | ComponentRequirement.forDependency(resolvedBindings.key().type()), |
| 848 | componentRequirementFields)); |
| 849 | |
| 850 | case COMPONENT_PROVISION: |
| 851 | return Optional.of( |
| 852 | new ComponentProvisionBindingExpression( |
| 853 | resolvedBindings, graph, componentRequirementFields, compilerOptions)); |
| 854 | |
| 855 | case SUBCOMPONENT_BUILDER: |
| 856 | return Optional.of( |
| 857 | new SubcomponentBuilderBindingExpression( |
| 858 | resolvedBindings, subcomponentNames.get(resolvedBindings.key()))); |
| 859 | |
| 860 | case MULTIBOUND_SET: |
| 861 | return Optional.of( |
| dstrasburg | 09adeae | 2018-09-07 12:11:06 -0700 | [diff] [blame^] | 862 | new SetBindingExpression( |
| 863 | resolvedBindings, generatedComponentModel, graph, this, types, elements)); |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 864 | |
| 865 | case MULTIBOUND_MAP: |
| 866 | return Optional.of( |
| dstrasburg | 09adeae | 2018-09-07 12:11:06 -0700 | [diff] [blame^] | 867 | new MapBindingExpression( |
| 868 | resolvedBindings, generatedComponentModel, graph, this, types, elements)); |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 869 | |
| 870 | case OPTIONAL: |
| 871 | return Optional.of(new OptionalBindingExpression(resolvedBindings, this, types)); |
| 872 | |
| 873 | case BOUND_INSTANCE: |
| 874 | return Optional.of( |
| 875 | new ComponentRequirementBindingExpression( |
| 876 | resolvedBindings, |
| 877 | ComponentRequirement.forBoundInstance(resolvedBindings.contributionBinding()), |
| 878 | componentRequirementFields)); |
| 879 | |
| 880 | case INJECTION: |
| 881 | case PROVISION: |
| 882 | return Optional.of( |
| 883 | new SimpleMethodBindingExpression( |
| 884 | resolvedBindings, |
| 885 | compilerOptions, |
| 886 | this, |
| 887 | membersInjectionMethods, |
| 888 | componentRequirementFields, |
| 889 | elements)); |
| 890 | |
| 891 | case MEMBERS_INJECTOR: |
| 892 | case RELEASABLE_REFERENCE_MANAGER: |
| 893 | case RELEASABLE_REFERENCE_MANAGERS: |
| 894 | // TODO(dpb): Implement direct expressions for these. |
| 895 | return Optional.empty(); |
| 896 | |
| 897 | case MEMBERS_INJECTION: |
| 898 | case COMPONENT_PRODUCTION: |
| 899 | case PRODUCTION: |
| 900 | throw new IllegalArgumentException( |
| 901 | resolvedBindings.contributionBinding().kind().toString()); |
| 902 | } |
| 903 | throw new AssertionError(); |
| 904 | } |
| 905 | |
| 906 | /** |
| bcorso | af9e004 | 2018-04-16 14:12:23 -0700 | [diff] [blame] | 907 | * Returns {@code true} if the binding should use the static factory creation strategy. |
| 908 | * |
| dstrasburg | 6d2a7ad | 2018-09-05 08:05:51 -0700 | [diff] [blame] | 909 | * <p>In default mode, we always use the static factory creation strategy. In fastInit mode, we |
| bcorso | d55c02d | 2018-06-06 15:02:34 -0700 | [diff] [blame] | 910 | * prefer to use a SwitchingProvider instead of static factories in order to reduce class loading; |
| dstrasburg | 6d2a7ad | 2018-09-05 08:05:51 -0700 | [diff] [blame] | 911 | * however, we allow static factories that can reused across multiple bindings, e.g. {@code |
| 912 | * MapFactory} or {@code SetFactory}. |
| bcorso | af9e004 | 2018-04-16 14:12:23 -0700 | [diff] [blame] | 913 | */ |
| 914 | private boolean useStaticFactoryCreation(ContributionBinding binding) { |
| bcorso | d55c02d | 2018-06-06 15:02:34 -0700 | [diff] [blame] | 915 | return !(compilerOptions.experimentalAndroidMode2() || compilerOptions.fastInit()) |
| bcorso | af9e004 | 2018-04-16 14:12:23 -0700 | [diff] [blame] | 916 | || binding.kind().equals(MULTIBOUND_MAP) |
| 917 | || binding.kind().equals(MULTIBOUND_SET); |
| 918 | } |
| 919 | |
| 920 | /** |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 921 | * Returns {@code true} if we can use a direct (not {@code Provider.get()}) expression for this |
| 922 | * binding. If the binding doesn't {@linkplain #needsCaching(ResolvedBindings) need to be cached}, |
| 923 | * we can. |
| 924 | * |
| bcorso | d55c02d | 2018-06-06 15:02:34 -0700 | [diff] [blame] | 925 | * <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] | 926 | * #needsCaching(ResolvedBindings) needs to be cached} as long as it's not in a |
| 927 | * reference-releasing scope. |
| 928 | */ |
| 929 | private boolean canUseDirectInstanceExpression(ResolvedBindings resolvedBindings) { |
| 930 | return !needsCaching(resolvedBindings) |
| bcorso | d55c02d | 2018-06-06 15:02:34 -0700 | [diff] [blame] | 931 | || (compilerOptions.fastInit() && !requiresReleasableReferences(resolvedBindings)); |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 932 | } |
| 933 | |
| 934 | /** |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 935 | * Returns a binding expression that uses a given one as the body of a method that users call. If |
| dstrasburg | 6d2a7ad | 2018-09-05 08:05:51 -0700 | [diff] [blame] | 936 | * a component provision method matches it, it will be the method implemented. If it does not |
| 937 | * match a component provision method and the binding is modifiable the a new public modifiable |
| 938 | * binding method will be written. If the binding doesn't match a component method nor is it |
| 939 | * modifiable, then a new private method will be written. |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 940 | */ |
| 941 | private BindingExpression wrapInMethod( |
| 942 | ResolvedBindings resolvedBindings, |
| cgdecker | 60a5dde | 2018-09-07 08:44:34 -0700 | [diff] [blame] | 943 | BindingRequest request, |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 944 | BindingExpression bindingExpression) { |
| dstrasburg | 09adeae | 2018-09-07 12:11:06 -0700 | [diff] [blame^] | 945 | // If we've already wrapped the expression, then use the delegate. |
| 946 | if (bindingExpression instanceof MethodBindingExpression) { |
| 947 | return bindingExpression; |
| 948 | } |
| 949 | |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 950 | BindingMethodImplementation methodImplementation = |
| cgdecker | 60a5dde | 2018-09-07 08:44:34 -0700 | [diff] [blame] | 951 | methodImplementation(resolvedBindings, request, bindingExpression); |
| dstrasburg | 6d2a7ad | 2018-09-05 08:05:51 -0700 | [diff] [blame] | 952 | Optional<ComponentMethodDescriptor> matchingComponentMethod = |
| cgdecker | 60a5dde | 2018-09-07 08:44:34 -0700 | [diff] [blame] | 953 | findMatchingComponentMethod(request); |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 954 | |
| cgdecker | 60a5dde | 2018-09-07 08:44:34 -0700 | [diff] [blame] | 955 | ModifiableBindingType modifiableBindingType = getModifiableBindingType(request); |
| dstrasburg | 09adeae | 2018-09-07 12:11:06 -0700 | [diff] [blame^] | 956 | if (shouldUseAModifiableConcreteMethodBindingExpression( |
| 957 | modifiableBindingType, matchingComponentMethod)) { |
| dstrasburg | 6d2a7ad | 2018-09-05 08:05:51 -0700 | [diff] [blame] | 958 | return new ModifiableConcreteMethodBindingExpression( |
| 959 | resolvedBindings, |
| cgdecker | 60a5dde | 2018-09-07 08:44:34 -0700 | [diff] [blame] | 960 | request, |
| dstrasburg | 6d2a7ad | 2018-09-05 08:05:51 -0700 | [diff] [blame] | 961 | modifiableBindingType, |
| 962 | methodImplementation, |
| 963 | generatedComponentModel, |
| cgdecker | 60a5dde | 2018-09-07 08:44:34 -0700 | [diff] [blame] | 964 | generatedComponentModel.getModifiableBindingMethod(request), |
| 965 | newModifiableBindingWillBeFinalized(modifiableBindingType, request)); |
| dstrasburg | 6d2a7ad | 2018-09-05 08:05:51 -0700 | [diff] [blame] | 966 | } |
| 967 | |
| 968 | return matchingComponentMethod |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 969 | .<BindingExpression>map( |
| 970 | componentMethod -> |
| 971 | new ComponentMethodBindingExpression( |
| dpb | d768576 | 2018-02-16 12:20:09 -0800 | [diff] [blame] | 972 | methodImplementation, generatedComponentModel, componentMethod)) |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 973 | .orElseGet( |
| 974 | () -> |
| 975 | new PrivateMethodBindingExpression( |
| cgdecker | 60a5dde | 2018-09-07 08:44:34 -0700 | [diff] [blame] | 976 | resolvedBindings, request, methodImplementation, generatedComponentModel)); |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 977 | } |
| 978 | |
| dstrasburg | 09adeae | 2018-09-07 12:11:06 -0700 | [diff] [blame^] | 979 | /** |
| 980 | * Returns true if we should wrap a binding expression using a {@link |
| 981 | * ModifiableConcreteMethodBindingExpression}. If we're generating the abstract base class of a |
| 982 | * subcomponent and the binding matches a component method, even if it is modifiable, then it |
| 983 | * should be "wrapped" by a {@link ComponentMethodBindingExpression}. If it isn't a base class |
| 984 | * then modifiable methods should be handled by a {@link |
| 985 | * ModifiableConcreteMethodBindingExpression}. When generating an inner subcomponent it doesn't |
| 986 | * matter whether the binding matches a component method: All modifiable bindings should be |
| 987 | * handled by a {@link ModifiableConcreteMethodBindingExpression}. |
| 988 | */ |
| 989 | private boolean shouldUseAModifiableConcreteMethodBindingExpression( |
| 990 | ModifiableBindingType type, Optional<ComponentMethodDescriptor> matchingComponentMethod) { |
| 991 | return type.isModifiable() |
| 992 | && (generatedComponentModel.supermodel().isPresent() |
| 993 | || !matchingComponentMethod.isPresent()); |
| 994 | } |
| 995 | |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 996 | /** Returns the first component method associated with this request kind, if one exists. */ |
| cgdecker | 60a5dde | 2018-09-07 08:44:34 -0700 | [diff] [blame] | 997 | private Optional<ComponentMethodDescriptor> findMatchingComponentMethod(BindingRequest request) { |
| 998 | return graph.componentDescriptor().componentMethods().stream() |
| 999 | .filter(method -> doesComponentMethodMatch(method, request)) |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 1000 | .findFirst(); |
| 1001 | } |
| 1002 | |
| cgdecker | 60a5dde | 2018-09-07 08:44:34 -0700 | [diff] [blame] | 1003 | /** Returns true if the component method matches the binding request. */ |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 1004 | private boolean doesComponentMethodMatch( |
| cgdecker | 60a5dde | 2018-09-07 08:44:34 -0700 | [diff] [blame] | 1005 | ComponentMethodDescriptor componentMethod, BindingRequest request) { |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 1006 | return componentMethod |
| 1007 | .dependencyRequest() |
| cgdecker | 60a5dde | 2018-09-07 08:44:34 -0700 | [diff] [blame] | 1008 | .map(BindingRequest::forDependencyRequest) |
| dstrasburg | 09adeae | 2018-09-07 12:11:06 -0700 | [diff] [blame^] | 1009 | .filter(request::equals) |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 1010 | .isPresent(); |
| 1011 | } |
| 1012 | |
| 1013 | private BindingMethodImplementation methodImplementation( |
| 1014 | ResolvedBindings resolvedBindings, |
| cgdecker | 60a5dde | 2018-09-07 08:44:34 -0700 | [diff] [blame] | 1015 | BindingRequest request, |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 1016 | BindingExpression bindingExpression) { |
| bcorso | d55c02d | 2018-06-06 15:02:34 -0700 | [diff] [blame] | 1017 | if (compilerOptions.fastInit()) { |
| cgdecker | 60a5dde | 2018-09-07 08:44:34 -0700 | [diff] [blame] | 1018 | if (request.isRequestKind(RequestKind.PROVIDER)) { |
| bcorso | 567ff0e | 2018-03-21 13:49:09 -0700 | [diff] [blame] | 1019 | return new SingleCheckedMethodImplementation( |
| cgdecker | 60a5dde | 2018-09-07 08:44:34 -0700 | [diff] [blame] | 1020 | resolvedBindings, request, bindingExpression, types, generatedComponentModel); |
| 1021 | } else if (request.isRequestKind(RequestKind.INSTANCE) && needsCaching(resolvedBindings)) { |
| bcorso | 567ff0e | 2018-03-21 13:49:09 -0700 | [diff] [blame] | 1022 | return resolvedBindings.scope().get().isReusable() |
| 1023 | ? new SingleCheckedMethodImplementation( |
| cgdecker | 60a5dde | 2018-09-07 08:44:34 -0700 | [diff] [blame] | 1024 | resolvedBindings, request, bindingExpression, types, generatedComponentModel) |
| bcorso | 567ff0e | 2018-03-21 13:49:09 -0700 | [diff] [blame] | 1025 | : new DoubleCheckedMethodImplementation( |
| cgdecker | 60a5dde | 2018-09-07 08:44:34 -0700 | [diff] [blame] | 1026 | resolvedBindings, request, bindingExpression, types, generatedComponentModel); |
| bcorso | 567ff0e | 2018-03-21 13:49:09 -0700 | [diff] [blame] | 1027 | } |
| 1028 | } |
| 1029 | |
| 1030 | return new BindingMethodImplementation( |
| cgdecker | 60a5dde | 2018-09-07 08:44:34 -0700 | [diff] [blame] | 1031 | resolvedBindings, request, bindingExpression, generatedComponentModel.name(), types); |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 1032 | } |
| 1033 | |
| 1034 | /** |
| 1035 | * Returns {@code true} if the component needs to make sure the provided value is cached. |
| 1036 | * |
| 1037 | * <p>The component needs to cache the value for scoped bindings except for {@code @Binds} |
| 1038 | * bindings whose scope is no stronger than their delegate's. |
| 1039 | */ |
| 1040 | private boolean needsCaching(ResolvedBindings resolvedBindings) { |
| 1041 | if (!resolvedBindings.scope().isPresent()) { |
| 1042 | return false; |
| bcorso | 3828ca2 | 2018-01-29 10:14:12 -0800 | [diff] [blame] | 1043 | } |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 1044 | if (resolvedBindings.contributionBinding().kind().equals(DELEGATE)) { |
| 1045 | return isBindsScopeStrongerThanDependencyScope(resolvedBindings, graph); |
| 1046 | } |
| 1047 | return true; |
| 1048 | } |
| 1049 | |
| bcorso | d55c02d | 2018-06-06 15:02:34 -0700 | [diff] [blame] | 1050 | // TODO(user): Enable releasable references in fastInit |
| dpb | 73afb30 | 2018-02-12 10:59:53 -0800 | [diff] [blame] | 1051 | private boolean requiresReleasableReferences(ResolvedBindings resolvedBindings) { |
| 1052 | return resolvedBindings.scope().isPresent() |
| 1053 | && referenceReleasingManagerFields.requiresReleasableReferences( |
| 1054 | resolvedBindings.scope().get()); |
| dpb | 68c77d8 | 2017-08-15 15:23:35 -0700 | [diff] [blame] | 1055 | } |
| 1056 | } |