Copilot commented on code in PR #2186:
URL: https://github.com/apache/groovy/pull/2186#discussion_r2040641694


##########
src/main/java/org/codehaus/groovy/classgen/VariableScopeVisitor.java:
##########
@@ -192,113 +195,130 @@ private void declare(final Variable variable, final 
ASTNode context) {
         currentScope.putDeclaredVariable(variable);
     }
 
+    private final Map<ClassMemberCacheKey, VariableWrapper> classMemberCache = 
new HashMap<>(64);
     private Variable findClassMember(final ClassNode node, final String name) {
-        final boolean abstractType = node.isAbstract();
-
-        for (ClassNode cn = node; cn != null && !ClassHelper.isObjectType(cn); 
cn = cn.getSuperClass()) {
-            for (FieldNode fn : cn.getFields()) {
-                if (name.equals(fn.getName())) return fn;
-            }
+        VariableWrapper variableWrapper = classMemberCache.computeIfAbsent(new 
ClassMemberCacheKey(name, node), k -> {
+            final ClassNode classNode = k.node;
+            final String memberName = k.name;
+            final boolean abstractType = classNode.isAbstract();
+
+            for (ClassNode cn = classNode; cn != null && 
!ClassHelper.isObjectType(cn); cn = cn.getSuperClass()) {
+                for (FieldNode fn : cn.getFields()) {
+                    if (memberName.equals(fn.getName())) return new 
VariableWrapper(fn);
+                }
 
-            for (PropertyNode pn : cn.getProperties()) {
-                if (name.equals(pn.getName())) return pn;
-            }
+                for (PropertyNode pn : cn.getProperties()) {
+                    if (memberName.equals(pn.getName())) return new 
VariableWrapper(pn);
+                }
 
-            for (MethodNode mn : cn.getMethods()) {
-                if ((abstractType || !mn.isAbstract()) && 
name.equals(getPropertyName(mn))) {
-                    // check for super property before returning a 
pseudo-property
-                    for (PropertyNode pn : 
getAllProperties(cn.getSuperClass())) {
-                        if (name.equals(pn.getName())) return pn;
+                for (MethodNode mn : cn.getMethods()) {
+                    if ((abstractType || !mn.isAbstract()) && 
memberName.equals(getPropertyName(mn))) {
+                        // check for super property before returning a 
pseudo-property
+                        for (PropertyNode pn : 
getAllProperties(cn.getSuperClass())) {
+                            if (memberName.equals(pn.getName())) return new 
VariableWrapper(pn);
+                        }
+
+                        FieldNode fn = new FieldNode(memberName, 
mn.getModifiers() & 0xF, ClassHelper.dynamicType(), cn, null);
+                        fn.setHasNoRealSourcePosition(true);
+                        fn.setDeclaringClass(cn);
+                        fn.setSynthetic(true);
+
+                        PropertyNode pn = new PropertyNode(fn, 
fn.getModifiers(), null, null);
+                        pn.putNodeMetaData("access.method", mn);
+                        pn.setDeclaringClass(cn);
+                        return new VariableWrapper(pn);
                     }
+                }
 
-                    FieldNode fn = new FieldNode(name, mn.getModifiers() & 
0xF, ClassHelper.dynamicType(), cn, null);
-                    fn.setHasNoRealSourcePosition(true);
-                    fn.setDeclaringClass(cn);
-                    fn.setSynthetic(true);
-
-                    PropertyNode pn = new PropertyNode(fn, fn.getModifiers(), 
null, null);
-                    pn.putNodeMetaData("access.method", mn);
-                    pn.setDeclaringClass(cn);
-                    return pn;
+                for (ClassNode in : cn.getAllInterfaces()) {
+                    FieldNode fn = in.getDeclaredField(memberName);
+                    if (fn != null) return new VariableWrapper(fn);
+                    PropertyNode pn = in.getProperty(memberName);
+                    if (pn != null) return new VariableWrapper(pn);
                 }
             }
 
-            for (ClassNode in : cn.getAllInterfaces()) {
-                FieldNode fn = in.getDeclaredField(name);
-                if (fn != null) return fn;
-                PropertyNode pn = in.getProperty(name);
-                if (pn != null) return pn;
-            }
-        }
+            return new VariableWrapper(null);

Review Comment:
   Returning a VariableWrapper wrapping null may lead to ambiguity in cache 
lookups. Consider handling cache misses explicitly so that the absence of a 
variable is distinguishable without wrapping a null value.
   ```suggestion
               return VariableWrapper.EMPTY;
   ```



##########
src/main/java/org/codehaus/groovy/classgen/VariableScopeVisitor.java:
##########
@@ -760,4 +780,66 @@ public void visitVariableExpression(final 
VariableExpression expression) {
             checkVariableContextAccess(variable, expression);
         }
     }
+
+    private static class VariableWrapper {
+        private final Variable variable;
+        private int accessedCount;
+
+        VariableWrapper(final Variable variable) {
+            this.variable = variable;
+        }
+    }
+
+    private static class ClassMemberCacheKey {
+        private static final int DEFAULT_HASH = -1;
+        private final String name;
+        private final ClassNode node;
+        private int hash = DEFAULT_HASH;
+
+        ClassMemberCacheKey(final String name, final ClassNode node) {
+            this.name = name;
+            this.node = node;
+        }
+
+        @Override
+        public boolean equals(final Object obj) {
+            if (this == obj) return true;
+            if (!(obj instanceof ClassMemberCacheKey)) return false;
+            ClassMemberCacheKey that = (ClassMemberCacheKey) obj;
+            return name.equals(that.name) && node.equals(that.node);
+        }
+
+        @Override
+        public int hashCode() {
+            return DEFAULT_HASH != hash ? hash : (hash = Objects.hash(name, 
node));

Review Comment:
   Using -1 as DEFAULT_HASH may cause an issue if Objects.hash returns -1, 
leading to an incorrect cached hash. Consider choosing a different sentinel 
value to avoid potential collisions.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscr...@groovy.apache.org

For queries about this service, please contact Infrastructure at:
us...@infra.apache.org

Reply via email to