diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/InvocationWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/InvocationWriter.java index 21b69fda94d..89eac79c992 100644 --- a/src/main/java/org/codehaus/groovy/classgen/asm/InvocationWriter.java +++ b/src/main/java/org/codehaus/groovy/classgen/asm/InvocationWriter.java @@ -21,7 +21,6 @@ import org.codehaus.groovy.ast.ClassHelper; import org.codehaus.groovy.ast.ClassNode; import org.codehaus.groovy.ast.ConstructorNode; -import org.codehaus.groovy.ast.FieldNode; import org.codehaus.groovy.ast.MethodNode; import org.codehaus.groovy.ast.Parameter; import org.codehaus.groovy.ast.expr.ArgumentListExpression; @@ -94,6 +93,7 @@ public class InvocationWriter { public static final MethodCallerMultiAdapter invokeMethodOnSuper = MethodCallerMultiAdapter.newStatic(ScriptBytecodeAdapter.class, "invokeMethodOnSuper", true, false); public static final MethodCallerMultiAdapter invokeMethod = MethodCallerMultiAdapter.newStatic(ScriptBytecodeAdapter.class, "invokeMethod", true, false); public static final MethodCallerMultiAdapter invokeStaticMethod = MethodCallerMultiAdapter.newStatic(ScriptBytecodeAdapter.class, "invokeStaticMethod", true, true); + @Deprecated(since = "5.0.0") public static final MethodCaller invokeClosureMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "invokeClosure"); public static final MethodCaller castToVargsArray = MethodCaller.newStatic(DefaultTypeTransformation.class, "castToVargsArray"); private static final MethodNode CLASS_FOR_NAME_STRING = ClassHelper.CLASS_Type.getDeclaredMethod("forName", new Parameter[]{new Parameter(ClassHelper.STRING_TYPE, "name")}); @@ -457,78 +457,38 @@ protected String getMethodName(final Expression message) { } public void writeInvokeMethod(MethodCallExpression call) { - if (isClosureCall(call)) { - invokeClosure(call.getArguments(), call.getMethodAsString()); - } else { - if (isFunctionInterfaceCall(call)) { - call = transformToRealMethodCall(call); - } - Expression receiver = call.getObjectExpression(); - MethodCallerMultiAdapter adapter = invokeMethod; - if (isSuperExpression(receiver)) { - adapter = invokeMethodOnSuper; - } else if (isThisExpression(receiver)) { - adapter = invokeMethodOnCurrent; - } - if (isStaticInvocation(call)) { - adapter = invokeStaticMethod; - } - Expression messageName = new CastExpression(ClassHelper.STRING_TYPE, call.getMethod()); - makeCall(call, receiver, messageName, call.getArguments(), adapter, call.isSafe(), call.isSpreadSafe(), call.isImplicitThis()); + Expression receiver = call.getObjectExpression(); + // GROOVY-8466: replace "rcvr.call(args)" with "rcvr.abstractMethod(args)" + if (!isThisExpression(receiver) && "call".equals(call.getMethodAsString())) { + ClassNode type = controller.getTypeChooser().resolveType(receiver, controller.getClassNode()); + if (isFunctionalInterface(type)) call = transformToRealMethodCall(call, type); } - } - - private static boolean isFunctionInterfaceCall(final MethodCallExpression call) { - if ("call".equals(call.getMethodAsString())) { - Expression objectExpression = call.getObjectExpression(); - if (!isThisExpression(objectExpression)) { - return isFunctionalInterface(objectExpression.getType()); - } + MethodCallerMultiAdapter adapter = invokeMethod; + if (isSuperExpression(receiver)) { + adapter = invokeMethodOnSuper; + } else if (isThisExpression(receiver)) { + adapter = invokeMethodOnCurrent; } - return false; + if (isStaticInvocation(call)) { + adapter = invokeStaticMethod; + } + Expression messageName = new CastExpression(ClassHelper.STRING_TYPE, call.getMethod()); + makeCall(call, receiver, messageName, call.getArguments(), adapter, call.isSafe(), call.isSpreadSafe(), call.isImplicitThis()); } - private static MethodCallExpression transformToRealMethodCall(MethodCallExpression call) { - ClassNode type = call.getObjectExpression().getType(); + private static MethodCallExpression transformToRealMethodCall(final MethodCallExpression call, final ClassNode type) { MethodNode methodNode = ClassHelper.findSAM(type); - call = (MethodCallExpression) call.transformExpression(expression -> { - if (!(expression instanceof ConstantExpression)) { - return expression; + var rewrite = (MethodCallExpression) call.transformExpression(expression -> { + if (expression == call.getMethod()) { + var methodName = new ConstantExpression(methodNode.getName()); + methodName.setSourcePosition(call.getMethod()); + return methodName; } - return new ConstantExpression(methodNode.getName()); + return expression; }); - call.setMethodTarget(methodNode); - return call; - } - - private boolean isClosureCall(final MethodCallExpression call) { - // are we a local variable? - // it should not be an explicitly "this" qualified method call - // and the current class should have a possible method - ClassNode classNode = controller.getClassNode(); - String methodName = call.getMethodAsString(); - if (methodName == null) return false; - if (!call.isImplicitThis()) return false; - if (!isThisExpression(call.getObjectExpression())) return false; - FieldNode field = classNode.getDeclaredField(methodName); - if (field == null) return false; - if (isStaticInvocation(call) && !field.isStatic()) return false; - Expression arguments = call.getArguments(); - return !classNode.hasPossibleMethod(methodName, arguments); - } - - private void invokeClosure(final Expression arguments, final String methodName) { - AsmClassGenerator acg = controller.getAcg(); - acg.visitVariableExpression(new VariableExpression(methodName)); - controller.getOperandStack().box(); - if (arguments instanceof TupleExpression) { - arguments.visit(acg); - } else { - new TupleExpression(arguments).visit(acg); - } - invokeClosureMethod.call(controller.getMethodVisitor()); - controller.getOperandStack().replace(ClassHelper.OBJECT_TYPE); + rewrite.setMethodTarget(methodNode); + return rewrite; } private boolean isStaticInvocation(final MethodCallExpression call) { diff --git a/src/test/groovy/bugs/Groovy5267Bug.groovy b/src/test/groovy/bugs/Groovy5267.groovy similarity index 56% rename from src/test/groovy/bugs/Groovy5267Bug.groovy rename to src/test/groovy/bugs/Groovy5267.groovy index 14d782a0459..85a4e947526 100644 --- a/src/test/groovy/bugs/Groovy5267Bug.groovy +++ b/src/test/groovy/bugs/Groovy5267.groovy @@ -18,33 +18,21 @@ */ package groovy.bugs -import org.codehaus.groovy.classgen.asm.AbstractBytecodeTestCase +import org.junit.Test -class Groovy5267Bug extends AbstractBytecodeTestCase { +import static groovy.test.GroovyAssert.shouldFail + +final class Groovy5267 { + @Test void testShouldNotThrowVerifyError() { - shouldFail(MissingMethodException) { - assertScript ''' - class Bar { - int defaultValue = 40 - void m() { - defaultValue() - } + shouldFail MissingMethodException, ''' + class C { + int defaultValue = 40 + void m() { + defaultValue() + } } - new Bar().m() + new C().m() ''' - } - } - - void testClosureCallBytecode() { - def bytecode= compile(method:'m', ''' - class BarScript { - int defaultValue = 40 - void m() { - defaultValue() - } - } - ''') - - assert bytecode.hasSequence(['INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;']) } } diff --git a/src/test/org/codehaus/groovy/transform/DelegateTransformTest.groovy b/src/test/org/codehaus/groovy/transform/DelegateTransformTest.groovy index 984b9b499a3..f481e2a4311 100644 --- a/src/test/org/codehaus/groovy/transform/DelegateTransformTest.groovy +++ b/src/test/org/codehaus/groovy/transform/DelegateTransformTest.groovy @@ -23,6 +23,7 @@ import org.codehaus.groovy.control.MultipleCompilationErrorsException import org.codehaus.groovy.tools.javac.JavaAwareCompilationUnit import org.junit.Test +import java.util.concurrent.Callable import java.util.concurrent.locks.Lock import static groovy.test.GroovyAssert.assertScript @@ -1090,11 +1091,11 @@ interface DelegateBar extends DelegateFoo { } class DelegateBarImpl implements DelegateBar { - @Delegate DelegateFoo foo; + @Delegate DelegateFoo foo - DelegateBarImpl(DelegateFoo f) { this.foo = f} + DelegateBarImpl(DelegateFoo foo) { this.foo = foo } - def bar() { 'bar impl'} + @Override bar() { 'bar impl' } } class BazWithDeprecatedFoo { @@ -1114,20 +1115,16 @@ class Foo4244 { @Delegate Bar4244 bar = new Bar4244() } +@groovy.transform.TupleConstructor(includeFields=true) class FooToMethod { - private final Closure strategy + private final Callable supplier - FooToMethod(Closure strategy) { - this.strategy = strategy - } - - @Delegate - DelegateFoo getStrategy() { strategy() } + @Delegate DelegateFoo getStrategy() { supplier() } } class Bar4244 { - String nonFinalBaz = "Initial value - nonFinalBaz" - final String finalBaz = "Initial value - finalBaz" + String nonFinalBaz = 'Initial value - nonFinalBaz' + final String finalBaz = 'Initial value - finalBaz' } interface SomeInterface4619 { @@ -1137,8 +1134,7 @@ interface SomeInterface4619 { interface SomeOtherInterface4619 extends SomeInterface4619 {} class SomeClass4619 { - @Delegate - SomeOtherInterface4619 delegate + @Delegate SomeOtherInterface4619 delegate } interface BarInt {