This is an automated email from the ASF dual-hosted git repository.
sunlan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/master by this push:
new 1fc8d99837 Avoid finding same variable declaration repeatedly
1fc8d99837 is described below
commit 1fc8d998377039ea2c2e4b1c76e25c092f0cd9d4
Author: Daniel Sun <[email protected]>
AuthorDate: Sun Mar 30 19:11:58 2025 +0900
Avoid finding same variable declaration repeatedly
---
.../groovy/classgen/VariableScopeVisitor.java | 146 +++++++++++++--------
1 file changed, 91 insertions(+), 55 deletions(-)
diff --git
a/src/main/java/org/codehaus/groovy/classgen/VariableScopeVisitor.java
b/src/main/java/org/codehaus/groovy/classgen/VariableScopeVisitor.java
index b54b693966..3ce83726a2 100644
--- a/src/main/java/org/codehaus/groovy/classgen/VariableScopeVisitor.java
+++ b/src/main/java/org/codehaus/groovy/classgen/VariableScopeVisitor.java
@@ -64,8 +64,11 @@ import org.codehaus.groovy.control.SourceUnit;
import org.codehaus.groovy.syntax.Types;
import java.util.Deque;
+import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
+import java.util.Map;
+import java.util.Objects;
import java.util.function.BiConsumer;
import java.util.function.Supplier;
@@ -235,71 +238,77 @@ public class VariableScopeVisitor extends
ClassCodeVisitorSupport {
return null;
}
+ private final Map<VariableCacheKey, Variable> variableCache = new
HashMap<>(64);
private Variable findVariableDeclaration(final String name) {
- if ("super".equals(name) || "this".equals(name)) return null;
-
- Variable variable = null;
- VariableScope scope = currentScope;
- boolean crossingStaticContext = false;
- // try to find a declaration of a variable
- while (true) {
- crossingStaticContext = (crossingStaticContext ||
scope.isInStaticContext());
-
- Variable var = scope.getDeclaredVariable(name);
- if (var != null) {
- variable = var;
- break;
- }
-
- var = scope.getReferencedLocalVariable(name);
- if (var != null) {
- variable = var;
- break;
- }
+ if ("this".equals(name) || "super".equals(name)) return null;
+ return variableCache.computeIfAbsent(new VariableCacheKey(name,
currentScope, inSpecialConstructorCall), k -> {
+ final String variableName = k.name;
+ final VariableScope currentScope = k.scope;
+ final boolean inSpecialConstructorCall =
k.inSpecialConstructorCall;
+
+ Variable variable = null;
+ VariableScope scope = currentScope;
+ boolean crossingStaticContext = false;
+ // try to find a declaration of a variable
+ while (true) {
+ crossingStaticContext = (crossingStaticContext ||
scope.isInStaticContext());
+
+ Variable var = scope.getDeclaredVariable(variableName);
+ if (var != null) {
+ variable = var;
+ break;
+ }
- var = scope.getReferencedClassVariable(name);
- if (var != null) {
- variable = var;
- break;
- }
+ var = scope.getReferencedLocalVariable(variableName);
+ if (var != null) {
+ variable = var;
+ break;
+ }
- ClassNode node = scope.getClassScope();
- if (node != null) {
- Variable member = findClassMember(node, name);
- boolean requireStatic = (crossingStaticContext ||
inSpecialConstructorCall);
- while (member == null && node.getOuterClass() != null &&
!isAnonymous(node)) {
- requireStatic = requireStatic ||
isStatic(node.getModifiers());
- member = findClassMember((node = node.getOuterClass()),
name);
+ var = scope.getReferencedClassVariable(variableName);
+ if (var != null) {
+ variable = var;
+ break;
}
- if (member != null) {
- // prevent a static context (e.g. a static method) from
accessing a non-static member (e.g. a non-static field)
- if (requireStatic ? member.isInStaticContext() :
!node.isScript()) {
- variable = member;
+
+ ClassNode node = scope.getClassScope();
+ if (node != null) {
+ Variable member = findClassMember(node, variableName);
+ boolean requireStatic = (crossingStaticContext ||
inSpecialConstructorCall);
+ while (member == null && node.getOuterClass() != null &&
!isAnonymous(node)) {
+ requireStatic = requireStatic ||
isStatic(node.getModifiers());
+ member = findClassMember((node =
node.getOuterClass()), variableName);
+ }
+ if (member != null) {
+ // prevent a static context (e.g. a static method)
from accessing a non-static member (e.g. a non-static field)
+ if (requireStatic ? member.isInStaticContext() :
!node.isScript()) {
+ variable = member;
+ }
}
- }
- if (!isAnonymous(scope.getClassScope())) break; // GROOVY-5961
+ if (!isAnonymous(scope.getClassScope())) break; //
GROOVY-5961
+ }
+ scope = scope.getParent();
+ }
+ if (variable == null) {
+ variable = new DynamicVariable(variableName,
crossingStaticContext);
}
- scope = scope.getParent();
- }
- if (variable == null) {
- variable = new DynamicVariable(name, crossingStaticContext);
- }
- boolean isClassVariable = (scope.isClassScope() &&
!scope.isReferencedLocalVariable(name))
- || (scope.isReferencedClassVariable(name) &&
scope.getDeclaredVariable(name) == null);
- VariableScope end = scope;
- scope = currentScope;
- while (scope != end) {
- if (isClassVariable) {
- scope.putReferencedClassVariable(variable);
- } else {
- scope.putReferencedLocalVariable(variable);
+ boolean isClassVariable = (scope.isClassScope() &&
!scope.isReferencedLocalVariable(variableName))
+ || (scope.isReferencedClassVariable(variableName) &&
scope.getDeclaredVariable(variableName) == null);
+ VariableScope end = scope;
+ scope = currentScope;
+ while (scope != end) {
+ if (isClassVariable) {
+ scope.putReferencedClassVariable(variable);
+ } else {
+ scope.putReferencedLocalVariable(variable);
+ }
+ scope = scope.getParent();
}
- scope = scope.getParent();
- }
- return variable;
+ return variable;
+ });
}
private void visitTypeVariables(final GenericsType[] types) {
@@ -758,4 +767,31 @@ public class VariableScopeVisitor extends
ClassCodeVisitorSupport {
checkVariableContextAccess(variable, expression);
}
}
+
+ private static class VariableCacheKey {
+ private static final int DEFAULT_HASH = 0;
+ private final String name;
+ private final VariableScope scope;
+ private final boolean inSpecialConstructorCall;
+ private int hash = DEFAULT_HASH;
+
+ VariableCacheKey(final String name, final VariableScope scope, boolean
inSpecialConstructorCall) {
+ this.name = name;
+ this.scope = scope;
+ this.inSpecialConstructorCall = inSpecialConstructorCall;
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) return true;
+ if (!(obj instanceof VariableCacheKey)) return false;
+ VariableCacheKey that = (VariableCacheKey) obj;
+ return name.equals(that.name) && scope.equals(that.scope) &&
inSpecialConstructorCall == that.inSpecialConstructorCall;
+ }
+
+ @Override
+ public int hashCode() {
+ return DEFAULT_HASH != hash ? hash : (hash = Objects.hash(name,
scope, inSpecialConstructorCall));
+ }
+ }
}