[
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)