This is an automated email from the ASF dual-hosted git repository. asf-gitbox-commits pushed a commit to branch GROOVY-12117 in repository https://gitbox.apache.org/repos/asf/groovy.git
commit 5f40130b5d1c3b81fa143671f5ab8d7d9e204112 Author: Eric Milles <[email protected]> AuthorDate: Wed Jul 8 10:42:17 2026 -0500 GROOVY-12117: replace super trait search with more complete helper stub --- .../transform/trait/TraitReceiverTransformer.java | 13 ----- .../codehaus/groovy/transform/trait/Traits.java | 64 +++++++++++++++++++--- 2 files changed, 55 insertions(+), 22 deletions(-) diff --git a/src/main/java/org/codehaus/groovy/transform/trait/TraitReceiverTransformer.java b/src/main/java/org/codehaus/groovy/transform/trait/TraitReceiverTransformer.java index 1176bb53d5..9e7a10bc3e 100644 --- a/src/main/java/org/codehaus/groovy/transform/trait/TraitReceiverTransformer.java +++ b/src/main/java/org/codehaus/groovy/transform/trait/TraitReceiverTransformer.java @@ -419,19 +419,6 @@ class TraitReceiverTransformer extends ClassCodeExpressionTransformer { return methodNode; } } - // GROOVY-12117: when a co-compiled super trait has not been transformed - // yet, its helper is still an empty GROOVY-7909 stub, so the lowered - // static above is not found. The original static is still declared on - // the trait node at this point, so resolve it there. This keeps the - // rewrite independent of the order in which sibling traits are - // transformed (GEP-22 P1' dispatch consistency); the helper resolves - // identically once every trait is lowered, so this only matters for - // the not-yet-lowered super trait. - for (MethodNode methodNode : superTrait.getDeclaredMethods(methodName)) { - if (methodNode.isPublic() && methodNode.isStatic()) { - return methodNode; - } - } } return null; diff --git a/src/main/java/org/codehaus/groovy/transform/trait/Traits.java b/src/main/java/org/codehaus/groovy/transform/trait/Traits.java index 529b7d9dfd..9536b3c10c 100644 --- a/src/main/java/org/codehaus/groovy/transform/trait/Traits.java +++ b/src/main/java/org/codehaus/groovy/transform/trait/Traits.java @@ -28,6 +28,7 @@ import org.codehaus.groovy.ast.ClassNode; import org.codehaus.groovy.ast.FieldNode; import org.codehaus.groovy.ast.GenericsType; import org.codehaus.groovy.ast.MethodNode; +import org.codehaus.groovy.ast.Parameter; import org.codehaus.groovy.ast.expr.ClassExpression; import org.codehaus.groovy.ast.expr.Expression; import org.codehaus.groovy.ast.expr.ListExpression; @@ -319,15 +320,10 @@ public abstract class Traits { GenericsType[] typeArguments = trait.getGenericsTypes(); if (helperClassNode != null) { helperClassNode = GenericsUtils.makeClassSafe0(helperClassNode, typeArguments); - } else { // GROOVY-7909: stub helper - helperClassNode = new ClassNode( - Traits.helperClassName(trait), - ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_SYNTHETIC, - ClassHelper.OBJECT_TYPE - ){{ - isPrimaryNode = false; - setGenericsTypes(typeArguments); - }}; + } else { + // GROOVY-7909, GROOVY-12117: stub the helper + helperClassNode = new HelperClassStub(trait); + helperClassNode.setGenericsTypes(typeArguments); } if (fieldHelperClassNode != null) { fieldHelperClassNode = GenericsUtils.makeClassSafe0(fieldHelperClassNode, typeArguments); @@ -336,6 +332,56 @@ public abstract class Traits { return new TraitHelpersTuple(helperClassNode, fieldHelperClassNode, staticFieldHelperClassNode); } + /** + * GROOVY-7909, GROOVY-12117: When a co-compiled super trait hasn't yet been + * transformed, its helper is stubbed so the lowered static methods can be + * found. This keeps the rewrite independent of the order in which sibling + * traits are transformed (GEP-22 dispatch consistency); the helper resolves + * identically once every trait is lowered, so this only matters for the + * not-yet-lowered super trait. + */ + private static class HelperClassStub extends ClassNode { + + private final ClassNode trait; + + @Override + public ClassNode getOuterClass() { + return trait; + } + + HelperClassStub(final ClassNode trait) { + super(Traits.helperClassName(trait), ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_SYNTHETIC, ClassHelper.OBJECT_TYPE); + this.isPrimaryNode = false; + this.trait = trait; + + for (MethodNode m : trait.getMethods()) { + if ((m.isPublic() || m.isPrivate()) && !m.isAbstract() && !m.isStaticConstructor()) { + addMethod(helperMethod(m)); + } + } + } + + private MethodNode helperMethod(final MethodNode method) { + int mods = (method.isPublic() ? ACC_PUBLIC : ACC_PRIVATE) | ACC_STATIC; + var self = !method.isStatic() ? trait : GenericsUtils.makeClassSafe0(ClassHelper.CLASS_Type, new GenericsType(trait)); + + var methodParams = method.getParameters(); + var helperParams = new Parameter[methodParams.length + 1]; + helperParams[0] = new Parameter(self, "traitImplementer"); + System.arraycopy(methodParams, 0, helperParams, 1, methodParams.length); + + var m = new MethodNode(method.getName(), mods, method.getReturnType(), helperParams, method.getExceptions(), null); + for (AnnotationNode annotation : method.getAnnotations()) { + if (!annotation.getClassNode().equals(ClassHelper.OVERRIDE_TYPE)) { + m.addAnnotation(annotation); + } + } + m.addAnnotation(Traits.IMPLEMENTED_CLASSNODE); + m.setGenericsTypes(method.getGenericsTypes()); + return m; + } + } + /** * Returns true if the specified class node is a trait. *
