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.base.Util.reentrantComputeIfAbsent; 20 import static dagger.internal.codegen.binding.BindingRequest.bindingRequest; 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.ComponentDescriptor.ComponentMethodDescriptor; 28 import dagger.internal.codegen.binding.ProvisionBinding; 29 import dagger.internal.codegen.model.RequestKind; 30 import dagger.internal.codegen.writing.ComponentImplementation.ShardImplementation; 31 import java.util.HashMap; 32 import java.util.Map; 33 import java.util.Optional; 34 35 /** Returns request representation based on a direct instance expression. */ 36 final class DirectInstanceBindingRepresentation { 37 private final ProvisionBinding binding; 38 private final BindingGraph graph; 39 private final ComponentImplementation componentImplementation; 40 private final ComponentMethodRequestRepresentation.Factory 41 componentMethodRequestRepresentationFactory; 42 private final ImmediateFutureRequestRepresentation.Factory 43 immediateFutureRequestRepresentationFactory; 44 private final PrivateMethodRequestRepresentation.Factory 45 privateMethodRequestRepresentationFactory; 46 private final UnscopedDirectInstanceRequestRepresentationFactory 47 unscopedDirectInstanceRequestRepresentationFactory; 48 private final Map<BindingRequest, RequestRepresentation> requestRepresentations = new HashMap<>(); 49 50 @AssistedInject DirectInstanceBindingRepresentation( @ssisted ProvisionBinding binding, BindingGraph graph, ComponentImplementation componentImplementation, ComponentMethodRequestRepresentation.Factory componentMethodRequestRepresentationFactory, ImmediateFutureRequestRepresentation.Factory immediateFutureRequestRepresentationFactory, PrivateMethodRequestRepresentation.Factory privateMethodRequestRepresentationFactory, UnscopedDirectInstanceRequestRepresentationFactory unscopedDirectInstanceRequestRepresentationFactory)51 DirectInstanceBindingRepresentation( 52 @Assisted ProvisionBinding binding, 53 BindingGraph graph, 54 ComponentImplementation componentImplementation, 55 ComponentMethodRequestRepresentation.Factory componentMethodRequestRepresentationFactory, 56 ImmediateFutureRequestRepresentation.Factory immediateFutureRequestRepresentationFactory, 57 PrivateMethodRequestRepresentation.Factory privateMethodRequestRepresentationFactory, 58 UnscopedDirectInstanceRequestRepresentationFactory 59 unscopedDirectInstanceRequestRepresentationFactory) { 60 this.binding = binding; 61 this.graph = graph; 62 this.componentImplementation = componentImplementation; 63 this.componentMethodRequestRepresentationFactory = componentMethodRequestRepresentationFactory; 64 this.immediateFutureRequestRepresentationFactory = immediateFutureRequestRepresentationFactory; 65 this.privateMethodRequestRepresentationFactory = privateMethodRequestRepresentationFactory; 66 this.unscopedDirectInstanceRequestRepresentationFactory = 67 unscopedDirectInstanceRequestRepresentationFactory; 68 } 69 getRequestRepresentation(BindingRequest request)70 public RequestRepresentation getRequestRepresentation(BindingRequest request) { 71 return reentrantComputeIfAbsent( 72 requestRepresentations, request, this::getRequestRepresentationUncached); 73 } 74 getRequestRepresentationUncached(BindingRequest request)75 private RequestRepresentation getRequestRepresentationUncached(BindingRequest request) { 76 switch (request.requestKind()) { 77 case INSTANCE: 78 return requiresMethodEncapsulation(binding) 79 ? wrapInMethod(unscopedDirectInstanceRequestRepresentationFactory.create(binding)) 80 : unscopedDirectInstanceRequestRepresentationFactory.create(binding); 81 82 case FUTURE: 83 return immediateFutureRequestRepresentationFactory.create( 84 getRequestRepresentation(bindingRequest(binding.key(), RequestKind.INSTANCE)), 85 binding.key().type().xprocessing()); 86 87 default: 88 throw new AssertionError( 89 String.format("Invalid binding request kind: %s", request.requestKind())); 90 } 91 } 92 93 /** 94 * Returns a binding expression that uses a given one as the body of a method that users call. If 95 * a component provision method matches it, it will be the method implemented. If it does not 96 * match a component provision method and the binding is modifiable, then a new public modifiable 97 * binding method will be written. If the binding doesn't match a component method and is not 98 * modifiable, then a new private method will be written. 99 */ wrapInMethod(RequestRepresentation bindingExpression)100 RequestRepresentation wrapInMethod(RequestRepresentation bindingExpression) { 101 // If we've already wrapped the expression, then use the delegate. 102 if (bindingExpression instanceof MethodRequestRepresentation) { 103 return bindingExpression; 104 } 105 106 BindingRequest request = bindingRequest(binding.key(), RequestKind.INSTANCE); 107 Optional<ComponentMethodDescriptor> matchingComponentMethod = 108 graph.componentDescriptor().firstMatchingComponentMethod(request); 109 110 ShardImplementation shardImplementation = componentImplementation.shardImplementation(binding); 111 112 // Consider the case of a request from a component method like: 113 // 114 // DaggerMyComponent extends MyComponent { 115 // @Overrides 116 // Foo getFoo() { 117 // <FOO_BINDING_REQUEST> 118 // } 119 // } 120 // 121 // Normally, in this case we would return a ComponentMethodRequestRepresentation rather than a 122 // PrivateMethodRequestRepresentation so that #getFoo() can inline the implementation rather 123 // than 124 // create an unnecessary private method and return that. However, with sharding we don't want to 125 // inline the implementation because that would defeat some of the class pool savings if those 126 // fields had to communicate across shards. Thus, when a key belongs to a separate shard use a 127 // PrivateMethodRequestRepresentation and put the private method in the shard. 128 if (matchingComponentMethod.isPresent() && shardImplementation.isComponentShard()) { 129 ComponentMethodDescriptor componentMethod = matchingComponentMethod.get(); 130 return componentMethodRequestRepresentationFactory.create(bindingExpression, componentMethod); 131 } else { 132 return privateMethodRequestRepresentationFactory.create(request, binding, bindingExpression); 133 } 134 } 135 requiresMethodEncapsulation(ProvisionBinding binding)136 private static boolean requiresMethodEncapsulation(ProvisionBinding binding) { 137 switch (binding.kind()) { 138 case COMPONENT: 139 case COMPONENT_PROVISION: 140 case SUBCOMPONENT_CREATOR: 141 case COMPONENT_DEPENDENCY: 142 case MULTIBOUND_SET: 143 case MULTIBOUND_MAP: 144 case BOUND_INSTANCE: 145 case ASSISTED_FACTORY: 146 case ASSISTED_INJECTION: 147 case INJECTION: 148 case PROVISION: 149 // These binding kinds satify a binding request with a component method or a private 150 // method when the requested binding has dependencies. The method will wrap the logic of 151 // creating the binding instance. Without the encapsulation, we might see many levels of 152 // nested instance creation code in a single statement to satisfy all dependencies of a 153 // binding request. 154 return !binding.dependencies().isEmpty(); 155 case MEMBERS_INJECTOR: 156 case PRODUCTION: 157 case COMPONENT_PRODUCTION: 158 case OPTIONAL: 159 case DELEGATE: 160 case MEMBERS_INJECTION: 161 return false; 162 } 163 throw new AssertionError(String.format("No such binding kind: %s", binding.kind())); 164 } 165 166 @AssistedFactory 167 static interface Factory { create(ProvisionBinding binding)168 DirectInstanceBindingRepresentation create(ProvisionBinding binding); 169 } 170 } 171