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