1 /* 2 * Copyright (C) 2021 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 dagger.internal.codegen.model.BindingKind.DELEGATE; 20 import static dagger.internal.codegen.writing.DelegateRequestRepresentation.isBindsScopeStrongerThanDependencyScope; 21 22 import dagger.assisted.Assisted; 23 import dagger.assisted.AssistedFactory; 24 import dagger.assisted.AssistedInject; 25 import dagger.internal.codegen.binding.BindingGraph; 26 import dagger.internal.codegen.binding.BindingRequest; 27 import dagger.internal.codegen.binding.ProvisionBinding; 28 import dagger.internal.codegen.model.RequestKind; 29 import dagger.internal.codegen.writing.ComponentImplementation.CompilerMode; 30 31 /** 32 * A binding representation that wraps code generation methods that satisfy all kinds of request for 33 * that binding. 34 */ 35 final class ProvisionBindingRepresentation implements BindingRepresentation { 36 private final BindingGraph graph; 37 private final CompilerMode compilerMode; 38 private final ProvisionBinding binding; 39 private final DirectInstanceBindingRepresentation directInstanceBindingRepresentation; 40 private final FrameworkInstanceBindingRepresentation frameworkInstanceBindingRepresentation; 41 42 @AssistedInject ProvisionBindingRepresentation( @ssisted ProvisionBinding binding, DirectInstanceBindingRepresentation.Factory directInstanceBindingRepresentationFactory, FrameworkInstanceBindingRepresentation.Factory frameworkInstanceBindingRepresentationFactory, BindingGraph graph, ComponentImplementation componentImplementation)43 ProvisionBindingRepresentation( 44 @Assisted ProvisionBinding binding, 45 DirectInstanceBindingRepresentation.Factory directInstanceBindingRepresentationFactory, 46 FrameworkInstanceBindingRepresentation.Factory frameworkInstanceBindingRepresentationFactory, 47 BindingGraph graph, 48 ComponentImplementation componentImplementation) { 49 this.binding = binding; 50 this.graph = graph; 51 this.compilerMode = componentImplementation.compilerMode(); 52 this.directInstanceBindingRepresentation = 53 directInstanceBindingRepresentationFactory.create(binding); 54 this.frameworkInstanceBindingRepresentation = 55 frameworkInstanceBindingRepresentationFactory.create(binding); 56 } 57 58 @Override getRequestRepresentation(BindingRequest request)59 public RequestRepresentation getRequestRepresentation(BindingRequest request) { 60 return usesDirectInstanceExpression(request.requestKind()) 61 ? directInstanceBindingRepresentation.getRequestRepresentation(request) 62 : frameworkInstanceBindingRepresentation.getRequestRepresentation(request); 63 } 64 usesDirectInstanceExpression(RequestKind requestKind)65 private boolean usesDirectInstanceExpression(RequestKind requestKind) { 66 if (requestKind != RequestKind.INSTANCE && requestKind != RequestKind.FUTURE) { 67 return false; 68 } 69 70 // In fast init mode, we can avoid generating direct instance expressions if a framework 71 // instance expression already exists in the graph. Default mode has more edge cases, so can not 72 // be handled with simple pre-check in the graph. For example, a provider for a subcomponent 73 // builder is backed with its direct instance, returning framework instance for both cases will 74 // form a loop. There are also difficulties introduced by manually created framework requests. 75 // TODO(wanyingd): refactor framework instance so that we don't need to generate both direct 76 // instance and framework instance representation for the same binding. 77 if (compilerMode.isFastInit() && graph.topLevelBindingGraph().hasFrameworkRequest(binding)) { 78 return false; 79 } 80 81 switch (binding.kind()) { 82 case MEMBERS_INJECTOR: 83 // Currently, we always use a framework instance for MembersInjectors, e.g. 84 // InstanceFactory.create(Foo_MembersInjector.create(...)). 85 // TODO(b/199889259): Consider optimizing this for fastInit mode. 86 case ASSISTED_FACTORY: 87 // Assisted factory binding can be requested with framework request, and it is essentially a 88 // provider for assisted injection binding. So we will always return framework instance for 89 // assisted factory bindings. 90 return false; 91 case ASSISTED_INJECTION: 92 throw new IllegalStateException( 93 "Assisted injection binding shouldn't be requested with an instance request."); 94 default: 95 // We don't need to use Provider#get() if there's no caching, so use a direct instance. 96 // TODO(bcorso): This can be optimized in cases where we know a Provider field already 97 // exists, in which case even if it's not scoped we might as well call Provider#get(). 98 return !needsCaching(binding, graph); 99 } 100 } 101 102 /** 103 * Returns {@code true} if the component needs to make sure the provided value is cached. 104 * 105 * <p>The component needs to cache the value for scoped bindings except for {@code @Binds} 106 * bindings whose scope is no stronger than their delegate's. 107 */ needsCaching(ProvisionBinding binding, BindingGraph graph)108 static boolean needsCaching(ProvisionBinding binding, BindingGraph graph) { 109 if (!binding.scope().isPresent()) { 110 return false; 111 } 112 if (binding.kind().equals(DELEGATE)) { 113 return isBindsScopeStrongerThanDependencyScope(binding, graph); 114 } 115 return true; 116 } 117 118 @AssistedFactory 119 static interface Factory { create(ProvisionBinding binding)120 ProvisionBindingRepresentation create(ProvisionBinding binding); 121 } 122 } 123