| erichang | 9dd6aed | 2017-06-15 15:54:00 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2017 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 | |
| erichang | 9dd6aed | 2017-06-15 15:54:00 -0700 | [diff] [blame] | 19 | import static com.google.common.base.Preconditions.checkNotNull; |
| dpb | a6ff6c1 | 2017-08-14 08:47:36 -0700 | [diff] [blame^] | 20 | import static dagger.internal.codegen.Accessibility.isRawTypeAccessible; |
| 21 | import static dagger.internal.codegen.Accessibility.isTypeAccessibleFrom; |
| erichang | 4377417 | 2017-08-04 10:42:07 -0700 | [diff] [blame] | 22 | import static dagger.internal.codegen.AnnotationSpecs.Suppression.RAWTYPES; |
| erichang | 9dd6aed | 2017-06-15 15:54:00 -0700 | [diff] [blame] | 23 | import static dagger.internal.codegen.MemberSelect.staticMemberSelect; |
| erichang | 4377417 | 2017-08-04 10:42:07 -0700 | [diff] [blame] | 24 | import static dagger.internal.codegen.TypeNames.PRODUCER; |
| dpb | a6ff6c1 | 2017-08-14 08:47:36 -0700 | [diff] [blame^] | 25 | import static dagger.internal.codegen.TypeNames.rawTypeName; |
| erichang | 4377417 | 2017-08-04 10:42:07 -0700 | [diff] [blame] | 26 | import static javax.lang.model.element.Modifier.PRIVATE; |
| erichang | 9dd6aed | 2017-06-15 15:54:00 -0700 | [diff] [blame] | 27 | |
| erichang | 9dd6aed | 2017-06-15 15:54:00 -0700 | [diff] [blame] | 28 | import com.google.common.collect.ImmutableMap; |
| 29 | import com.squareup.javapoet.ClassName; |
| 30 | import com.squareup.javapoet.CodeBlock; |
| 31 | import com.squareup.javapoet.FieldSpec; |
| dpb | a6ff6c1 | 2017-08-14 08:47:36 -0700 | [diff] [blame^] | 32 | import com.squareup.javapoet.TypeName; |
| erichang | 9dd6aed | 2017-06-15 15:54:00 -0700 | [diff] [blame] | 33 | import java.util.Optional; |
| dpb | a6ff6c1 | 2017-08-14 08:47:36 -0700 | [diff] [blame^] | 34 | import javax.lang.model.type.TypeMirror; |
| ronshapiro | cb725da | 2017-07-04 15:14:48 -0700 | [diff] [blame] | 35 | import javax.lang.model.util.Elements; |
| erichang | 9dd6aed | 2017-06-15 15:54:00 -0700 | [diff] [blame] | 36 | |
| 37 | /** The code expressions to declare, initialize, and/or access a binding in a component. */ |
| ronshapiro | 186d17a | 2017-08-11 08:18:07 -0700 | [diff] [blame] | 38 | abstract class BindingExpression { |
| 39 | private final BindingKey bindingKey; |
| erichang | 9dd6aed | 2017-06-15 15:54:00 -0700 | [diff] [blame] | 40 | |
| ronshapiro | 186d17a | 2017-08-11 08:18:07 -0700 | [diff] [blame] | 41 | BindingExpression(BindingKey bindingKey) { |
| 42 | this.bindingKey = checkNotNull(bindingKey); |
| dpb | 02dd23f | 2017-07-27 11:13:11 -0700 | [diff] [blame] | 43 | } |
| 44 | |
| ronshapiro | 186d17a | 2017-08-11 08:18:07 -0700 | [diff] [blame] | 45 | /** The key for which this instance can fulfill requests. */ |
| 46 | final BindingKey bindingKey() { |
| 47 | return bindingKey; |
| dpb | 02dd23f | 2017-07-27 11:13:11 -0700 | [diff] [blame] | 48 | } |
| 49 | |
| 50 | /** |
| dpb | a6ff6c1 | 2017-08-14 08:47:36 -0700 | [diff] [blame^] | 51 | * Returns an expression that evaluates to the value of a dependency request. |
| 52 | * |
| 53 | * @param requestingClass the class that will contain the expression |
| dpb | 02dd23f | 2017-07-27 11:13:11 -0700 | [diff] [blame] | 54 | */ |
| dpb | a6ff6c1 | 2017-08-14 08:47:36 -0700 | [diff] [blame^] | 55 | abstract CodeBlock getDependencyExpression(DependencyRequest request, ClassName requestingClass); |
| dpb | 02dd23f | 2017-07-27 11:13:11 -0700 | [diff] [blame] | 56 | |
| 57 | /** |
| dpb | a6ff6c1 | 2017-08-14 08:47:36 -0700 | [diff] [blame^] | 58 | * Returns an expression that evaluates to the value of a framework dependency. |
| 59 | * |
| 60 | * @param requestingClass the class that will contain the expression |
| dpb | 02dd23f | 2017-07-27 11:13:11 -0700 | [diff] [blame] | 61 | */ |
| dpb | a6ff6c1 | 2017-08-14 08:47:36 -0700 | [diff] [blame^] | 62 | abstract CodeBlock getDependencyExpression( |
| ronshapiro | 186d17a | 2017-08-11 08:18:07 -0700 | [diff] [blame] | 63 | FrameworkDependency frameworkDependency, ClassName requestingClass); |
| erichang | 9dd6aed | 2017-06-15 15:54:00 -0700 | [diff] [blame] | 64 | |
| dpb | a6ff6c1 | 2017-08-14 08:47:36 -0700 | [diff] [blame^] | 65 | /** |
| 66 | * Returns an expression that evaluates to the value of a dependency request, for passing to a |
| 67 | * binding method, an {@code @Inject}-annotated constructor or member, or a proxy for one. |
| 68 | * |
| 69 | * <p>If the method is a generated static {@link InjectionMethods injection method}, each |
| 70 | * parameter will be {@link Object} if the dependency's raw type is inaccessible. If that is the |
| 71 | * case for this dependency, the returned expression will use a cast to evaluate to the raw type. |
| 72 | * |
| 73 | * @param requestingClass the class that will contain the expression |
| 74 | */ |
| 75 | // TODO(b/64024402) Merge with getDependencyExpression(DependencyRequest, ClassName) if possible. |
| 76 | CodeBlock getDependencyArgumentExpression( |
| 77 | DependencyRequest dependencyRequest, ClassName requestingClass) { |
| 78 | CodeBlock.Builder argument = CodeBlock.builder(); |
| 79 | |
| 80 | TypeMirror dependencyType = dependencyRequest.key().type(); |
| 81 | if (!isTypeAccessibleFrom(dependencyType, requestingClass.packageName()) |
| 82 | && isRawTypeAccessible(dependencyType, requestingClass.packageName())) { |
| 83 | argument.add("($T) ", rawTypeName(TypeName.get(dependencyType))); |
| 84 | } |
| 85 | |
| 86 | argument.add(getDependencyExpression(dependencyRequest, requestingClass)); |
| 87 | return argument.build(); |
| 88 | } |
| 89 | |
| erichang | 9dd6aed | 2017-06-15 15:54:00 -0700 | [diff] [blame] | 90 | /** Factory for building a {@link BindingExpression}. */ |
| 91 | static final class Factory { |
| dpb | c8c6c05 | 2017-07-28 10:48:06 -0700 | [diff] [blame] | 92 | private final CompilerOptions compilerOptions; |
| erichang | 9dd6aed | 2017-06-15 15:54:00 -0700 | [diff] [blame] | 93 | private final ClassName componentName; |
| erichang | 4377417 | 2017-08-04 10:42:07 -0700 | [diff] [blame] | 94 | private final UniqueNameSet componentFieldNames; |
| erichang | 9dd6aed | 2017-06-15 15:54:00 -0700 | [diff] [blame] | 95 | private final HasBindingExpressions hasBindingExpressions; |
| 96 | private final ImmutableMap<BindingKey, String> subcomponentNames; |
| 97 | private final BindingGraph graph; |
| ronshapiro | cb725da | 2017-07-04 15:14:48 -0700 | [diff] [blame] | 98 | private final Elements elements; |
| erichang | 9dd6aed | 2017-06-15 15:54:00 -0700 | [diff] [blame] | 99 | |
| 100 | Factory( |
| dpb | c8c6c05 | 2017-07-28 10:48:06 -0700 | [diff] [blame] | 101 | CompilerOptions compilerOptions, |
| erichang | 9dd6aed | 2017-06-15 15:54:00 -0700 | [diff] [blame] | 102 | ClassName componentName, |
| erichang | 4377417 | 2017-08-04 10:42:07 -0700 | [diff] [blame] | 103 | UniqueNameSet componentFieldNames, |
| erichang | 9dd6aed | 2017-06-15 15:54:00 -0700 | [diff] [blame] | 104 | HasBindingExpressions hasBindingExpressions, |
| 105 | ImmutableMap<BindingKey, String> subcomponentNames, |
| ronshapiro | cb725da | 2017-07-04 15:14:48 -0700 | [diff] [blame] | 106 | BindingGraph graph, |
| 107 | Elements elements) { |
| dpb | c8c6c05 | 2017-07-28 10:48:06 -0700 | [diff] [blame] | 108 | this.compilerOptions = checkNotNull(compilerOptions); |
| erichang | 9dd6aed | 2017-06-15 15:54:00 -0700 | [diff] [blame] | 109 | this.componentName = checkNotNull(componentName); |
| erichang | 4377417 | 2017-08-04 10:42:07 -0700 | [diff] [blame] | 110 | this.componentFieldNames = checkNotNull(componentFieldNames); |
| erichang | 9dd6aed | 2017-06-15 15:54:00 -0700 | [diff] [blame] | 111 | this.hasBindingExpressions = checkNotNull(hasBindingExpressions); |
| 112 | this.subcomponentNames = checkNotNull(subcomponentNames); |
| 113 | this.graph = checkNotNull(graph); |
| ronshapiro | e05f921 | 2017-08-08 11:22:11 -0700 | [diff] [blame] | 114 | this.elements = checkNotNull(elements); |
| erichang | 9dd6aed | 2017-06-15 15:54:00 -0700 | [diff] [blame] | 115 | } |
| 116 | |
| 117 | /** Creates a binding expression for a field. */ |
| erichang | 4377417 | 2017-08-04 10:42:07 -0700 | [diff] [blame] | 118 | BindingExpression forField(ResolvedBindings resolvedBindings) { |
| 119 | FieldSpec fieldSpec = generateFrameworkField(resolvedBindings, Optional.empty()); |
| erichang | 9dd6aed | 2017-06-15 15:54:00 -0700 | [diff] [blame] | 120 | MemberSelect memberSelect = MemberSelect.localField(componentName, fieldSpec.name); |
| ronshapiro | 186d17a | 2017-08-11 08:18:07 -0700 | [diff] [blame] | 121 | return create(resolvedBindings, Optional.of(fieldSpec), memberSelect); |
| erichang | 4377417 | 2017-08-04 10:42:07 -0700 | [diff] [blame] | 122 | } |
| 123 | |
| 124 | BindingExpression forProducerFromProviderField(ResolvedBindings resolvedBindings) { |
| 125 | FieldSpec fieldSpec = generateFrameworkField(resolvedBindings, Optional.of(PRODUCER)); |
| 126 | MemberSelect memberSelect = MemberSelect.localField(componentName, fieldSpec.name); |
| ronshapiro | 186d17a | 2017-08-11 08:18:07 -0700 | [diff] [blame] | 127 | return new ProducerBindingExpression( |
| 128 | resolvedBindings.bindingKey(), |
| erichang | 4377417 | 2017-08-04 10:42:07 -0700 | [diff] [blame] | 129 | Optional.of(fieldSpec), |
| 130 | hasBindingExpressions, |
| ronshapiro | 186d17a | 2017-08-11 08:18:07 -0700 | [diff] [blame] | 131 | memberSelect, |
| erichang | 4377417 | 2017-08-04 10:42:07 -0700 | [diff] [blame] | 132 | true); |
| erichang | 9dd6aed | 2017-06-15 15:54:00 -0700 | [diff] [blame] | 133 | } |
| 134 | |
| ronshapiro | 186d17a | 2017-08-11 08:18:07 -0700 | [diff] [blame] | 135 | /** |
| 136 | * Creates a binding expression for a static method call. |
| 137 | */ |
| erichang | 9dd6aed | 2017-06-15 15:54:00 -0700 | [diff] [blame] | 138 | Optional<BindingExpression> forStaticMethod(ResolvedBindings resolvedBindings) { |
| ronshapiro | 186d17a | 2017-08-11 08:18:07 -0700 | [diff] [blame] | 139 | return staticMemberSelect(resolvedBindings) |
| 140 | .map(memberSelect -> create(resolvedBindings, Optional.empty(), memberSelect)); |
| erichang | 4377417 | 2017-08-04 10:42:07 -0700 | [diff] [blame] | 141 | } |
| 142 | |
| 143 | /** |
| 144 | * Adds a field representing the resolved bindings, optionally forcing it to use a particular |
| 145 | * binding type (instead of the type the resolved bindings would typically use). |
| 146 | */ |
| 147 | private FieldSpec generateFrameworkField( |
| 148 | ResolvedBindings resolvedBindings, Optional<ClassName> frameworkClass) { |
| 149 | boolean useRawType = useRawType(resolvedBindings); |
| 150 | |
| 151 | FrameworkField contributionBindingField = |
| 152 | FrameworkField.forResolvedBindings(resolvedBindings, frameworkClass); |
| 153 | FieldSpec.Builder contributionField = |
| 154 | FieldSpec.builder( |
| 155 | useRawType |
| 156 | ? contributionBindingField.type().rawType |
| 157 | : contributionBindingField.type(), |
| 158 | componentFieldNames.getUniqueName(contributionBindingField.name())); |
| 159 | contributionField.addModifiers(PRIVATE); |
| 160 | if (useRawType) { |
| 161 | contributionField.addAnnotation(AnnotationSpecs.suppressWarnings(RAWTYPES)); |
| 162 | } |
| 163 | |
| 164 | return contributionField.build(); |
| 165 | } |
| 166 | |
| 167 | private boolean useRawType(ResolvedBindings resolvedBindings) { |
| 168 | Optional<String> bindingPackage = resolvedBindings.bindingPackage(); |
| 169 | return bindingPackage.isPresent() |
| 170 | && !bindingPackage.get().equals(componentName.packageName()); |
| erichang | 9dd6aed | 2017-06-15 15:54:00 -0700 | [diff] [blame] | 171 | } |
| 172 | |
| ronshapiro | 186d17a | 2017-08-11 08:18:07 -0700 | [diff] [blame] | 173 | private BindingExpression create( |
| 174 | ResolvedBindings resolvedBindings, |
| 175 | Optional<FieldSpec> fieldSpec, |
| 176 | MemberSelect memberSelect) { |
| erichang | 9dd6aed | 2017-06-15 15:54:00 -0700 | [diff] [blame] | 177 | BindingKey bindingKey = resolvedBindings.bindingKey(); |
| 178 | switch (resolvedBindings.bindingType()) { |
| 179 | case MEMBERS_INJECTION: |
| ronshapiro | 186d17a | 2017-08-11 08:18:07 -0700 | [diff] [blame] | 180 | return new MembersInjectorBindingExpression( |
| 181 | bindingKey, fieldSpec, hasBindingExpressions, memberSelect); |
| erichang | 9dd6aed | 2017-06-15 15:54:00 -0700 | [diff] [blame] | 182 | case PRODUCTION: |
| ronshapiro | 186d17a | 2017-08-11 08:18:07 -0700 | [diff] [blame] | 183 | return new ProducerBindingExpression( |
| 184 | bindingKey, fieldSpec, hasBindingExpressions, memberSelect, false); |
| erichang | 9dd6aed | 2017-06-15 15:54:00 -0700 | [diff] [blame] | 185 | case PROVISION: |
| 186 | ProvisionBinding provisionBinding = |
| 187 | (ProvisionBinding) resolvedBindings.contributionBinding(); |
| 188 | |
| ronshapiro | 186d17a | 2017-08-11 08:18:07 -0700 | [diff] [blame] | 189 | ProviderBindingExpression providerBindingExpression = |
| 190 | new ProviderBindingExpression( |
| 191 | bindingKey, fieldSpec, hasBindingExpressions, memberSelect); |
| erichang | 9dd6aed | 2017-06-15 15:54:00 -0700 | [diff] [blame] | 192 | |
| 193 | switch (provisionBinding.bindingKind()) { |
| 194 | case SUBCOMPONENT_BUILDER: |
| ronshapiro | 186d17a | 2017-08-11 08:18:07 -0700 | [diff] [blame] | 195 | return new SubcomponentBuilderBindingExpression( |
| 196 | providerBindingExpression, subcomponentNames.get(bindingKey)); |
| erichang | 9dd6aed | 2017-06-15 15:54:00 -0700 | [diff] [blame] | 197 | case SYNTHETIC_MULTIBOUND_SET: |
| ronshapiro | 186d17a | 2017-08-11 08:18:07 -0700 | [diff] [blame] | 198 | return new SetBindingExpression( |
| erichang | 9dd6aed | 2017-06-15 15:54:00 -0700 | [diff] [blame] | 199 | provisionBinding, |
| 200 | graph, |
| 201 | hasBindingExpressions, |
| ronshapiro | 186d17a | 2017-08-11 08:18:07 -0700 | [diff] [blame] | 202 | providerBindingExpression, |
| ronshapiro | cb725da | 2017-07-04 15:14:48 -0700 | [diff] [blame] | 203 | elements); |
| ronshapiro | 3a891dd | 2017-07-04 20:04:14 -0700 | [diff] [blame] | 204 | case SYNTHETIC_OPTIONAL_BINDING: |
| ronshapiro | 186d17a | 2017-08-11 08:18:07 -0700 | [diff] [blame] | 205 | return new OptionalBindingExpression( |
| 206 | provisionBinding, providerBindingExpression, hasBindingExpressions); |
| erichang | 9dd6aed | 2017-06-15 15:54:00 -0700 | [diff] [blame] | 207 | case INJECTION: |
| 208 | case PROVISION: |
| ronshapiro | e05f921 | 2017-08-08 11:22:11 -0700 | [diff] [blame] | 209 | if (!provisionBinding.scope().isPresent() |
| erichang | 9dd6aed | 2017-06-15 15:54:00 -0700 | [diff] [blame] | 210 | && !provisionBinding.requiresModuleInstance() |
| 211 | && provisionBinding.bindingElement().isPresent()) { |
| ronshapiro | 186d17a | 2017-08-11 08:18:07 -0700 | [diff] [blame] | 212 | return new SimpleMethodBindingExpression( |
| dpb | c8c6c05 | 2017-07-28 10:48:06 -0700 | [diff] [blame] | 213 | compilerOptions, |
| erichang | 9dd6aed | 2017-06-15 15:54:00 -0700 | [diff] [blame] | 214 | provisionBinding, |
| ronshapiro | 186d17a | 2017-08-11 08:18:07 -0700 | [diff] [blame] | 215 | providerBindingExpression, |
| erichang | 9dd6aed | 2017-06-15 15:54:00 -0700 | [diff] [blame] | 216 | hasBindingExpressions); |
| 217 | } |
| 218 | // fall through |
| 219 | default: |
| ronshapiro | 186d17a | 2017-08-11 08:18:07 -0700 | [diff] [blame] | 220 | return providerBindingExpression; |
| erichang | 9dd6aed | 2017-06-15 15:54:00 -0700 | [diff] [blame] | 221 | } |
| 222 | default: |
| 223 | throw new AssertionError(); |
| 224 | } |
| 225 | } |
| 226 | } |
| erichang | 9dd6aed | 2017-06-15 15:54:00 -0700 | [diff] [blame] | 227 | } |