|
57 | 57 | private static final String METHOD_DESCRIPTOR_VOID = Type.getMethodDescriptor(Type.VOID_TYPE);
|
58 | 58 | private static final String JAVA_LANG_OBJECT = "java/lang/Object";
|
59 | 59 | private static final String NAME_CTOR = "<init>";
|
| 60 | + private static final String LAMBDA_INSTANCE_FIELD = "LAMBDA_INSTANCE$"; |
60 | 61 |
|
61 | 62 | //Serialization support
|
62 | 63 | private static final String NAME_SERIALIZED_LAMBDA = "java/lang/invoke/SerializedLambda";
|
@@ -206,32 +207,42 @@ private static String lambdaClassName(Class<?> targetClass) {
|
206 | 207 | @Override
|
207 | 208 | CallSite buildCallSite() throws LambdaConversionException {
|
208 | 209 | final Class<?> innerClass = spinInnerClass();
|
209 |
| - if (invokedType.parameterCount() == 0 && !disableEagerInitialization) { |
| 210 | + if (invokedType.parameterCount() == 0) { |
210 | 211 | // In the case of a non-capturing lambda, we optimize linkage by pre-computing a single instance,
|
211 | 212 | // unless we've suppressed eager initialization
|
212 |
| - final Constructor<?>[] ctrs = AccessController.doPrivileged( |
213 |
| - new PrivilegedAction<>() { |
214 |
| - @Override |
215 |
| - public Constructor<?>[] run() { |
216 |
| - Constructor<?>[] ctrs = innerClass.getDeclaredConstructors(); |
217 |
| - if (ctrs.length == 1) { |
218 |
| - // The lambda implementing inner class constructor is private, set |
219 |
| - // it accessible (by us) before creating the constant sole instance |
220 |
| - ctrs[0].setAccessible(true); |
221 |
| - } |
222 |
| - return ctrs; |
| 213 | + if (disableEagerInitialization) { |
| 214 | + try { |
| 215 | + return new ConstantCallSite(caller.findStaticGetter(innerClass, LAMBDA_INSTANCE_FIELD, |
| 216 | + invokedType.returnType())); |
| 217 | + } catch (ReflectiveOperationException e) { |
| 218 | + throw new LambdaConversionException( |
| 219 | + "Exception finding " + LAMBDA_INSTANCE_FIELD + " static field", e); |
| 220 | + } |
| 221 | + } else { |
| 222 | + final Constructor<?>[] ctrs = AccessController.doPrivileged( |
| 223 | + new PrivilegedAction<>() { |
| 224 | + @Override |
| 225 | + public Constructor<?>[] run() { |
| 226 | + Constructor<?>[] ctrs = innerClass.getDeclaredConstructors(); |
| 227 | + if (ctrs.length == 1) { |
| 228 | + // The lambda implementing inner class constructor is private, set |
| 229 | + // it accessible (by us) before creating the constant sole instance |
| 230 | + ctrs[0].setAccessible(true); |
| 231 | + } |
| 232 | + return ctrs; |
| 233 | + } |
| 234 | + }); |
| 235 | + if (ctrs.length != 1) { |
| 236 | + throw new LambdaConversionException("Expected one lambda constructor for " |
| 237 | + + innerClass.getCanonicalName() + ", got " + ctrs.length); |
223 | 238 | }
|
224 |
| - }); |
225 |
| - if (ctrs.length != 1) { |
226 |
| - throw new LambdaConversionException("Expected one lambda constructor for " |
227 |
| - + innerClass.getCanonicalName() + ", got " + ctrs.length); |
228 |
| - } |
229 | 239 |
|
230 |
| - try { |
231 |
| - Object inst = ctrs[0].newInstance(); |
232 |
| - return new ConstantCallSite(MethodHandles.constant(samBase, inst)); |
233 |
| - } catch (ReflectiveOperationException e) { |
234 |
| - throw new LambdaConversionException("Exception instantiating lambda object", e); |
| 240 | + try { |
| 241 | + Object inst = ctrs[0].newInstance(); |
| 242 | + return new ConstantCallSite(MethodHandles.constant(samBase, inst)); |
| 243 | + } catch (ReflectiveOperationException e) { |
| 244 | + throw new LambdaConversionException("Exception instantiating lambda object", e); |
| 245 | + } |
235 | 246 | }
|
236 | 247 | } else {
|
237 | 248 | try {
|
@@ -331,6 +342,10 @@ private Class<?> generateInnerClass() throws LambdaConversionException {
|
331 | 342 |
|
332 | 343 | generateConstructor();
|
333 | 344 |
|
| 345 | + if (invokedType.parameterCount() == 0 && disableEagerInitialization) { |
| 346 | + generateClassInitializer(); |
| 347 | + } |
| 348 | + |
334 | 349 | // Forward the SAM method
|
335 | 350 | MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, samMethodName,
|
336 | 351 | samMethodType.toMethodDescriptorString(), null, null);
|
@@ -398,6 +413,32 @@ public Void run() {
|
398 | 413 | }
|
399 | 414 | }
|
400 | 415 |
|
| 416 | + /** |
| 417 | + * Generate a static field and a static initializer that sets this field to an instance of the lambda |
| 418 | + */ |
| 419 | + private void generateClassInitializer() { |
| 420 | + String lambdaTypeDescriptor = invokedType.returnType().descriptorString(); |
| 421 | + |
| 422 | + // Generate the static final field that holds the lambda singleton |
| 423 | + FieldVisitor fv = cw.visitField(ACC_PRIVATE | ACC_STATIC | ACC_FINAL, |
| 424 | + LAMBDA_INSTANCE_FIELD, lambdaTypeDescriptor, null, null); |
| 425 | + fv.visitEnd(); |
| 426 | + |
| 427 | + // Instantiate the lambda and store it to the static final field |
| 428 | + MethodVisitor clinit = cw.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null); |
| 429 | + clinit.visitCode(); |
| 430 | + |
| 431 | + clinit.visitTypeInsn(NEW, lambdaClassName); |
| 432 | + clinit.visitInsn(Opcodes.DUP); |
| 433 | + assert invokedType.parameterCount() == 0; |
| 434 | + clinit.visitMethodInsn(INVOKESPECIAL, lambdaClassName, NAME_CTOR, constructorType.toMethodDescriptorString(), false); |
| 435 | + clinit.visitFieldInsn(PUTSTATIC, lambdaClassName, LAMBDA_INSTANCE_FIELD, lambdaTypeDescriptor); |
| 436 | + |
| 437 | + clinit.visitInsn(RETURN); |
| 438 | + clinit.visitMaxs(-1, -1); |
| 439 | + clinit.visitEnd(); |
| 440 | + } |
| 441 | + |
401 | 442 | /**
|
402 | 443 | * Generate the constructor for the class
|
403 | 444 | */
|
|
0 commit comments