[ 
https://issues.apache.org/jira/browse/GROOVY-11609?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17943802#comment-17943802
 ] 

ASF GitHub Bot commented on GROOVY-11609:
-----------------------------------------

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.





> Avoid finding same variable/class member repeatedly
> ---------------------------------------------------
>
>                 Key: GROOVY-11609
>                 URL: https://issues.apache.org/jira/browse/GROOVY-11609
>             Project: Groovy
>          Issue Type: Improvement
>            Reporter: Daniel Sun
>            Priority: Major
>
> When I debugged GROOVY-4721, I found {{VariableScopeVisitor}} will try to 
> find same variable/class member again and again, the finding logic is quite 
> complex. Unfortunately, {{VariableScopeVisitor}} is widely used, e.g. 
> {{ResolveVisitor}}, {{JavaStubCompilationUnit}} and 
> {{TraitASTTransformation}}. So it's better to avoid finding same 
> variable/class member repeatedly to gain better performance.
> For example, in the following code, {{a}}, {{i}}, {{j}} will be tried to find 
> many times:
> {code}
> class BubbleSort {
>     public static void bubbleSort(int[] a) {
>         for (int i = 0, n = a.length; i < n - 1; i++) {
>             for (int j = 0; j < n - i - 1; j++) {
>                 if (a[j] > a[j + 1]) {
>                     int temp = a[j]
>                     a[j] = a[j + 1]
>                     a[j + 1] = temp
>                 }
>             }
>         }
>     }
> }
> {code}



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to