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 2026/10/22 22:42:25 UTC
(groovy) branch GROOVY-12473 updated (593a9a76ad -> d66bb86723)
This is an automated email from the ASF dual-hosted git repository.
asf-gitbox-commits pushed a change to branch GROOVY-12473
in repository https://gitbox.apache.org/repos/asf/groovy.git
discard 593a9a76ad GROOVY-12473: SC: read class property from static getter or static field
new d66bb86723 GROOVY-12473: SC: read class property from static getter or static field
This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version. This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:
* -- * -- B -- O -- O -- O (593a9a76ad)
\
N -- N -- N refs/heads/GROOVY-12473 (d66bb86723)
You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.
Any revisions marked "omit" are not gone; other references still
refer to them. Any revisions marked "discard" are gone forever.
The 1 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:
.../groovy/classgen/asm/sc/StaticTypesCallSiteWriter.java | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
(groovy) 01/01: GROOVY-12473: SC: read class property from static getter or static field
Posted by em...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
asf-gitbox-commits pushed a commit to branch GROOVY-12473
in repository https://gitbox.apache.org/repos/asf/groovy.git
commit d66bb867237ffd136011e01a66aa17d00a8eaa9e
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Thu Oct 22 17:23:21 2026 -0500
GROOVY-12473: SC: read class property from static getter or static field
of specific type, then finally from `java.lang.Class`
---
.../classgen/asm/sc/StaticTypesCallSiteWriter.java | 100 ++++++++++++---------
.../stc/FieldsAndPropertiesSTCTest.groovy | 52 +++++++++++
2 files changed, 108 insertions(+), 44 deletions(-)
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesCallSiteWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesCallSiteWriter.java
index 421ed1f5de..476888d91d 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesCallSiteWriter.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesCallSiteWriter.java
@@ -159,7 +159,7 @@ public class StaticTypesCallSiteWriter extends CallSiteWriter {
/** {@inheritDoc} */
@Override
public void makeGetPropertySite(final Expression receiver, final String propertyName, final boolean safe, final boolean implicitThis) {
- Object dynamic = receiver.getNodeMetaData(StaticCompilationMetadataKeys.RECEIVER_OF_DYNAMIC_PROPERTY);
+ var dynamic = receiver.getNodeMetaData(StaticCompilationMetadataKeys.RECEIVER_OF_DYNAMIC_PROPERTY);
if (dynamic != null) {
makeDynamicGetProperty(receiver, propertyName, safe);
return;
@@ -167,41 +167,54 @@ public class StaticTypesCallSiteWriter extends CallSiteWriter {
boolean[] isClassReceiver = new boolean[1];
ClassNode receiverType = getPropertyOwnerType(receiver, isClassReceiver);
- if (receiverType.isArray() && "length".equals(propertyName)) {
- receiver.visit(controller.getAcg());
- controller.getOperandStack().doGroovyCast(receiverType); // GROOVY-5683, GROOVY-11011
- controller.getMethodVisitor().visitInsn(ARRAYLENGTH);
- controller.getOperandStack().replace(int_TYPE);
- return;
- }
- // GROOVY-12314: no special case for "size"/"length" on Collection receivers: the
- // type checker no longer admits either name unless a genuine member backs it, and
- // rewriting an admitted member to size() produced silently wrong values
- if (makeGetPropertyWithGetter(receiver, receiverType, propertyName, safe, implicitThis)) return;
+ if (isClassReceiver[0]) {
+ Expression theClass = receiver instanceof ClassExpression ? receiver : classX(receiverType); // GROOVY-12473
- boolean isStaticProperty = (receiver instanceof ClassExpression
- && (receiverType.isDerivedFrom(receiver.getType()) || receiverType.implementsInterface(receiver.getType())));
+ if (makeGetPropertyWithGetter(theClass, receiverType, propertyName, safe, implicitThis)) {
+ return;
+ }
+ if (makeGetField (theClass, receiverType, propertyName, safe, implicitThis)) {
+ return;
+ }
+ if (makeGetPropertyWithGetter(receiver, CLASS_Type, propertyName, safe, implicitThis)) {
+ return;
+ }
+ } else {
+ if (receiverType.isArray() && "length".equals(propertyName)) {
+ receiver.visit(controller.getAcg());
+ controller.getOperandStack().doGroovyCast(receiverType); // GROOVY-5683, GROOVY-11011
+ controller.getMethodVisitor().visitInsn(ARRAYLENGTH);
+ controller.getOperandStack().replace(int_TYPE);
+ return;
+ }
- // GROOVY-5001, GROOVY-5491, GROOVY-5517, GROOVY-6144, GROOVY-8788: for map types,
- // replace "map.foo" with "map.get('foo')" -- if no public field "foo" is declared
- if (!isStaticProperty && isOrImplements(receiverType, MAP_TYPE)
- && getField(receiverType, propertyName, FieldNode::isPublic) == null) {
- writeMapDotProperty(receiver, propertyName, safe);
- return;
- }
+ if (makeGetPropertyWithGetter(receiver, receiverType, propertyName, safe, implicitThis)) {
+ return;
+ }
- if (makeGetField(receiver, receiverType, propertyName, safe, implicitThis)) return;
- if (isThisExpression(receiver) && receiverType.getOuterClass() != null) { // GROOVY-11198: outer field
- if (makeGetField(receiver, receiverType.getOuterClass(), propertyName, safe, implicitThis)) return;
- }
- if (isClassReceiver[0]) {
- if (makeGetPropertyWithGetter(receiver, CLASS_Type, propertyName, safe, implicitThis)) return;
- }
+ // GROOVY-5001, GROOVY-5491, GROOVY-5517, GROOVY-6144, GROOVY-8788: for map types,
+ // replace "map.foo" with "map.get('foo')" -- if no public field "foo" is declared
+ if (isOrImplements(receiverType, MAP_TYPE)
+ && getField(receiverType, propertyName, FieldNode::isPublic) == null) {
+ writeMapDotProperty(receiver, propertyName, safe);
+ return;
+ }
- if (!isStaticProperty && isOrImplements(receiverType, LIST_TYPE)) {
- writeListDotProperty(receiver, propertyName, safe);
- return;
+ if (makeGetField(receiver, receiverType, propertyName, safe, implicitThis)) {
+ return;
+ }
+
+ if (isThisExpression(receiver) && receiverType.getOuterClass() != null) { // GROOVY-11198: outer field
+ if (makeGetField(receiver, receiverType.getOuterClass(), propertyName, safe, implicitThis)) {
+ return;
+ }
+ }
+
+ if (isOrImplements(receiverType, LIST_TYPE)) {
+ writeListDotProperty(receiver, propertyName, safe);
+ return;
+ }
}
addPropertyAccessError(receiver, propertyName, receiverType);
@@ -794,16 +807,14 @@ public class StaticTypesCallSiteWriter extends CallSiteWriter {
}
private ClassNode getPropertyOwnerType(final Expression receiver, final boolean... isClassReceiver) {
- Object inferredType = receiver.getNodeMetaData(StaticTypesMarker.INFERRED_TYPE);
- if (inferredType == null && receiver instanceof VariableExpression) {
- Variable variable = ((VariableExpression) receiver).getAccessedVariable();
- if (variable instanceof Expression) {
- inferredType = ((Expression) variable).getNodeMetaData(StaticTypesMarker.INFERRED_TYPE);
- }
+ var inferredType = receiver.getNodeMetaData(StaticTypesMarker.INFERRED_TYPE);
+ if (inferredType == null && receiver instanceof VariableExpression ve
+ && ve.getAccessedVariable() instanceof Expression variable) {
+ inferredType = variable.getNodeMetaData(StaticTypesMarker.INFERRED_TYPE);
}
ClassNode receiverType;
if (inferredType instanceof ClassNode) {
- // in case a "flow type" is found, it is preferred to use it instead of the declaration type
+ // in case a "flow type" is found, it is preferred over the declaration type
receiverType = (ClassNode) inferredType;
} else {
receiverType = receiver.getNodeMetaData(StaticCompilationMetadataKeys.PROPERTY_OWNER);
@@ -811,13 +822,14 @@ public class StaticTypesCallSiteWriter extends CallSiteWriter {
receiverType = controller.getTypeChooser().resolveType(receiver, controller.getClassNode());
}
}
- if (isClassClassNodeWrappingConcreteType(receiverType)) {
- if (isClassReceiver.length > 0) isClassReceiver[0] = true;
- receiverType = receiverType.getGenericsTypes()[0].getType();
- }
if (isPrimitiveType(receiverType)) {
- // GROOVY-6590: wrap primitive types
- receiverType = getWrapper(receiverType);
+ receiverType = getWrapper(receiverType); // GROOVY-6590
+ } else if (isClassType(receiverType)
+ || isClassType(receiver.getType())) { // GROOVY-12473
+ if (isClassReceiver.length > 0) isClassReceiver[0]= true;
+ if (isClassClassNodeWrappingConcreteType(receiverType)) {
+ receiverType = receiverType.getGenericsTypes()[0].getType();
+ }
}
return receiverType;
}
diff --git a/src/test/groovy/groovy/transform/stc/FieldsAndPropertiesSTCTest.groovy b/src/test/groovy/groovy/transform/stc/FieldsAndPropertiesSTCTest.groovy
index 4e1ab160f3..f3e85a66d5 100644
--- a/src/test/groovy/groovy/transform/stc/FieldsAndPropertiesSTCTest.groovy
+++ b/src/test/groovy/groovy/transform/stc/FieldsAndPropertiesSTCTest.groovy
@@ -1423,6 +1423,58 @@ class FieldsAndPropertiesSTCTest extends StaticTypeCheckingTestCase {
'''
}
+ @Test
+ void testClassPropertyAccess1() {
+ assertScript '''
+ assert Float.isNaN(Float.NaN)
+ '''
+ }
+
+ @Test
+ void testClassPropertyAccess2() {
+ assertScript '''
+ assert Float.isNaN(Float.@NaN)
+ '''
+ }
+
+ @Test
+ void testClassPropertyAccess3() {
+ assertScript '''
+ assert Float.isNaN(Float.class.NaN)
+ '''
+ }
+
+ @Test
+ void testClassPropertyAccess4() {
+ assertScript '''
+ assert Float.isNaN(Float.class.@NaN)
+ '''
+ }
+
+ @Test
+ void testClassPropertyAccess5() {
+ assertScript '''
+ Class<Float> float_class = Float
+ assert Float.isNaN(float_class.NaN)
+ '''
+ }
+
+ @Test
+ void testClassPropertyAccess6() {
+ assertScript '''
+ Class<Float> float_class = Float
+ assert Float.isNaN(float_class.@NaN)
+ '''
+ }
+
+ // GROOVY-12473
+ @Test
+ void testClassPropertyAccess7() {
+ assertScript '''
+ assert Float.isNaN((Float as Class<Float>).NaN)
+ '''
+ }
+
@Test
void testOuterPropertyAccess1() {
assertScript '''