Skip to content

Commit ecc4df7

Browse files
committed
GROOVY-11907: SC: trait static field helper generates invalid bytecode when method-level DYNAMIC_RESOLUTION is present (GROOVY-11817 regression)
1 parent 9941325 commit ecc4df7

File tree

2 files changed

+53
-2
lines changed

2 files changed

+53
-2
lines changed

src/main/java/org/codehaus/groovy/transform/trait/TraitReceiverTransformer.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ private Expression transformBinaryExpression(final BinaryExpression exp, final C
220220
private Expression transformFieldReference(final Expression exp, final FieldNode fn, final boolean isStatic) {
221221
Expression receiver = createFieldHelperReceiver();
222222
if (isStatic) {
223-
receiver = asClass(receiver);
223+
receiver = asClass(receiver, fn); // GROOVY-11907
224224
}
225225

226226
MethodCallExpression mce = callX(receiver, Traits.helperGetterName(fn));
@@ -352,7 +352,17 @@ private Expression createFieldHelperReceiver() {
352352
}
353353

354354
private Expression asClass(final Expression e) {
355+
return asClass(e, null);
356+
}
357+
358+
private Expression asClass(final Expression e, final FieldNode fn) {
355359
ClassNode rawClass = ClassHelper.CLASS_Type.getPlainNodeReference();
356-
return ternaryX(isInstanceOfX(e, rawClass), e, callX(e, "getClass"));
360+
MethodCallExpression getClassCall = callX(e, "getClass");
361+
if (fn != null && fn.isStatic()) {
362+
// GROOVY-11907: mark sub-expression for dynamic dispatch so that
363+
// GROOVY-11817's per-expression DYNAMIC_RESOLUTION check handles it
364+
getClassCall.putNodeMetaData(org.codehaus.groovy.transform.stc.StaticTypesMarker.DYNAMIC_RESOLUTION, rawClass);
365+
}
366+
return ternaryX(isInstanceOfX(e, rawClass), e, getClassCall);
357367
}
358368
}

src/test/groovy/org/codehaus/groovy/transform/traitx/TraitASTTransformationTest.groovy

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4053,4 +4053,45 @@ final class TraitASTTransformationTest {
40534053
new C().test()
40544054
'''
40554055
}
4056+
4057+
// GROOVY-11907
4058+
@Test
4059+
void testTraitStaticFieldHelperGetClassMarkedDynamic() {
4060+
// When a global AST transform alters class processing order (e.g. Spock's SpockTransform),
4061+
// TraitTypeCheckingExtension.handleMissingMethod is invoked for the static field helper.
4062+
// It calls makeDynamic() on the outer MCE, but the inner getClass() sub-expression was
4063+
// not marked, producing incompatible stack frame types. Verify the fix: asClass(e, fn)
4064+
// must mark the getClass() call with DYNAMIC_RESOLUTION for static fields.
4065+
def cu = new org.codehaus.groovy.control.CompilationUnit()
4066+
cu.addSource('MyTrait.groovy', '''
4067+
@groovy.transform.CompileStatic
4068+
trait MyTrait {
4069+
static String myField
4070+
}
4071+
''')
4072+
cu.compile(org.codehaus.groovy.control.Phases.CANONICALIZATION)
4073+
4074+
// Find the helper class that accesses the static field
4075+
def allClasses = cu.AST.modules.collectMany { it.classes }
4076+
def traitHelper = allClasses.find { it.name.contains('Helper') }
4077+
assert traitHelper != null, "Trait helper class not found in: ${allClasses*.name}"
4078+
4079+
// Walk the AST to find getClass() calls in the helper methods
4080+
def getClassCalls = []
4081+
def visitor = new org.codehaus.groovy.ast.CodeVisitorSupport() {
4082+
void visitMethodCallExpression(org.codehaus.groovy.ast.expr.MethodCallExpression mce) {
4083+
if (mce.methodAsString == 'getClass') {
4084+
getClassCalls << mce
4085+
}
4086+
super.visitMethodCallExpression(mce)
4087+
}
4088+
}
4089+
traitHelper.methods.each { it.code?.visit(visitor) }
4090+
4091+
assert !getClassCalls.isEmpty(), 'Expected getClass() call in trait helper for static field'
4092+
getClassCalls.each { mce ->
4093+
assert mce.getNodeMetaData(org.codehaus.groovy.transform.stc.StaticTypesMarker.DYNAMIC_RESOLUTION) != null,
4094+
'getClass() sub-expression must be marked with DYNAMIC_RESOLUTION for static trait fields'
4095+
}
4096+
}
40564097
}

0 commit comments

Comments
 (0)