17
17
package com .google .inject .internal ;
18
18
19
19
import static com .google .common .base .Preconditions .checkNotNull ;
20
+ import static com .google .inject .internal .InternalMethodHandles .castReturnTo ;
21
+ import static com .google .inject .internal .InternalMethodHandles .castReturnToObject ;
22
+ import static com .google .inject .internal .InternalMethodHandles .findVirtualOrDie ;
23
+ import static java .lang .invoke .MethodType .methodType ;
20
24
25
+ import com .google .errorprone .annotations .Keep ;
21
26
import com .google .errorprone .annotations .concurrent .LazyInit ;
22
27
import com .google .inject .Provider ;
23
28
import com .google .inject .Scope ;
24
29
import com .google .inject .spi .Dependency ;
30
+ import java .lang .invoke .MethodHandle ;
31
+ import java .lang .invoke .MethodHandles ;
32
+ import java .lang .invoke .MutableCallSite ;
25
33
26
34
/**
27
35
* A factory that wraps a provider that has been scoped.
@@ -66,6 +74,37 @@ public T get(InternalContext context, Dependency<?> dependency, boolean linked)
66
74
}
67
75
}
68
76
77
+ @ Override
78
+ public MethodHandle getHandle (LinkageContext context , Dependency <?> dependency , boolean linked ) {
79
+ // Call Provider.get() on the constant provider
80
+ // ()->Object
81
+ var invokeProvider =
82
+ InternalMethodHandles .getProvider (
83
+ MethodHandles .constant (jakarta .inject .Provider .class , provider ));
84
+
85
+ // null check the result using the dependency.
86
+ // ()->Object
87
+ invokeProvider = InternalMethodHandles .nullCheckResult (invokeProvider , source , dependency );
88
+ // Catch any RuntimeException as an InternalProvisionException.
89
+ // ()->Object
90
+ invokeProvider =
91
+ InternalMethodHandles .catchErrorInProviderAndRethrowWithSource (invokeProvider , source );
92
+ // (InternalContext) -> Object
93
+ // Add an InternalContext argument to match the factory protocol.
94
+ invokeProvider = MethodHandles .dropArguments (invokeProvider , 0 , InternalContext .class );
95
+ // We need to call 'setDependency' so it is available to scope implementations and scope
96
+ // delegate providers. See comment in `get` method for more details.
97
+ invokeProvider =
98
+ MethodHandles .foldArguments (
99
+ invokeProvider ,
100
+ MethodHandles .insertArguments (INTERNAL_CONTEXT_SET_DEPENDENCY_HANDLE , 1 , dependency ));
101
+ return castReturnTo (invokeProvider , dependency .getKey ().getTypeLiteral ().getRawType ());
102
+ }
103
+
104
+ private static final MethodHandle INTERNAL_CONTEXT_SET_DEPENDENCY_HANDLE =
105
+ InternalMethodHandles .findVirtualOrDie (
106
+ InternalContext .class , "setDependency" , methodType (void .class , Dependency .class ));
107
+
69
108
@ Override
70
109
public String toString () {
71
110
return provider .toString ();
@@ -91,15 +130,101 @@ static final class ForSingletonScope<T> extends InternalFactoryToScopedProviderA
91
130
super (provider , source );
92
131
}
93
132
133
+ @ Override
134
+ public T get (InternalContext context , Dependency <?> dependency , boolean linked )
135
+ throws InternalProvisionException {
136
+ Object value = this .value ;
137
+ if (value != UNINITIALIZED_VALUE ) {
138
+ // safe because we only store values of T or UNINITIALIZED_VALUE
139
+ @ SuppressWarnings ("unchecked" )
140
+ T typedValue = (T ) value ;
141
+ if (typedValue == null && !dependency .isNullable ()) {
142
+ InternalProvisionException .onNullInjectedIntoNonNullableDependency (source , dependency );
143
+ }
144
+ return typedValue ;
145
+ }
146
+ T t = super .get (context , dependency , linked );
147
+ if (!context .areCircularProxiesEnabled () || !BytecodeGen .isCircularProxy (t )) {
148
+ // Avoid caching circular proxies.
149
+ this .value = t ;
150
+ }
151
+ return t ;
152
+ }
153
+
154
+ @ Override
155
+ public MethodHandle getHandle (
156
+ LinkageContext context , Dependency <?> dependency , boolean linked ) {
157
+ // If it is somehow already initialized, we can return a constant handle.
158
+ Object value = this .value ;
159
+ if (value != UNINITIALIZED_VALUE ) {
160
+ return getHandleForConstant (dependency , source , value );
161
+ }
162
+ // Otherwise we bind to a callsite that will patch itself once it is initialized.
163
+ return new SingletonCallSite (super .getHandle (context , dependency , linked ), dependency , source )
164
+ .dynamicInvoker ();
165
+ }
166
+
167
+ private static MethodHandle getHandleForConstant (
168
+ Dependency <?> dependency , Object source , Object value ) {
169
+ var handle = InternalMethodHandles .constantFactoryGetHandle (dependency , value );
170
+ handle = InternalMethodHandles .nullCheckResult (handle , source , dependency );
171
+ return handle ;
172
+ }
173
+
174
+ /**
175
+ * A special callsite that allows us to take advantage of the singleton scope semantics.
176
+ *
177
+ * <p>After initialization we can patch the callsite with a constant, which should unlock some
178
+ * inlining opportunities.
179
+ */
180
+ static final class SingletonCallSite extends MutableCallSite {
181
+ static final MethodHandle BOOTSTRAP_CALL_MH =
182
+ findVirtualOrDie (
183
+ SingletonCallSite .class ,
184
+ "boostrapCallSite" ,
185
+ methodType (Object .class , InternalContext .class , Object .class ));
186
+
187
+ private final Dependency <?> dependency ;
188
+ private final Object source ;
189
+
190
+ SingletonCallSite (MethodHandle actualGetHandle , Dependency <?> dependency , Object source ) {
191
+ super (actualGetHandle .type ());
192
+ this .dependency = dependency ;
193
+ this .source = source ;
194
+ // Invoke the 'actual' handle and then pass the result to the `boostrapCallSite` method.
195
+ // This will allow us to eventually 'fold' the result into the callsite.
196
+ // (InternalContext, InternalContext) -> Object
197
+ var invokeBootstrap =
198
+ MethodHandles .filterArguments (
199
+ BOOTSTRAP_CALL_MH .bindTo (this ), 1 , castReturnToObject (actualGetHandle ));
200
+
201
+ // (InternalContext, InternalContext) -> T
202
+ invokeBootstrap = castReturnTo (invokeBootstrap , actualGetHandle .type ().returnType ());
203
+ // (InternalContext) -> T
204
+ invokeBootstrap =
205
+ MethodHandles .permuteArguments (invokeBootstrap , actualGetHandle .type (), new int [2 ]);
206
+ setTarget (invokeBootstrap );
207
+ }
208
+
209
+ @ Keep
210
+ Object boostrapCallSite (InternalContext context , Object result ) {
211
+ // Don't cache circular proxies.
212
+ if (!context .areCircularProxiesEnabled () || !BytecodeGen .isCircularProxy (result )) {
213
+ setTarget (getHandleForConstant (dependency , source , result ));
214
+ // This ensures that other threads will see the new target. This isn't strictly necessary
215
+ // since the underlying provider is both ThreadSafe and idempotent, but it should improve
216
+ // performance by giving the JIT and easy optimization opportunity.
217
+ MutableCallSite .syncAll (new MutableCallSite [] {this });
218
+ }
219
+ // otherwise we shouldn't cache the result.
220
+ return result ;
221
+ }
222
+ }
223
+
94
224
private T getAndCache (InjectorImpl injector , Dependency <?> dependency )
95
225
throws InternalProvisionException {
96
226
try (InternalContext context = injector .enterContext ()) {
97
- T t = get (context , dependency , /* linked= */ false );
98
- if (!context .areCircularProxiesEnabled () || !BytecodeGen .isCircularProxy (t )) {
99
- // Avoid caching circular proxies.
100
- this .value = t ;
101
- }
102
- return t ;
227
+ return get (context , dependency , /* linked= */ false );
103
228
}
104
229
}
105
230
0 commit comments