Skip to content

Commit

Permalink
GROOVY-11304: SC: scrub lambda generics
Browse files Browse the repository at this point in the history
  • Loading branch information
eric-milles committed Feb 24, 2024
1 parent 6721ac9 commit b1032d2
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 61 deletions.
Expand Up @@ -62,7 +62,7 @@
import static org.codehaus.groovy.ast.tools.GeneralUtils.declS;
import static org.codehaus.groovy.ast.tools.GeneralUtils.localVarX;
import static org.codehaus.groovy.ast.tools.GeneralUtils.returnS;
import static org.codehaus.groovy.transform.stc.StaticTypesMarker.INFERRED_TYPE;
import static org.codehaus.groovy.transform.stc.StaticTypesMarker.CLOSURE_ARGUMENTS;
import static org.codehaus.groovy.transform.stc.StaticTypesMarker.PARAMETER_TYPE;
import static org.objectweb.asm.Opcodes.ACC_FINAL;
import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
Expand Down Expand Up @@ -108,13 +108,10 @@ public void writeLambda(final LambdaExpression expression) {
expression.setSerializable(true);
}

ClassNode enclosingClass = controller.getClassNode();
int modifiers = ACC_FINAL | ACC_PUBLIC;
if (enclosingClass.isInterface()) modifiers |= ACC_STATIC;
ClassNode lambdaClass = getOrAddLambdaClass(expression, modifiers, abstractMethod);
ClassNode lambdaClass = getOrAddLambdaClass(expression, abstractMethod);
MethodNode lambdaMethod = lambdaClass.getMethods("doCall").get(0);

