From b1032d28d0c2dbdcc9a504a21d0c41b088533098 Mon Sep 17 00:00:00 2001 From: Eric Milles Date: Sat, 24 Feb 2024 13:27:40 -0600 Subject: [PATCH] GROOVY-11304: SC: scrub lambda generics --- .../asm/sc/StaticTypesLambdaWriter.java | 39 ++++++++----------- src/test/groovy/bugs/Groovy9770.groovy | 38 ------------------ .../groovy/transform/stc/LambdaTest.groovy | 29 ++++++++++++++ 3 files changed, 45 insertions(+), 61 deletions(-) delete mode 100644 src/test/groovy/bugs/Groovy9770.groovy diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesLambdaWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesLambdaWriter.java index 843c834f16a..142312ef6b1 100644 --- a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesLambdaWriter.java +++ b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesLambdaWriter.java @@ -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; @@ -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); @@ -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()) { @@ -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); @@ -225,10 +222,8 @@ 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); @@ -236,7 +231,8 @@ protected ClassNode createLambdaClass(final LambdaExpression expression, final i if (controller.isInScriptBody()) { lambdaClass.setScriptBody(true); } - if (staticMethodOrInStaticClass) { + if (controller.isStaticMethod() + || enclosingClass.isStaticClass()) { lambdaClass.setStaticClass(true); } if (expression.isSerializable()) { @@ -244,9 +240,10 @@ protected ClassNode createLambdaClass(final LambdaExpression expression, final i } 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); @@ -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() { diff --git a/src/test/groovy/bugs/Groovy9770.groovy b/src/test/groovy/bugs/Groovy9770.groovy deleted file mode 100644 index 557c93fb6c3..00000000000 --- a/src/test/groovy/bugs/Groovy9770.groovy +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package groovy.bugs - -import groovy.test.GroovyTestCase - -class Groovy9770 extends GroovyTestCase { - void testLambdaClassModifiers() { - assertScript ''' - @groovy.transform.CompileStatic - class Foo { - def bar(String arg) { - java.util.function.Function up = (String s) -> { s.toUpperCase() } - up(arg) - } - } - - assert new Foo().bar('hello') == 'HELLO' - assert getClass().classLoader.loadClass('Foo$_bar_lambda1').modifiers == 17 // PUBLIC | FINAL - ''' - } -} diff --git a/src/test/groovy/transform/stc/LambdaTest.groovy b/src/test/groovy/transform/stc/LambdaTest.groovy index fb06d6203f4..a75a3f57a80 100644 --- a/src/test/groovy/transform/stc/LambdaTest.groovy +++ b/src/test/groovy/transform/stc/LambdaTest.groovy @@ -258,6 +258,19 @@ final class LambdaTest { } } + // GROOVY-11304 + @Test + void testCollectors3() { + assertScript shell, ''' + List list = ['a', 'b', 'c'] + Map 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, ''' @@ -1765,4 +1778,20 @@ final class LambdaTest { } ''' } + + // GROOVY-9770 + @Test + void testLambdaClassIsntSynthetic() { + assertScript shell, ''' + class Foo { + def bar(String arg) { + Function 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) + ''' + } }