1 /* 2 * Copyright (C) 2015 The Dagger Authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package dagger.internal.codegen.writing; 18 19 import static androidx.room.compiler.codegen.XTypeNameKt.toJavaPoet; 20 import static com.google.common.base.Preconditions.checkNotNull; 21 import static dagger.internal.codegen.binding.SourceFiles.generatedClassNameForBinding; 22 import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.RAWTYPES; 23 import static dagger.internal.codegen.writing.ComponentImplementation.FieldSpecKind.FRAMEWORK_FIELD; 24 import static javax.lang.model.element.Modifier.PRIVATE; 25 26 import androidx.room.compiler.codegen.XClassName; 27 import androidx.room.compiler.codegen.XTypeName; 28 import androidx.room.compiler.processing.XType; 29 import com.squareup.javapoet.ClassName; 30 import com.squareup.javapoet.CodeBlock; 31 import com.squareup.javapoet.FieldSpec; 32 import dagger.internal.DelegateFactory; 33 import dagger.internal.codegen.binding.BindingType; 34 import dagger.internal.codegen.binding.ContributionBinding; 35 import dagger.internal.codegen.binding.FrameworkField; 36 import dagger.internal.codegen.javapoet.AnnotationSpecs; 37 import dagger.internal.codegen.javapoet.TypeNames; 38 import dagger.internal.codegen.model.BindingKind; 39 import dagger.internal.codegen.writing.ComponentImplementation.ShardImplementation; 40 import dagger.internal.codegen.xprocessing.XTypeNames; 41 import java.util.Optional; 42 43 /** 44 * An object that can initialize a framework-type component field for a binding. An instance should 45 * be created for each field. 46 */ 47 class FrameworkFieldInitializer implements FrameworkInstanceSupplier { 48 49 /** 50 * An object that can determine the expression to use to assign to the component field for a 51 * binding. 52 */ 53 interface FrameworkInstanceCreationExpression { 54 /** Returns the expression to use to assign to the component field for the binding. */ creationExpression()55 CodeBlock creationExpression(); 56 57 /** 58 * Returns the framework class to use for the field, if different from the one implied by the 59 * binding. This implementation returns {@link Optional#empty()}. 60 */ alternativeFrameworkClass()61 default Optional<XClassName> alternativeFrameworkClass() { 62 return Optional.empty(); 63 } 64 } 65 66 private final ShardImplementation shardImplementation; 67 private final ContributionBinding binding; 68 private final FrameworkInstanceCreationExpression frameworkInstanceCreationExpression; 69 private FieldSpec fieldSpec; 70 private InitializationState fieldInitializationState = InitializationState.UNINITIALIZED; 71 FrameworkFieldInitializer( ComponentImplementation componentImplementation, ContributionBinding binding, FrameworkInstanceCreationExpression frameworkInstanceCreationExpression)72 FrameworkFieldInitializer( 73 ComponentImplementation componentImplementation, 74 ContributionBinding binding, 75 FrameworkInstanceCreationExpression frameworkInstanceCreationExpression) { 76 this.binding = checkNotNull(binding); 77 this.shardImplementation = checkNotNull(componentImplementation).shardImplementation(binding); 78 this.frameworkInstanceCreationExpression = checkNotNull(frameworkInstanceCreationExpression); 79 } 80 81 /** 82 * Returns the {@link MemberSelect} for the framework field, and adds the field and its 83 * initialization code to the component if it's needed and not already added. 84 */ 85 @Override memberSelect()86 public final MemberSelect memberSelect() { 87 initializeField(); 88 return MemberSelect.localField(shardImplementation, checkNotNull(fieldSpec).name); 89 } 90 91 /** Adds the field and its initialization code to the component. */ initializeField()92 private void initializeField() { 93 switch (fieldInitializationState) { 94 case UNINITIALIZED: 95 // Change our state in case we are recursively invoked via initializeRequestRepresentation 96 fieldInitializationState = InitializationState.INITIALIZING; 97 CodeBlock.Builder codeBuilder = CodeBlock.builder(); 98 CodeBlock fieldInitialization = frameworkInstanceCreationExpression.creationExpression(); 99 CodeBlock initCode = CodeBlock.of("this.$N = $L;", getOrCreateField(), fieldInitialization); 100 101 if (fieldInitializationState == InitializationState.DELEGATED) { 102 codeBuilder.add( 103 "$T.setDelegate($N, $L);", delegateType(), fieldSpec, fieldInitialization); 104 } else { 105 codeBuilder.add(initCode); 106 } 107 shardImplementation.addInitialization(codeBuilder.build()); 108 109 fieldInitializationState = InitializationState.INITIALIZED; 110 break; 111 112 case INITIALIZING: 113 fieldSpec = getOrCreateField(); 114 // We were recursively invoked, so create a delegate factory instead to break the loop. 115 116 // TODO(erichang): For the most part SwitchingProvider takes no dependencies so even if they 117 // are recursively invoked, we don't need to delegate it since there is no dependency cycle. 118 // However, there is a case with a scoped @Binds where we reference the impl binding when 119 // passing it into DoubleCheck. For this case, we do need to delegate it. There might be 120 // a way to only do delegates in this situation, but we'd need to keep track of what other 121 // bindings use this. 122 123 fieldInitializationState = InitializationState.DELEGATED; 124 shardImplementation.addInitialization( 125 CodeBlock.of("this.$N = new $T<>();", fieldSpec, delegateType())); 126 break; 127 128 case DELEGATED: 129 case INITIALIZED: 130 break; 131 } 132 } 133 134 /** 135 * Adds a field representing the resolved bindings, optionally forcing it to use a particular 136 * binding type (instead of the type the resolved bindings would typically use). 137 */ getOrCreateField()138 private FieldSpec getOrCreateField() { 139 if (fieldSpec != null) { 140 return fieldSpec; 141 } 142 boolean useRawType = !shardImplementation.isTypeAccessible(binding.key().type().xprocessing()); 143 FrameworkField contributionBindingField = 144 FrameworkField.forBinding( 145 binding, frameworkInstanceCreationExpression.alternativeFrameworkClass()); 146 147 XTypeName fieldType = useRawType 148 ? contributionBindingField.type().getRawTypeName() 149 : contributionBindingField.type(); 150 151 if (binding.kind() == BindingKind.ASSISTED_INJECTION) { 152 // An assisted injection factory doesn't extend Provider, so we reference the generated 153 // factory type directly (i.e. Foo_Factory<T> instead of Provider<Foo<T>>). 154 XTypeName[] typeParameters = 155 binding.key().type().xprocessing().getTypeArguments().stream() 156 .map(XType::asTypeName) 157 .toArray(XTypeName[]::new); 158 fieldType = 159 typeParameters.length == 0 160 ? generatedClassNameForBinding(binding) 161 : generatedClassNameForBinding(binding).parametrizedBy(typeParameters); 162 } 163 164 FieldSpec.Builder contributionField = 165 FieldSpec.builder( 166 toJavaPoet(fieldType), 167 shardImplementation.getUniqueFieldName(contributionBindingField.name())); 168 contributionField.addModifiers(PRIVATE); 169 if (useRawType) { 170 contributionField.addAnnotation(AnnotationSpecs.suppressWarnings(RAWTYPES)); 171 } 172 173 fieldSpec = contributionField.build(); 174 shardImplementation.addField(FRAMEWORK_FIELD, fieldSpec); 175 176 return fieldSpec; 177 } 178 delegateType()179 private ClassName delegateType() { 180 return isProvider() ? TypeNames.DELEGATE_FACTORY : TypeNames.DELEGATE_PRODUCER; 181 } 182 isProvider()183 private boolean isProvider() { 184 return binding.bindingType().equals(BindingType.PROVISION) 185 && frameworkInstanceCreationExpression 186 .alternativeFrameworkClass() 187 .map(XTypeNames.PROVIDER::equals) 188 .orElse(true); 189 } 190 191 /** Initialization state for a factory field. */ 192 private enum InitializationState { 193 /** The field is {@code null}. */ 194 UNINITIALIZED, 195 196 /** 197 * The field's dependencies are being set up. If the field is needed in this state, use a {@link 198 * DelegateFactory}. 199 */ 200 INITIALIZING, 201 202 /** 203 * The field's dependencies are being set up, but the field can be used because it has already 204 * been set to a {@link DelegateFactory}. 205 */ 206 DELEGATED, 207 208 /** The field is set to an undelegated factory. */ 209 INITIALIZED; 210 } 211 } 212