You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by em...@apache.org on 2022/02/24 15:28:39 UTC

[groovy] branch GROOVY_4_0_X updated (e2c0006 -> 6a8f965)

This is an automated email from the ASF dual-hosted git repository.

emilles pushed a change to branch GROOVY_4_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git.


    from e2c0006  GROOVY-10507: Bump Spock to 2.1-groovy-3.0 (cont'd))
     new 0f3fcbf  GROOVY-10505: mark trait super methods as generated
     new b431626  GROOVY-10478: SC: indicate dynamic resolution of trait super fallback
     new 6a8f965  GROOVY-5726: source range of method call in command expression

The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../apache/groovy/parser/antlr4/AstBuilder.java    |  49 ++++------
 .../classgen/asm/sc/StaticInvocationWriter.java    |   3 +-
 .../groovy/transform/trait/TraitComposer.java      | 102 +++++++++------------
 src/test/groovy/bugs/Groovy10478.groovy            |  32 +++++++
 .../org/apache/groovy/ginq/GinqErrorTest.groovy    |  12 +--
 5 files changed, 99 insertions(+), 99 deletions(-)
 create mode 100644 src/test/groovy/bugs/Groovy10478.groovy

[groovy] 02/03: GROOVY-10478: SC: indicate dynamic resolution of trait super fallback

Posted by em...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

emilles pushed a commit to branch GROOVY_4_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit b43162677754ea606012cdba62b377f6efd6bf6b
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Wed Feb 23 17:57:23 2022 -0600

    GROOVY-10478: SC: indicate dynamic resolution of trait super fallback
---
 .../classgen/asm/sc/StaticInvocationWriter.java    |  3 +-
 .../groovy/transform/trait/TraitComposer.java      |  2 ++
 src/test/groovy/bugs/Groovy10478.groovy            | 32 ++++++++++++++++++++++
 3 files changed, 35 insertions(+), 2 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java
index 6fee1be..ea3c60f 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java
@@ -512,8 +512,7 @@ public class StaticInvocationWriter extends InvocationWriter {
 
     @Override
     public void makeCall(final Expression origin, final Expression receiver, final Expression message, final Expression arguments, final MethodCallerMultiAdapter adapter, final boolean safe, final boolean spreadSafe, final boolean implicitThis) {
-        ClassNode dynamicCallReturnType = origin.getNodeMetaData(StaticTypesMarker.DYNAMIC_RESOLUTION);
-        if (dynamicCallReturnType != null) {
+        if (origin.getNodeMetaData(StaticTypesMarker.DYNAMIC_RESOLUTION) != null) {
             StaticTypesWriterController staticController = (StaticTypesWriterController) controller;
             if (origin instanceof MethodCallExpression) {
                 ((MethodCallExpression) origin).setMethodTarget(null);
diff --git a/src/main/java/org/codehaus/groovy/transform/trait/TraitComposer.java b/src/main/java/org/codehaus/groovy/transform/trait/TraitComposer.java
index 67f0701..07cd228 100644
--- a/src/main/java/org/codehaus/groovy/transform/trait/TraitComposer.java
+++ b/src/main/java/org/codehaus/groovy/transform/trait/TraitComposer.java
@@ -53,6 +53,7 @@ import org.codehaus.groovy.syntax.Token;
 import org.codehaus.groovy.syntax.Types;
 import org.codehaus.groovy.transform.ASTTransformationCollectorCodeVisitor;
 import org.codehaus.groovy.transform.sc.StaticCompileTransformation;
+import org.codehaus.groovy.transform.stc.StaticTypesMarker;
 import org.objectweb.asm.Opcodes;
 
 import java.util.ArrayList;
@@ -519,6 +520,7 @@ public abstract class TraitComposer {
         );
 
         MethodCallExpression superCall = callX(varX("super"), forwarderMethod.getName(), paramTuple);
+        superCall.putNodeMetaData(StaticTypesMarker.DYNAMIC_RESOLUTION, Boolean.TRUE); //GROOVY-10478
         superCall.setImplicitThis(false);
 
         // if (this instanceof GeneratedGroovyProxy)
diff --git a/src/test/groovy/bugs/Groovy10478.groovy b/src/test/groovy/bugs/Groovy10478.groovy
new file mode 100644
index 0000000..a7e5868
--- /dev/null
+++ b/src/test/groovy/bugs/Groovy10478.groovy
@@ -0,0 +1,32 @@
+package groovy.bugs
+
+import org.junit.Test
+
+import static groovy.test.GroovyAssert.assertScript
+
+final class Groovy10478 {
+
+    @Test
+    void testIndirectInterface() {
+        assertScript '''
+            trait A {
+                final String string = 'works'
+            }
+            interface B {
+            }
+            trait C implements A, B {
+            }
+            @groovy.transform.CompileStatic
+            class D implements C {
+            }
+
+            // VerifyError: Bad invokespecial instruction: interface method reference is in an indirect superinterface
+            //    Location: D.Atrait$super$getString()Ljava/lang/String; @37: invokespecial
+            def cls = D.class
+            cls.name
+
+            String result = new D().string
+            assert result == 'works'
+        '''
+    }
+}

[groovy] 03/03: GROOVY-5726: source range of method call in command expression

Posted by em...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

emilles pushed a commit to branch GROOVY_4_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit 6a8f96578f759bf213db7e42a29aef8c81d51c82
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Mon Feb 7 12:12:09 2022 -0600

    GROOVY-5726: source range of method call in command expression
    
    range of call only covered argument(s)
    
    `foo bar baz` --> `this.foo(bar).baz`
---
 .../apache/groovy/parser/antlr4/AstBuilder.java    | 49 ++++++++--------------
 .../org/apache/groovy/ginq/GinqErrorTest.groovy    | 12 +++---
 2 files changed, 24 insertions(+), 37 deletions(-)

diff --git a/src/main/java/org/apache/groovy/parser/antlr4/AstBuilder.java b/src/main/java/org/apache/groovy/parser/antlr4/AstBuilder.java
index 19a5568..f88c609 100644
--- a/src/main/java/org/apache/groovy/parser/antlr4/AstBuilder.java
+++ b/src/main/java/org/apache/groovy/parser/antlr4/AstBuilder.java
@@ -2538,7 +2538,7 @@ public class AstBuilder extends GroovyParserBaseVisitor<Object> {
         boolean hasArgumentList = asBoolean(ctx.enhancedArgumentListInPar());
         boolean hasCommandArgument = asBoolean(ctx.commandArgument());
 
-        if (visitingArrayInitializerCount > 0 && (hasArgumentList || hasCommandArgument)) {
+        if ((hasArgumentList || hasCommandArgument) && visitingArrayInitializerCount > 0) {
             // To avoid ambiguities, command chain expression should not be used in array initializer
             // the old parser does not support either, so no breaking changes
             // SEE http://groovy.329449.n5.nabble.com/parrot-Command-expressions-in-array-initializer-tt5752273.html
@@ -2547,12 +2547,9 @@ public class AstBuilder extends GroovyParserBaseVisitor<Object> {
 
         Expression baseExpr = (Expression) this.visit(ctx.expression());
 
-        if (hasArgumentList || hasCommandArgument) {
-            if (baseExpr instanceof BinaryExpression) {
-                if (!"[".equals(((BinaryExpression) baseExpr).getOperation().getText()) && !isInsideParentheses(baseExpr)) {
-                    throw createParsingFailedException("Unexpected input: '" + getOriginalText(ctx.expression()) + "'", ctx.expression());
-                }
-            }
+        if ((hasArgumentList || hasCommandArgument) && !isInsideParentheses(baseExpr)
+                && baseExpr instanceof BinaryExpression && !"[".equals(((BinaryExpression) baseExpr).getOperation().getText())) {
+            throw createParsingFailedException("Unexpected input: '" + getOriginalText(ctx.expression()) + "'", ctx.expression());
         }
 
         MethodCallExpression methodCallExpression = null;
@@ -2561,54 +2558,44 @@ public class AstBuilder extends GroovyParserBaseVisitor<Object> {
             Expression arguments = this.visitEnhancedArgumentListInPar(ctx.enhancedArgumentListInPar());
 
             if (baseExpr instanceof PropertyExpression) { // e.g. obj.a 1, 2
-                methodCallExpression =
-                        configureAST(
-                                this.createMethodCallExpression(
-                                        (PropertyExpression) baseExpr, arguments),
-                                arguments);
+                methodCallExpression = configureAST(this.createMethodCallExpression((PropertyExpression) baseExpr, arguments), ctx.expression(), arguments);
 
             } else if (baseExpr instanceof MethodCallExpression && !isInsideParentheses(baseExpr)) { // e.g. m {} a, b  OR  m(...) a, b
                 if (asBoolean(arguments)) {
                     // The error should never be thrown.
                     throw new GroovyBugError("When baseExpr is a instance of MethodCallExpression, which should follow NO argumentList");
                 }
-
                 methodCallExpression = (MethodCallExpression) baseExpr;
-            } else if (
-                    !isInsideParentheses(baseExpr)
-                            && (baseExpr instanceof VariableExpression /* e.g. m 1, 2 */
+            } else {
+                if (!isInsideParentheses(baseExpr)
+                        && (baseExpr instanceof VariableExpression /* e.g. m 1, 2 */
                             || baseExpr instanceof GStringExpression /* e.g. "$m" 1, 2 */
-                            || (baseExpr instanceof ConstantExpression && isTrue(baseExpr, IS_STRING)) /* e.g. "m" 1, 2 */)
-            ) {
-                validateInvalidMethodDefinition(baseExpr, arguments);
-
-                methodCallExpression =
-                        configureAST(
-                                this.createMethodCallExpression(baseExpr, arguments),
-                                arguments);
-            } else { // e.g. a[x] b, new A() b, etc.
-                methodCallExpression = configureAST(this.createCallMethodCallExpression(baseExpr, arguments), arguments);
+                            || (baseExpr instanceof ConstantExpression && isTrue(baseExpr, IS_STRING)) /* e.g. "m" 1, 2 */)) {
+                    validateInvalidMethodDefinition(baseExpr, arguments);
+                } else {
+                    // e.g. a[x] b, new A() b, etc.
+                }
+                methodCallExpression = configureAST(this.createMethodCallExpression(baseExpr, arguments), ctx.expression(), arguments);
             }
 
-            methodCallExpression.putNodeMetaData(IS_COMMAND_EXPRESSION, true);
+            methodCallExpression.putNodeMetaData(IS_COMMAND_EXPRESSION, Boolean.TRUE);
 
             if (!hasCommandArgument) {
-                return configureAST(methodCallExpression, ctx);
+                return methodCallExpression;
             }
         }
 
         if (hasCommandArgument) {
-            baseExpr.putNodeMetaData(IS_COMMAND_EXPRESSION, true);
+            baseExpr.putNodeMetaData(IS_COMMAND_EXPRESSION, Boolean.TRUE);
         }
 
         return configureAST(
                 (Expression) ctx.commandArgument().stream()
                         .map(e -> (Object) e)
-                        .reduce(null == methodCallExpression ? baseExpr : methodCallExpression,
+                        .reduce(methodCallExpression != null ? methodCallExpression : baseExpr,
                                 (r, e) -> {
                                     CommandArgumentContext commandArgumentContext = (CommandArgumentContext) e;
                                     commandArgumentContext.putNodeMetaData(CMD_EXPRESSION_BASE_EXPR, r);
-
                                     return this.visitCommandArgument(commandArgumentContext);
                                 }
                         ),
diff --git a/subprojects/groovy-ginq/src/test/groovy/org/apache/groovy/ginq/GinqErrorTest.groovy b/subprojects/groovy-ginq/src/test/groovy/org/apache/groovy/ginq/GinqErrorTest.groovy
index 1c4e7d4..e9f572c 100644
--- a/subprojects/groovy-ginq/src/test/groovy/org/apache/groovy/ginq/GinqErrorTest.groovy
+++ b/subprojects/groovy-ginq/src/test/groovy/org/apache/groovy/ginq/GinqErrorTest.groovy
@@ -21,11 +21,11 @@ package org.apache.groovy.ginq
 import groovy.transform.CompileStatic
 import org.junit.Test
 
-import static groovy.test.GroovyAssert.assertScript
 import static groovy.test.GroovyAssert.shouldFail
 
 @CompileStatic
-class GinqErrorTest {
+final class GinqErrorTest {
+
     @Test
     void "testGinq - from - 1"() {
         def err = shouldFail '''\
@@ -65,9 +65,9 @@ class GinqErrorTest {
             GQ {
                 select n from n in [0, 1, 2]
             }
-        '''
+        ''' //  ^
 
-        assert err.toString().contains("One `from` is expected and must be the first clause @ line 2, column 24.")
+        assert err.toString().contains("One `from` is expected and must be the first clause @ line 2, column 17.")
     }
 
     @Test
@@ -462,9 +462,9 @@ class GinqErrorTest {
                 from n in [1, 1, 2, 2]
                 select n, (rowNumber() over(order by n))
             }.toList()
-        '''
+        ''' //                              ^
 
-        assert err.toString().contains('Unknown window clause: `order` @ line 3, column 51.')
+        assert err.toString().contains('Unknown window clause: `order` @ line 3, column 45.')
     }
 
     @Test

[groovy] 01/03: GROOVY-10505: mark trait super methods as generated

Posted by em...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

emilles pushed a commit to branch GROOVY_4_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit 0f3fcbf755b883ed074715a39e24bee4cda62540
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Wed Feb 23 17:33:17 2022 -0600

    GROOVY-10505: mark trait super methods as generated
---
 .../groovy/transform/trait/TraitComposer.java      | 100 +++++++++------------
 1 file changed, 40 insertions(+), 60 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/transform/trait/TraitComposer.java b/src/main/java/org/codehaus/groovy/transform/trait/TraitComposer.java
index 4486889..67f0701 100644
--- a/src/main/java/org/codehaus/groovy/transform/trait/TraitComposer.java
+++ b/src/main/java/org/codehaus/groovy/transform/trait/TraitComposer.java
@@ -31,7 +31,6 @@ import org.codehaus.groovy.ast.PropertyNode;
 import org.codehaus.groovy.ast.expr.ArgumentListExpression;
 import org.codehaus.groovy.ast.expr.ArrayExpression;
 import org.codehaus.groovy.ast.expr.BinaryExpression;
-import org.codehaus.groovy.ast.expr.BooleanExpression;
 import org.codehaus.groovy.ast.expr.CastExpression;
 import org.codehaus.groovy.ast.expr.ClassExpression;
 import org.codehaus.groovy.ast.expr.ConstantExpression;
@@ -39,11 +38,8 @@ import org.codehaus.groovy.ast.expr.Expression;
 import org.codehaus.groovy.ast.expr.MethodCallExpression;
 import org.codehaus.groovy.ast.expr.StaticMethodCallExpression;
 import org.codehaus.groovy.ast.expr.VariableExpression;
-import org.codehaus.groovy.ast.stmt.BlockStatement;
 import org.codehaus.groovy.ast.stmt.EmptyStatement;
 import org.codehaus.groovy.ast.stmt.ExpressionStatement;
-import org.codehaus.groovy.ast.stmt.IfStatement;
-import org.codehaus.groovy.ast.stmt.ReturnStatement;
 import org.codehaus.groovy.ast.stmt.Statement;
 import org.codehaus.groovy.ast.tools.GeneralUtils;
 import org.codehaus.groovy.ast.tools.GenericsUtils;
@@ -69,15 +65,20 @@ import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 
+import static org.apache.groovy.ast.tools.ClassNodeUtils.addGeneratedMethod;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.args;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.assignX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.block;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.callX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.castX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.constX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.ifElseS;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.isInstanceOfX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.nullX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.returnS;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.stmt;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.varX;
 import static org.codehaus.groovy.ast.tools.GenericsUtils.correctToGenericsSpecRecurse;
-import static org.codehaus.groovy.ast.ClassHelper.isClassType;
-import static org.codehaus.groovy.ast.ClassHelper.isObjectType;
 
 /**
  * This class contains a static utility method {@link #doExtendTraits(org.codehaus.groovy.ast.ClassNode, org.codehaus.groovy.control.SourceUnit, org.codehaus.groovy.control.CompilationUnit)}
@@ -120,7 +121,7 @@ public abstract class TraitComposer {
 
     private static void checkTraitAllowed(final ClassNode bottomTrait, final SourceUnit unit) {
         ClassNode superClass = bottomTrait.getSuperClass();
-        if (superClass==null || isObjectType(superClass)) return;
+        if (superClass == null || ClassHelper.isObjectType(superClass)) return;
         if (!Traits.isTrait(superClass)) {
             unit.addError(new SyntaxException("A trait can only inherit from another trait", superClass.getLineNumber(), superClass.getColumnNumber()));
         }
@@ -339,7 +340,7 @@ public abstract class TraitComposer {
         Expression forwardExpression = noCastRequired ? mce : new CastExpression(fixedReturnType,mce);
         // we could rely on the first parameter name ($static$self) but that information is not
         // guaranteed to be always present
-        boolean isHelperForStaticMethod = isClassType(helperMethodParams[0].getOriginType());
+        boolean isHelperForStaticMethod = ClassHelper.isClassType(helperMethodParams[0].getOriginType());
         if (helperMethod.isPrivate() && !isHelperForStaticMethod) {
             // GROOVY-7213: do not create forwarder for private methods
             return;
@@ -475,18 +476,17 @@ public abstract class TraitComposer {
 
     /**
      * Creates a method to dispatch to "super traits" in a "stackable" fashion. The generated method looks like this:
-     * <p>
-     * <code>ReturnType trait$super$method(Class clazz, Arg1 arg1, Arg2 arg2, ...) {
-     *     if (SomeTrait.is(A) { return SomeOtherTrait$Trait$Helper.method(this, arg1, arg2) }
+     * <pre>ReturnType trait$super$method(Class clazz, Arg1 arg1, Arg2 arg2, ...) {
+     *     if (SomeTrait.is(A) return SomeOtherTrait$Trait$Helper.method(this, arg1, arg2)
      *     super.method(arg1,arg2)
-     * }</code>
-     * </p>
+     * }
+     * </pre>
      * @param targetNode
      * @param forwarderMethod
      * @param interfacesToGenerateForwarderFor
      * @param genericsSpec
      */
-    private static void doCreateSuperForwarder(ClassNode targetNode, MethodNode forwarderMethod, ClassNode[] interfacesToGenerateForwarderFor, Map<String,ClassNode> genericsSpec) {
+    private static void doCreateSuperForwarder(final ClassNode targetNode, final MethodNode forwarderMethod, final ClassNode[] interfacesToGenerateForwarderFor, final Map<String,ClassNode> genericsSpec) {
         Parameter[] parameters = forwarderMethod.getParameters();
         Parameter[] superForwarderParams = new Parameter[parameters.length];
         for (int i = 0; i < parameters.length; i++) {
@@ -501,65 +501,45 @@ public abstract class TraitComposer {
             if (targetNode.getDeclaredMethod(forwarderName, superForwarderParams) == null) {
                 ClassNode returnType = correctToGenericsSpecRecurse(genericsSpec, forwarderMethod.getReturnType());
                 Statement delegate = next == null ? createSuperFallback(forwarderMethod, returnType) : createDelegatingForwarder(forwarderMethod, next);
-                MethodNode methodNode = targetNode.addMethod(forwarderName, Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC, returnType, superForwarderParams, ClassNode.EMPTY_ARRAY, delegate);
+
+                MethodNode methodNode = addGeneratedMethod(targetNode, forwarderName, Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC, returnType, superForwarderParams, ClassNode.EMPTY_ARRAY, delegate);
                 methodNode.setGenericsTypes(forwarderMethod.getGenericsTypes());
             }
         }
     }
 
-    private static Statement createSuperFallback(MethodNode forwarderMethod, ClassNode returnType) {
-        ArgumentListExpression args = new ArgumentListExpression();
-        Parameter[] forwarderMethodParameters = forwarderMethod.getParameters();
-        for (final Parameter forwarderMethodParameter : forwarderMethodParameters) {
-            args.addExpression(new VariableExpression(forwarderMethodParameter));
-        }
-        BinaryExpression instanceOfExpr = new BinaryExpression(new VariableExpression("this"), Token.newSymbol(Types.KEYWORD_INSTANCEOF, -1, -1), new ClassExpression(Traits.GENERATED_PROXY_CLASSNODE));
-        MethodCallExpression superCall = new MethodCallExpression(
-                new VariableExpression("super"),
-                forwarderMethod.getName(),
-                args
+    private static Statement createSuperFallback(final MethodNode forwarderMethod, final ClassNode returnType) {
+        ArgumentListExpression paramTuple = args(Arrays.stream(forwarderMethod.getParameters()).map(p -> varX(p)).toArray(Expression[]::new));
+
+        MethodCallExpression proxyTarget = callX(castX(Traits.GENERATED_PROXY_CLASSNODE, varX("this")), "getProxyTarget");
+        proxyTarget.setImplicitThis(false);
+
+        Expression proxyCall = callX(ClassHelper.make(InvokerHelper.class), "invokeMethod",
+                args(proxyTarget, constX(forwarderMethod.getName()), new ArrayExpression(ClassHelper.OBJECT_TYPE, paramTuple.getExpressions()))
         );
+
+        MethodCallExpression superCall = callX(varX("super"), forwarderMethod.getName(), paramTuple);
         superCall.setImplicitThis(false);
-        CastExpression proxyReceiver = new CastExpression(Traits.GENERATED_PROXY_CLASSNODE, new VariableExpression("this"));
-        MethodCallExpression getProxy = new MethodCallExpression(proxyReceiver, "getProxyTarget", ArgumentListExpression.EMPTY_ARGUMENTS);
-        getProxy.setImplicitThis(false);
-        StaticMethodCallExpression proxyCall = new StaticMethodCallExpression(
-                ClassHelper.make(InvokerHelper.class),
-                "invokeMethod",
-                new ArgumentListExpression(getProxy, new ConstantExpression(forwarderMethod.getName()), new ArrayExpression(ClassHelper.OBJECT_TYPE, args.getExpressions()))
-        );
-        IfStatement stmt = new IfStatement(
-                new BooleanExpression(instanceOfExpr),
-                new ExpressionStatement(new CastExpression(returnType,proxyCall)),
-                new ExpressionStatement(superCall)
+
+        // if (this instanceof GeneratedGroovyProxy)
+        //   (ReturnType) InvokerHelper.invokeMethod(((GeneratedGroovyProxy) this).getProxyTarget(), "targetMethod", new Object[]{arguments})
+        // else
+        //   super.targetMethod(arguments)
+        return ifElseS(
+                isInstanceOfX(varX("this"), Traits.GENERATED_PROXY_CLASSNODE),
+                stmt(castX(returnType, proxyCall)),
+                stmt(superCall)
         );
-        return stmt;
     }
 
     private static Statement createDelegatingForwarder(final MethodNode forwarderMethod, final ClassNode next) {
         // generates --> next$Trait$Helper.method(this, arg1, arg2)
-        TraitHelpersTuple helpers = Traits.findHelpers(next);
         ArgumentListExpression args = new ArgumentListExpression();
-        args.addExpression(new VariableExpression("this"));
-        Parameter[] forwarderMethodParameters = forwarderMethod.getParameters();
-        for (final Parameter forwarderMethodParameter : forwarderMethodParameters) {
-            args.addExpression(new VariableExpression(forwarderMethodParameter));
-        }
-        StaticMethodCallExpression delegateCall = new StaticMethodCallExpression(
-                helpers.getHelper(),
-                forwarderMethod.getName(),
-                args
-        );
-        Statement result;
-        if (forwarderMethod.isVoidMethod()) {
-            BlockStatement stmt = new BlockStatement();
-            stmt.addStatement(new ExpressionStatement(delegateCall));
-            stmt.addStatement(new ReturnStatement(new ConstantExpression(null)));
-            result = stmt;
-        } else {
-            result = new ReturnStatement(delegateCall);
-        }
-        return result;
+        args.addExpression(varX("this"));
+        for (Parameter p : forwarderMethod.getParameters()) args.addExpression(varX(p));
+        Expression delegateCall = callX(Traits.findHelper(next), forwarderMethod.getName(), args);
+
+        return forwarderMethod.isVoidMethod() ? block(stmt(delegateCall), returnS(nullX())) : returnS(delegateCall);
     }
 
     private static ClassNode[] copyExceptions(final ClassNode[] sourceExceptions) {