This is an automated email from the ASF dual-hosted git repository.
emilles pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/master by this push:
new bd20770a98 GROOVY-7909, GROOVY-11743: stub trait helper in
`Traits#findHelpers`
bd20770a98 is described below
commit bd20770a9891684b4d455b31184dac522d03fff6
Author: Eric Milles <[email protected]>
AuthorDate: Sat Feb 21 14:21:45 2026 -0600
GROOVY-7909, GROOVY-11743: stub trait helper in `Traits#findHelpers`
---
.../transform/trait/SuperCallTraitTransformer.java | 33 +---------
.../transform/trait/TraitASTTransformation.java | 25 ++-----
.../groovy/transform/trait/TraitComposer.java | 8 ++-
.../transform/trait/TraitReceiverTransformer.java | 1 -
.../codehaus/groovy/transform/trait/Traits.java | 42 ++++++++----
src/test/groovy/bugs/Groovy7909Bug.groovy | 76 ----------------------
.../traitx/TraitASTTransformationTest.groovy | 30 +++++++++
7 files changed, 75 insertions(+), 140 deletions(-)
diff --git
a/src/main/java/org/codehaus/groovy/transform/trait/SuperCallTraitTransformer.java
b/src/main/java/org/codehaus/groovy/transform/trait/SuperCallTraitTransformer.java
index 2c25f97b6b..8ebe3b4277 100644
---
a/src/main/java/org/codehaus/groovy/transform/trait/SuperCallTraitTransformer.java
+++
b/src/main/java/org/codehaus/groovy/transform/trait/SuperCallTraitTransformer.java
@@ -21,7 +21,6 @@ package org.codehaus.groovy.transform.trait;
import org.codehaus.groovy.ast.ClassCodeExpressionTransformer;
import org.codehaus.groovy.ast.ClassHelper;
import org.codehaus.groovy.ast.ClassNode;
-import org.codehaus.groovy.ast.InnerClassNode;
import org.codehaus.groovy.ast.MethodNode;
import org.codehaus.groovy.ast.Parameter;
import org.codehaus.groovy.ast.expr.ArgumentListExpression;
@@ -46,10 +45,6 @@ import static
org.codehaus.groovy.ast.tools.GeneralUtils.getGetterName;
import static org.codehaus.groovy.ast.tools.GeneralUtils.getSetterName;
import static org.codehaus.groovy.ast.tools.GeneralUtils.thisPropX;
import static org.codehaus.groovy.ast.tools.GeneralUtils.varX;
-import static org.objectweb.asm.Opcodes.ACC_ABSTRACT;
-import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
-import static org.objectweb.asm.Opcodes.ACC_STATIC;
-import static org.objectweb.asm.Opcodes.ACC_SYNTHETIC;
/**
* This transformer is used to transform calls to
<code>SomeTrait.super.foo()</code> into the appropriate trait call.
@@ -58,8 +53,6 @@ import static org.objectweb.asm.Opcodes.ACC_SYNTHETIC;
*/
class SuperCallTraitTransformer extends ClassCodeExpressionTransformer {
- static final String UNRESOLVED_HELPER_CLASS = "UNRESOLVED_HELPER_CLASS";
-
private final SourceUnit unit;
SuperCallTraitTransformer(final SourceUnit unit) {
@@ -98,7 +91,7 @@ class SuperCallTraitTransformer extends
ClassCodeExpressionTransformer {
if (bin.getOperation().getType() == Types.ASSIGN &&
bin.getLeftExpression() instanceof PropertyExpression leftExpression) {
ClassNode traitType =
getTraitSuperTarget(leftExpression.getObjectExpression());
if (traitType != null) {
- ClassNode helperType = getHelper(traitType);
+ ClassNode helperType =
Traits.findHelper(traitType).getPlainNodeReference();
// TraitType.super.foo = ... ->
TraitType$Trait$Helper.setFoo(this, ...)
String setterName =
getSetterName(leftExpression.getPropertyAsString());
@@ -134,7 +127,7 @@ class SuperCallTraitTransformer extends
ClassCodeExpressionTransformer {
if (exp.getNodeMetaData("assign.target") == null) {
ClassNode traitType =
getTraitSuperTarget(exp.getObjectExpression());
if (traitType != null) {
- ClassNode helperType = getHelper(traitType);
+ ClassNode helperType =
Traits.findHelper(traitType).getPlainNodeReference();
// TraitType.super.foo -> TraitType$Trait$Helper.getFoo(this)
Function<MethodNode, MethodCallExpression> xform =
(methodNode) -> {
@@ -173,7 +166,7 @@ class SuperCallTraitTransformer extends
ClassCodeExpressionTransformer {
private Expression transformMethodCallExpression(final
MethodCallExpression exp) {
ClassNode traitType = getTraitSuperTarget(exp.getObjectExpression());
if (traitType != null) {
- ClassNode helperType = getHelper(traitType);
+ ClassNode helperType =
Traits.findHelper(traitType).getPlainNodeReference();
// TraitType.super.foo() -> TraitType$Trait$Helper.foo(this)
List<MethodNode> targets =
helperType.getMethods(exp.getMethodAsString());
@@ -200,26 +193,6 @@ class SuperCallTraitTransformer extends
ClassCodeExpressionTransformer {
return super.transform(exp);
}
- private ClassNode getHelper(final ClassNode traitType) {
- // GROOVY-7909: A helper class in the same compilation unit may not
have
- // been created when referenced; create a placeholder to be resolved
later.
- if (!traitType.redirect().getInnerClasses().hasNext()
- &&
getSourceUnit().getAST().getClasses().contains(traitType.redirect())) {
- ClassNode helperType = new InnerClassNode(
- traitType,
- Traits.helperClassName(traitType),
- ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_SYNTHETIC,
- ClassHelper.OBJECT_TYPE,
- ClassNode.EMPTY_ARRAY,
- null
- ).getPlainNodeReference();
- helperType.setRedirect(null);
- traitType.redirect().setNodeMetaData(UNRESOLVED_HELPER_CLASS,
helperType);
- return helperType;
- }
- return Traits.findHelper(traitType).getPlainNodeReference();
- }
-
private ClassNode getTraitSuperTarget(final Expression exp) {
if (exp instanceof PropertyExpression pexp) {
Expression objExp = pexp.getObjectExpression();
diff --git
a/src/main/java/org/codehaus/groovy/transform/trait/TraitASTTransformation.java
b/src/main/java/org/codehaus/groovy/transform/trait/TraitASTTransformation.java
index 70daf4acff..60829e083a 100644
---
a/src/main/java/org/codehaus/groovy/transform/trait/TraitASTTransformation.java
+++
b/src/main/java/org/codehaus/groovy/transform/trait/TraitASTTransformation.java
@@ -94,7 +94,6 @@ import static
org.codehaus.groovy.ast.tools.GeneralUtils.throwS;
import static org.codehaus.groovy.ast.tools.GeneralUtils.tryCatchS;
import static org.codehaus.groovy.ast.tools.GeneralUtils.varX;
import static
org.codehaus.groovy.ast.tools.PropertyNodeUtils.adjustPropertyModifiersForMethod;
-import static
org.codehaus.groovy.transform.trait.SuperCallTraitTransformer.UNRESOLVED_HELPER_CLASS;
import static org.objectweb.asm.Opcodes.ACC_ABSTRACT;
import static org.objectweb.asm.Opcodes.ACC_FINAL;
import static org.objectweb.asm.Opcodes.ACC_INTERFACE;
@@ -139,7 +138,11 @@ public class TraitASTTransformation extends
AbstractASTTransformation implements
checkExtendsClause(cNode);
replaceExtendsByImplements(cNode);
generateMethodsWithDefaultArgs(cNode);
- resolveHelperClassIfNecessary(createHelperClass(cNode));
+
+ cNode.setModifiers(ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE
+ | (cNode.getOuterClass() != null ? ACC_STATIC : 0)); //
GROOVY-11600
+
+ createHelperClasses(cNode);
}
}
@@ -182,10 +185,7 @@ public class TraitASTTransformation extends
AbstractASTTransformation implements
}.addDefaultParameterMethods(cNode);
}
- private ClassNode createHelperClass(final ClassNode cNode) {
- cNode.setModifiers(ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE
- | (cNode.getOuterClass() != null ? ACC_STATIC : 0)); //
GROOVY-11600
-
+ private void createHelperClasses(final ClassNode cNode) {
ClassNode helper = new InnerClassNode(
cNode,
Traits.helperClassName(cNode),
@@ -252,7 +252,7 @@ public class TraitASTTransformation extends
AbstractASTTransformation implements
if (!methodNode.isSynthetic() && (methodNode.isProtected() ||
methodNode.isPackageScope())) {
sourceUnit.addError(new SyntaxException("Cannot have
protected/package-private method in a trait (" + cNode.getName() + "#" +
methodNode.getTypeDescriptor() + ")",
methodNode.getLineNumber(),
methodNode.getColumnNumber()));
- return null;
+ return;
}
if (!methodNode.isAbstract()) {
MethodNode newMethod = processMethod(cNode, helper,
methodNode, fieldHelper, fieldNames);
@@ -317,17 +317,6 @@ public class TraitASTTransformation extends
AbstractASTTransformation implements
resolveScope(staticFieldHelper);
}
}
-
- return helper;
- }
-
- private void resolveHelperClassIfNecessary(final ClassNode helperClass) {
- for (ClassNode cn : sourceUnit.getAST().getClasses()) {
- ClassNode unresolvedHelperClass =
cn.getNodeMetaData(UNRESOLVED_HELPER_CLASS);
- if (unresolvedHelperClass != null &&
unresolvedHelperClass.getName().equals(helperClass.getName())) {
- unresolvedHelperClass.setRedirect(helperClass);
- }
- }
}
//--------------------------------------------------------------------------
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 713617e71d..f60866c4b0 100644
--- a/src/main/java/org/codehaus/groovy/transform/trait/TraitComposer.java
+++ b/src/main/java/org/codehaus/groovy/transform/trait/TraitComposer.java
@@ -18,7 +18,6 @@
*/
package org.codehaus.groovy.transform.trait;
-import groovy.transform.CompileStatic;
import org.apache.groovy.ast.tools.MethodNodeUtils;
import org.codehaus.groovy.ast.ASTNode;
import org.codehaus.groovy.ast.AnnotationNode;
@@ -47,6 +46,7 @@ import org.codehaus.groovy.runtime.InvokerHelper;
import org.codehaus.groovy.runtime.MetaClassHelper;
import org.codehaus.groovy.syntax.SyntaxException;
import org.codehaus.groovy.transform.ASTTransformationCollectorCodeVisitor;
+import org.codehaus.groovy.transform.sc.StaticCompilationVisitor;
import org.codehaus.groovy.transform.sc.StaticCompileTransformation;
import org.codehaus.groovy.transform.stc.StaticTypesMarker;
import org.objectweb.asm.Opcodes;
@@ -85,7 +85,11 @@ import static
org.codehaus.groovy.ast.tools.GeneralUtils.varX;
*/
public abstract class TraitComposer {
- public static final ClassNode COMPILESTATIC_CLASSNODE =
ClassHelper.make(CompileStatic.class);
+ /**
+ * @deprecated
+ */
+ @Deprecated(since = "6.0.0")
+ public static final ClassNode COMPILESTATIC_CLASSNODE =
StaticCompilationVisitor.COMPILESTATIC_CLASSNODE;
/**
* Given a class node, if this class node implements a trait, then generate
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 787cf26543..99baf45201 100644
---
a/src/main/java/org/codehaus/groovy/transform/trait/TraitReceiverTransformer.java
+++
b/src/main/java/org/codehaus/groovy/transform/trait/TraitReceiverTransformer.java
@@ -317,7 +317,6 @@ class TraitReceiverTransformer extends
ClassCodeExpressionTransformer {
for (ClassNode superTrait : traits) {
ClassNode traitHelper = Traits.findHelper(superTrait);
- if (traitHelper == null) continue; // GROOVY-11743: order issue
for (MethodNode methodNode :
traitHelper.getDeclaredMethods(methodName)) {
if (methodNode.isPublic() && methodNode.isStatic()
// exclude public method with body as it's included in
trait interface
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 1d2dc7ec1b..40ed22b110 100644
--- a/src/main/java/org/codehaus/groovy/transform/trait/Traits.java
+++ b/src/main/java/org/codehaus/groovy/transform/trait/Traits.java
@@ -34,7 +34,6 @@ import org.codehaus.groovy.ast.expr.ListExpression;
import org.codehaus.groovy.ast.tools.GenericsUtils;
import org.codehaus.groovy.classgen.asm.BytecodeHelper;
import org.codehaus.groovy.runtime.DefaultGroovyMethods;
-import org.objectweb.asm.Opcodes;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
@@ -46,12 +45,21 @@ import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
+import static org.objectweb.asm.Opcodes.ACC_ABSTRACT;
+import static org.objectweb.asm.Opcodes.ACC_FINAL;
+import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
+import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
+import static org.objectweb.asm.Opcodes.ACC_STATIC;
+import static org.objectweb.asm.Opcodes.ACC_SYNTHETIC;
+import static org.objectweb.asm.Opcodes.ACC_TRANSIENT;
+
/**
* A collection of utility methods used to deal with traits.
*
* @since 2.3.0
*/
public abstract class Traits {
+
public static final ClassNode IMPLEMENTED_CLASSNODE =
ClassHelper.make(Implemented.class);
public static final ClassNode TRAITBRIDGE_CLASSNODE =
ClassHelper.make(TraitBridge.class);
public static final Class<Trait> TRAIT_CLASS = Trait.class;
@@ -79,7 +87,7 @@ public abstract class Traits {
// (a ? hex('80') : 0) + (b ? hex('10') : 0) + (c ? hex('8') : 0) +
(d ? hex('2') : hex('1'))
// }.sort()
static final List<Integer> FIELD_PREFIXES = Arrays.asList(1, 2, 9, 10, 17,
18, 25, 26, 129, 130, 137, 138, 145, 146, 153, 154);
- static final int FIELD_PREFIX_MASK = Opcodes.ACC_PRIVATE |
Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL |
Opcodes.ACC_TRANSIENT;
+ static final int FIELD_PREFIX_MASK = ACC_PRIVATE | ACC_PUBLIC | ACC_STATIC
| ACC_FINAL | ACC_TRANSIENT;
static final String SUPER_TRAIT_METHOD_PREFIX = "trait$super$";
static String fieldHelperClassName(final ClassNode traitNode) {
@@ -129,11 +137,10 @@ public abstract class Traits {
ClassNode helperClassNode = null;
ClassNode fieldHelperClassNode = null;
ClassNode staticFieldHelperClassNode = null;
- var innerClasses = trait.redirect().getInnerClasses();
- if (innerClasses != null && innerClasses.hasNext() ) {
- // trait declared in same unit
- do {
- ClassNode icn = innerClasses.next();
+
+ if (trait.isPrimaryClassNode()) { // GROOVY-11743
+ var ici = trait.redirect().getInnerClasses();
+ while (ici.hasNext()) { ClassNode icn = ici.next();
if (icn.getName().endsWith(Traits.TRAIT_HELPER)) {
helperClassNode = icn;
} else if (icn.getName().endsWith(Traits.FIELD_HELPER)) {
@@ -141,30 +148,39 @@ public abstract class Traits {
} else if (icn.getName().endsWith(Traits.STATIC_FIELD_HELPER))
{
staticFieldHelperClassNode = icn;
}
- } while (innerClasses.hasNext());
- } else if (!trait.isPrimaryClassNode()) { // GROOVY-11743
- // precompiled trait
+ }
+ } else { // pre-compiled trait
try {
- String helperClassName = Traits.helperClassName(trait);
ClassLoader classLoader =
trait.getTypeClass().getClassLoader();
- helperClassNode =
ClassHelper.make(Class.forName(helperClassName, false, classLoader));
+ helperClassNode =
ClassHelper.make(classLoader.loadClass(Traits.helperClassName(trait)));
try {
fieldHelperClassNode =
ClassHelper.make(classLoader.loadClass(Traits.fieldHelperClassName(trait)));
staticFieldHelperClassNode =
ClassHelper.make(classLoader.loadClass(Traits.staticFieldHelperClassName(trait)));
} catch (ClassNotFoundException e) {
- // not a problem, the field helpers may be absent
+ // field helper(s) may be absent
}
} catch (ClassNotFoundException e) {
throw new GroovyBugError("Couldn't find trait helper classes
on compile classpath!", e);
}
}
+
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);
+ }};
}
if (fieldHelperClassNode != null) {
fieldHelperClassNode =
GenericsUtils.makeClassSafe0(fieldHelperClassNode, typeArguments);
}
+
return new TraitHelpersTuple(helperClassNode, fieldHelperClassNode,
staticFieldHelperClassNode);
}
diff --git a/src/test/groovy/bugs/Groovy7909Bug.groovy
b/src/test/groovy/bugs/Groovy7909Bug.groovy
deleted file mode 100644
index 71571ef6b4..0000000000
--- a/src/test/groovy/bugs/Groovy7909Bug.groovy
+++ /dev/null
@@ -1,76 +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 bugs
-
-import gls.CompilableTestSupport
-
-class Groovy7909Bug extends CompilableTestSupport {
- void testDynamicCompile(){
- shouldCompile '''
-trait Three implements One, Two {
- def postMake() {
- One.super.postMake()
- Two.super.postMake()
- println "Three"
- }
-}
-trait One {
- def postMake() { println "One"}
-}
-trait Two {
- def postMake() { println "Two"}
-}
-class Four implements Three {
- def make() {
- Three.super.postMake()
- println "All done?"
- }
-}
-Four f = new Four()
-f.make()
- '''
- }
- void testStaticCompile(){
- shouldCompile '''
[email protected]
-trait Three implements One, Two {
- def postMake() {
- One.super.postMake()
- Two.super.postMake()
- println "Three"
- }
-}
-trait One {
- def postMake() { println "One"}
-}
-trait Two {
- def postMake() { println "Two"}
-}
-class Four implements Three {
- def make() {
- Three.super.postMake()
- println "All done?"
- }
-}
-Four f = new Four()
-f.make()
- '''
- }
-}
-
diff --git
a/src/test/groovy/org/codehaus/groovy/transform/traitx/TraitASTTransformationTest.groovy
b/src/test/groovy/org/codehaus/groovy/transform/traitx/TraitASTTransformationTest.groovy
index aeb2353c4d..e3151c340d 100644
---
a/src/test/groovy/org/codehaus/groovy/transform/traitx/TraitASTTransformationTest.groovy
+++
b/src/test/groovy/org/codehaus/groovy/transform/traitx/TraitASTTransformationTest.groovy
@@ -1212,6 +1212,36 @@ final class TraitASTTransformationTest {
'''
}
+ // GROOVY-7909
+ @CompileModesTest
+ void testTraitMethodOverloadAndOverride2(String mode) {
+ assertScript shell, """
+ $mode
+ trait Three implements One, Two {
+ def postMake() {
+ '3' + Two.super.postMake() + One.super.postMake()
+ }
+ }
+ $mode
+ trait One {
+ def postMake() { '1' }
+ }
+ $mode
+ trait Two {
+ def postMake() { '2' }
+ }
+ $mode
+ class Four implements Three {
+ def make() {
+ '4' + Three.super.postMake()
+ }
+ }
+
+ String result = new Four().make()
+ assert result == '4321'
+ """
+ }
+
// GROOVY-9255
@Test
void testTraitSuperPropertyGet() {