|
72 | 72 | import static org.objectweb.asm.Opcodes.ALOAD; |
73 | 73 | import static org.objectweb.asm.Opcodes.CHECKCAST; |
74 | 74 | import static org.objectweb.asm.Opcodes.DUP; |
| 75 | +import static org.objectweb.asm.Opcodes.H_INVOKESTATIC; |
75 | 76 | import static org.objectweb.asm.Opcodes.H_INVOKEVIRTUAL; |
76 | 77 | import static org.objectweb.asm.Opcodes.ICONST_0; |
77 | 78 | import static org.objectweb.asm.Opcodes.INVOKESPECIAL; |
@@ -113,27 +114,47 @@ public void writeLambda(final LambdaExpression expression) { |
113 | 114 | ClassNode lambdaClass = getOrAddLambdaClass(expression, abstractMethod); |
114 | 115 | MethodNode lambdaMethod = lambdaClass.getMethods(DO_CALL).get(0); |
115 | 116 |
|
| 117 | + Parameter[] lambdaSharedVariables = expression.getNodeMetaData(LAMBDA_SHARED_VARIABLES); |
| 118 | + boolean accessingInstanceMembers = isAccessingInstanceMembersOfEnclosingClass(lambdaMethod); |
| 119 | + // For non-capturing lambdas: make doCall static and use a capture-free invokedynamic, |
| 120 | + // so LambdaMetafactory creates a singleton instance — just like Java non-capturing lambdas. |
| 121 | + boolean isNonCapturing = lambdaSharedVariables.length == 0 && !accessingInstanceMembers; |
116 | 122 | boolean canDeserialize = controller.getClassNode().hasMethod(createDeserializeLambdaMethodName(lambdaClass), createDeserializeLambdaMethodParams()); |
117 | | - if (!canDeserialize) { |
| 123 | + |
| 124 | + if (isNonCapturing) { |
| 125 | + lambdaMethod.setModifiers(lambdaMethod.getModifiers() | ACC_STATIC); |
| 126 | + if (expression.isSerializable() && !canDeserialize) { |
| 127 | + addDeserializeLambdaMethodForEachLambdaExpression(expression, lambdaClass, true); |
| 128 | + addDeserializeLambdaMethod(); |
| 129 | + } |
| 130 | + } else if (!canDeserialize) { |
118 | 131 | if (expression.isSerializable()) { |
119 | | - addDeserializeLambdaMethodForEachLambdaExpression(expression, lambdaClass); |
| 132 | + addDeserializeLambdaMethodForEachLambdaExpression(expression, lambdaClass, false); |
120 | 133 | addDeserializeLambdaMethod(); |
121 | 134 | } |
122 | | - newGroovyLambdaWrapperAndLoad(lambdaClass, expression, isAccessingInstanceMembersOfEnclosingClass(lambdaMethod)); |
| 135 | + newGroovyLambdaWrapperAndLoad(lambdaClass, expression, accessingInstanceMembers); |
123 | 136 | } |
124 | 137 |
|
125 | 138 | MethodVisitor mv = controller.getMethodVisitor(); |
126 | 139 | mv.visitInvokeDynamicInsn( |
127 | 140 | abstractMethod.getName(), |
128 | | - createAbstractMethodDesc(functionalType.redirect(), lambdaClass), |
| 141 | + isNonCapturing |
| 142 | + ? BytecodeHelper.getMethodDescriptor(functionalType.redirect(), Parameter.EMPTY_ARRAY) |
| 143 | + : createAbstractMethodDesc(functionalType.redirect(), lambdaClass), |
129 | 144 | createBootstrapMethod(controller.getClassNode().isInterface(), expression.isSerializable()), |
130 | | - createBootstrapMethodArguments(createMethodDescriptor(abstractMethod), H_INVOKEVIRTUAL, lambdaClass, lambdaMethod, lambdaMethod.getParameters(), expression.isSerializable()) |
| 145 | + createBootstrapMethodArguments(createMethodDescriptor(abstractMethod), |
| 146 | + isNonCapturing ? H_INVOKESTATIC : H_INVOKEVIRTUAL, |
| 147 | + lambdaClass, lambdaMethod, lambdaMethod.getParameters(), expression.isSerializable()) |
131 | 148 | ); |
132 | 149 | if (expression.isSerializable()) { |
133 | 150 | mv.visitTypeInsn(CHECKCAST, "java/io/Serializable"); |
134 | 151 | } |
135 | 152 |
|
136 | | - controller.getOperandStack().replace(functionalType.redirect(), 1); |
| 153 | + if (isNonCapturing) { |
| 154 | + controller.getOperandStack().push(functionalType.redirect()); |
| 155 | + } else { |
| 156 | + controller.getOperandStack().replace(functionalType.redirect(), 1); |
| 157 | + } |
137 | 158 | } |
138 | 159 |
|
139 | 160 | private static Parameter[] createDeserializeLambdaMethodParams() { |
@@ -325,27 +346,32 @@ private void addDeserializeLambdaMethod() { |
325 | 346 | code); |
326 | 347 | } |
327 | 348 |
|
328 | | - private void addDeserializeLambdaMethodForEachLambdaExpression(final LambdaExpression expression, final ClassNode lambdaClass) { |
| 349 | + private void addDeserializeLambdaMethodForEachLambdaExpression(final LambdaExpression expression, final ClassNode lambdaClass, final boolean isNonCapturing) { |
329 | 350 | ClassNode enclosingClass = controller.getClassNode(); |
330 | | - Statement code = block( |
331 | | - new BytecodeSequence(new BytecodeInstruction() { |
332 | | - @Override |
333 | | - public void visit(final MethodVisitor mv) { |
334 | | - mv.visitVarInsn(ALOAD, 0); |
335 | | - mv.visitInsn(ICONST_0); |
336 | | - mv.visitMethodInsn( |
337 | | - INVOKEVIRTUAL, |
338 | | - "java/lang/invoke/SerializedLambda", |
339 | | - "getCapturedArg", |
340 | | - "(I)Ljava/lang/Object;", |
341 | | - false); |
342 | | - mv.visitTypeInsn(CHECKCAST, BytecodeHelper.getClassInternalName(lambdaClass)); |
343 | | - OperandStack operandStack = controller.getOperandStack(); |
344 | | - operandStack.push(lambdaClass); |
345 | | - } |
346 | | - }), |
347 | | - returnS(expression) |
348 | | - ); |
| 351 | + Statement code; |
| 352 | + if (isNonCapturing) { |
| 353 | + code = block(returnS(expression)); |
| 354 | + } else { |
| 355 | + code = block( |
| 356 | + new BytecodeSequence(new BytecodeInstruction() { |
| 357 | + @Override |
| 358 | + public void visit(final MethodVisitor mv) { |
| 359 | + mv.visitVarInsn(ALOAD, 0); |
| 360 | + mv.visitInsn(ICONST_0); |
| 361 | + mv.visitMethodInsn( |
| 362 | + INVOKEVIRTUAL, |
| 363 | + "java/lang/invoke/SerializedLambda", |
| 364 | + "getCapturedArg", |
| 365 | + "(I)Ljava/lang/Object;", |
| 366 | + false); |
| 367 | + mv.visitTypeInsn(CHECKCAST, BytecodeHelper.getClassInternalName(lambdaClass)); |
| 368 | + OperandStack operandStack = controller.getOperandStack(); |
| 369 | + operandStack.push(lambdaClass); |
| 370 | + } |
| 371 | + }), |
| 372 | + returnS(expression) |
| 373 | + ); |
| 374 | + } |
349 | 375 |
|
350 | 376 | enclosingClass.addSyntheticMethod( |
351 | 377 | createDeserializeLambdaMethodName(lambdaClass), |
|
0 commit comments