Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ private Expression transformBinaryExpression(final BinaryExpression exp, final C
private Expression transformFieldReference(final Expression exp, final FieldNode fn, final boolean isStatic) {
Expression receiver = createFieldHelperReceiver();
if (isStatic) {
receiver = asClass(receiver);
receiver = asClass(receiver, fn); // GROOVY-11907
}

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

private Expression asClass(final Expression e) {
return asClass(e, null);
}

private Expression asClass(final Expression e, final FieldNode fn) {
ClassNode rawClass = ClassHelper.CLASS_Type.getPlainNodeReference();
return ternaryX(isInstanceOfX(e, rawClass), e, callX(e, "getClass"));
MethodCallExpression getClassCall = callX(e, "getClass");
if (fn != null && fn.isStatic()) {
// GROOVY-11907: mark sub-expression for dynamic dispatch so that
// GROOVY-11817's per-expression DYNAMIC_RESOLUTION check handles it
getClassCall.putNodeMetaData(org.codehaus.groovy.transform.stc.StaticTypesMarker.DYNAMIC_RESOLUTION, rawClass);
}
return ternaryX(isInstanceOfX(e, rawClass), e, getClassCall);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4053,4 +4053,45 @@ final class TraitASTTransformationTest {
new C().test()
'''
}

// GROOVY-11907
@Test
void testTraitStaticFieldHelperGetClassMarkedDynamic() {
// When a global AST transform alters class processing order (e.g. Spock's SpockTransform),
// TraitTypeCheckingExtension.handleMissingMethod is invoked for the static field helper.
// It calls makeDynamic() on the outer MCE, but the inner getClass() sub-expression was
// not marked, producing incompatible stack frame types. Verify the fix: asClass(e, fn)
// must mark the getClass() call with DYNAMIC_RESOLUTION for static fields.
def cu = new org.codehaus.groovy.control.CompilationUnit()
cu.addSource('MyTrait.groovy', '''
@groovy.transform.CompileStatic
trait MyTrait {
static String myField
}
''')
cu.compile(org.codehaus.groovy.control.Phases.CANONICALIZATION)

// Find the helper class that accesses the static field
def allClasses = cu.AST.modules.collectMany { it.classes }
def traitHelper = allClasses.find { it.name.contains('Helper') }
assert traitHelper != null, "Trait helper class not found in: ${allClasses*.name}"

// Walk the AST to find getClass() calls in the helper methods
def getClassCalls = []
def visitor = new org.codehaus.groovy.ast.CodeVisitorSupport() {
void visitMethodCallExpression(org.codehaus.groovy.ast.expr.MethodCallExpression mce) {
if (mce.methodAsString == 'getClass') {
getClassCalls << mce
}
super.visitMethodCallExpression(mce)
}
}
traitHelper.methods.each { it.code?.visit(visitor) }

assert !getClassCalls.isEmpty(), 'Expected getClass() call in trait helper for static field'
getClassCalls.each { mce ->
assert mce.getNodeMetaData(org.codehaus.groovy.transform.stc.StaticTypesMarker.DYNAMIC_RESOLUTION) != null,
'getClass() sub-expression must be marked with DYNAMIC_RESOLUTION for static trait fields'
}
}
}
Loading