boolean canDeserialize = enclosingClass.hasMethod(createDeserializeLambdaMethodName(lambdaClass), createDeserializeLambdaMethodParams());
boolean canDeserialize = controller.getClassNode().hasMethod(createDeserializeLambdaMethodName(lambdaClass), createDeserializeLambdaMethodParams());
if (!canDeserialize) {
if (expression.isSerializable()) {
addDeserializeLambdaMethodForEachLambdaExpression(expression, lambdaClass);
Expand All @@ -127,7 +124,7 @@ public void writeLambda(final LambdaExpression expression) {
mv.visitInvokeDynamicInsn(
abstractMethod.getName(),
createAbstractMethodDesc(functionalType.redirect(), lambdaClass),
createBootstrapMethod(enclosingClass.isInterface(), expression.isSerializable()),
createBootstrapMethod(controller.getClassNode().isInterface(), expression.isSerializable()),
createBootstrapMethodArguments(createMethodDescriptor(abstractMethod), H_INVOKEVIRTUAL, lambdaClass, lambdaMethod, lambdaMethod.getParameters(), expression.isSerializable())
);
if (expression.isSerializable()) {
Expand Down Expand Up @@ -206,9 +203,9 @@ private String createAbstractMethodDesc(final ClassNode functionalInterface, fin
return BytecodeHelper.getMethodDescriptor(functionalInterface, lambdaSharedVariables.toArray(Parameter.EMPTY_ARRAY));
}

private ClassNode getOrAddLambdaClass(final LambdaExpression expression, final int modifiers, final MethodNode abstractMethod) {
private ClassNode getOrAddLambdaClass(final LambdaExpression expression, final MethodNode abstractMethod) {
return lambdaClassNodes.computeIfAbsent(expression, key -> {
ClassNode lambdaClass = createLambdaClass(expression, modifiers, abstractMethod);
ClassNode lambdaClass = createLambdaClass(expression, ACC_FINAL | ACC_PUBLIC | ACC_STATIC, abstractMethod);
controller.getAcg().addInnerClass(lambdaClass);
lambdaClass.addInterface(GENERATED_LAMBDA_TYPE);
lambdaClass.putNodeMetaData(StaticCompilationMetadataKeys.STATIC_COMPILE_NODE, Boolean.TRUE);
Expand All @@ -225,28 +222,28 @@ protected ClassNode createClosureClass(final ClosureExpression expression, final
protected ClassNode createLambdaClass(final LambdaExpression expression, final int modifiers, final MethodNode abstractMethod) {
ClassNode enclosingClass = controller.getClassNode();
ClassNode outermostClass = controller.getOutermostClass();
boolean staticMethodOrInStaticClass = (controller.isStaticMethod() || enclosingClass.isStaticClass());

InnerClassNode lambdaClass = new InnerClassNode(enclosingClass, nextLambdaClassName(), modifiers, CLOSURE_TYPE.getPlainNodeReference());
//lambdaClass.setUsingGenerics(outermostClass.isUsingGenerics());
lambdaClass.setEnclosingMethod(controller.getMethodNode());
lambdaClass.setSourcePosition(expression);
lambdaClass.setSynthetic(true);

if (controller.isInScriptBody()) {
lambdaClass.setScriptBody(true);
}
if (staticMethodOrInStaticClass) {
if (controller.isStaticMethod()
|| enclosingClass.isStaticClass()) {
lambdaClass.setStaticClass(true);
}
if (expression.isSerializable()) {
addSerialVersionUIDField(lambdaClass);
}

MethodNode syntheticLambdaMethodNode = addSyntheticLambdaMethodNode(expression, lambdaClass, abstractMethod);
Parameter[] localVariableParameters = expression.getNodeMetaData(LAMBDA_SHARED_VARIABLES);

Parameter[] localVariableParameters = expression.getNodeMetaData(LAMBDA_SHARED_VARIABLES);
addFieldsAndGettersForLocalVariables(lambdaClass, localVariableParameters);

ConstructorNode constructorNode = addConstructor(expression, localVariableParameters, lambdaClass, createBlockStatementForConstructor(expression, outermostClass, enclosingClass));
constructorNode.putNodeMetaData(IS_GENERATED_CONSTRUCTOR, Boolean.TRUE);

Expand Down Expand Up @@ -282,17 +279,13 @@ private MethodNode addSyntheticLambdaMethodNode(final LambdaExpression expressio

private Parameter[] createParametersWithExactType(final LambdaExpression expression, final MethodNode abstractMethod) {
Parameter[] targetParameters = abstractMethod.getParameters();
Parameter[] parameters = getParametersSafe(expression);
for (int i = 0, n = parameters.length; i < n; i += 1) {
Parameter targetParameter = targetParameters[i];
Parameter parameter = parameters[i];
ClassNode inferredType = parameter.getNodeMetaData(INFERRED_TYPE);
if (inferredType != null) {
ClassNode type = convertParameterType(targetParameter.getType(), parameter.getType(), inferredType);
parameter.setType(type);
}
Parameter[] lambdaParameters = getParametersSafe(expression);
ClassNode[] lambdaParamTypes = expression.getNodeMetaData(CLOSURE_ARGUMENTS);
for (int i = 0, n = lambdaParameters.length; i < n; i += 1) {
ClassNode resolvedType = convertParameterType(targetParameters[i].getType(), lambdaParameters[i].getType(), lambdaParamTypes[i]);
lambdaParameters[i].setType(resolvedType);
}
return parameters;
return lambdaParameters;
}

private void addDeserializeLambdaMethod() {
Expand Down
38 changes: 0 additions & 38 deletions src/test/groovy/bugs/Groovy9770.groovy

This file was deleted.

29 changes: 29 additions & 0 deletions src/test/groovy/transform/stc/LambdaTest.groovy
Expand Up @@ -258,6 +258,19 @@ final class LambdaTest {
}
}

// GROOVY-11304
@Test
void testCollectors3() {
assertScript shell, '''
List<String> list = ['a', 'b', 'c']
Map<String, Integer> map = list.stream().collect(
Collectors.toMap(Function.identity(), (k) -> 1, (v, w) -> v + w)
)
assert map == [a: 1, b: 1, c: 1]
'''
}

@Test
void testFunctionWithLocalVariables() {
assertScript shell, '''
Expand Down Expand Up @@ -1765,4 +1778,20 @@ final class LambdaTest {
}
'''
}

// GROOVY-9770
@Test
void testLambdaClassIsntSynthetic() {
assertScript shell, '''
class Foo {
def bar(String arg) {
Function<String, String> fun = (String s) -> s.toUpperCase()
fun.apply(arg)
}
}
assert new Foo().bar('hello') == 'HELLO'
assert this.class.classLoader.loadClass('Foo$_bar_lambda1').modifiers == 25 // public(1) + static(8) + final(16)
'''
}
}

0 comments on commit b1032d2

Please sign in to comment.