|
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,45 @@ 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 |
|
116 | | - boolean canDeserialize = controller.getClassNode().hasMethod(createDeserializeLambdaMethodName(lambdaClass), createDeserializeLambdaMethodParams()); |
117 | | - if (!canDeserialize) { |
118 | | - if (expression.isSerializable()) { |
119 | | - addDeserializeLambdaMethodForEachLambdaExpression(expression, lambdaClass); |
120 | | - addDeserializeLambdaMethod(); |
| 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 && !expression.isSerializable(); |
| 122 | + |
| 123 | + if (isNonCapturing) { |
| 124 | + lambdaMethod.setModifiers(lambdaMethod.getModifiers() | ACC_STATIC); |
| 125 | + } else { |
| 126 | + boolean canDeserialize = controller.getClassNode().hasMethod(createDeserializeLambdaMethodName(lambdaClass), createDeserializeLambdaMethodParams()); |
| 127 | + if (!canDeserialize) { |
| 128 | + if (expression.isSerializable()) { |
| 129 | + addDeserializeLambdaMethodForEachLambdaExpression(expression, lambdaClass); |
| 130 | + addDeserializeLambdaMethod(); |
| 131 | + } |
| 132 | + newGroovyLambdaWrapperAndLoad(lambdaClass, expression, accessingInstanceMembers); |
121 | 133 | } |
122 | | - newGroovyLambdaWrapperAndLoad(lambdaClass, expression, isAccessingInstanceMembersOfEnclosingClass(lambdaMethod)); |
123 | 134 | } |
124 | 135 |
|
125 | 136 | MethodVisitor mv = controller.getMethodVisitor(); |
126 | 137 | mv.visitInvokeDynamicInsn( |
127 | 138 | abstractMethod.getName(), |
128 | | - createAbstractMethodDesc(functionalType.redirect(), lambdaClass), |
| 139 | + isNonCapturing |
| 140 | + ? BytecodeHelper.getMethodDescriptor(functionalType.redirect(), Parameter.EMPTY_ARRAY) |
| 141 | + : createAbstractMethodDesc(functionalType.redirect(), lambdaClass), |
129 | 142 | createBootstrapMethod(controller.getClassNode().isInterface(), expression.isSerializable()), |
130 | | - createBootstrapMethodArguments(createMethodDescriptor(abstractMethod), H_INVOKEVIRTUAL, lambdaClass, lambdaMethod, lambdaMethod.getParameters(), expression.isSerializable()) |
| 143 | + createBootstrapMethodArguments(createMethodDescriptor(abstractMethod), |
| 144 | + isNonCapturing ? H_INVOKESTATIC : H_INVOKEVIRTUAL, |
| 145 | + lambdaClass, lambdaMethod, lambdaMethod.getParameters(), expression.isSerializable()) |
131 | 146 | ); |
132 | 147 | if (expression.isSerializable()) { |
133 | 148 | mv.visitTypeInsn(CHECKCAST, "java/io/Serializable"); |
134 | 149 | } |
135 | 150 |
|
136 | | - controller.getOperandStack().replace(functionalType.redirect(), 1); |
| 151 | + if (isNonCapturing) { |
| 152 | + controller.getOperandStack().push(functionalType.redirect()); |
| 153 | + } else { |
| 154 | + controller.getOperandStack().replace(functionalType.redirect(), 1); |
| 155 | + } |
137 | 156 | } |
138 | 157 |
|
139 | 158 | private static Parameter[] createDeserializeLambdaMethodParams() { |
|
0 commit comments