You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by su...@apache.org on 2026/08/22 16:28:31 UTC

(groovy) 01/01: GROOVY-12288: Cache ClassWriter getCommonSuperClass lookups per class pair

This is an automated email from the ASF dual-hosted git repository.

asf-gitbox-commits pushed a commit to branch GROOVY-12288
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit 2e0da3d79d22b7a5ad1d25dbaaec47fa26c03402
Author: Daniel Sun <su...@apache.org>
AuthorDate: Sat Aug 15 17:32:22 2026 +0900

    GROOVY-12288: Cache ClassWriter getCommonSuperClass lookups per class pair
---
 .../codehaus/groovy/control/CompilationUnit.java   | 65 +++++++++++++++-------
 1 file changed, 44 insertions(+), 21 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/control/CompilationUnit.java b/src/main/java/org/codehaus/groovy/control/CompilationUnit.java
index 5aad7ece5b..d9d2a3a09a 100644
--- a/src/main/java/org/codehaus/groovy/control/CompilationUnit.java
+++ b/src/main/java/org/codehaus/groovy/control/CompilationUnit.java
@@ -62,6 +62,7 @@ import java.security.CodeSource;
 import java.util.ArrayList;
 import java.util.Comparator;
 import java.util.Deque;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
@@ -905,8 +906,34 @@ public class CompilationUnit extends ProcessingUnit {
      * @return the class visitor used to emit bytecode
      */
     protected ClassVisitor createClassVisitor() {
-        return new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES) {
-            private ClassNode getClassNode(String name) {
+        return new CachingClassWriter();
+    }
+
+    /**
+     * Per-class {@link ClassWriter} that memoizes {@link #getCommonSuperClass}.
+     * {@code COMPUTE_FRAMES} repeats the same internal-name pairs at merge points;
+     * the writer (and its maps) live for one generated class and are then discarded.
+     */
+    private final class CachingClassWriter extends ClassWriter {
+        private final Map<String, ClassNode> classNodeByInternalName = new HashMap<>();
+        private final Map<Set<String>, String> commonSuperByPair = new HashMap<>();
+
+        private CachingClassWriter() {
+            super(COMPUTE_MAXS | COMPUTE_FRAMES);
+        }
+
+        @Override
+        protected String getCommonSuperClass(final String type1, final String type2) {
+            return commonSuperByPair.computeIfAbsent(Set.of(type1, type2), k -> {
+                ClassNode class1 = getClassNode(type1);
+                ClassNode class2 = getClassNode(type2);
+                return getCommonSuperClassNode(class1, class2).getName().replace('.', '/');
+            });
+        }
+
+        private ClassNode getClassNode(final String internalName) {
+            return classNodeByInternalName.computeIfAbsent(internalName, k -> {
+                String name = k.replace('/', '.');
                 // try classes under compilation
                 CompileUnit cu = getAST();
                 ClassNode cn = cu.getClass(name);
@@ -916,25 +943,21 @@ public class CompilationUnit extends ProcessingUnit {
                 if (cn != null) return cn;
                 ClassNodeResolver.LookupResult lookupResult = getClassNodeResolver().resolveName(name, CompilationUnit.this);
                 return lookupResult == null ? null : lookupResult.getClassNode();
-            }
-            private ClassNode getCommonSuperClassNode(ClassNode c, ClassNode d) {
-                // adapted from ClassWriter code
-                if (c.isDerivedFrom(d)) return d;
-                if (d.isDerivedFrom(c)) return c;
-                if (c.isInterface() || d.isInterface()) return ClassHelper.OBJECT_TYPE;
-                do {
-                    c = c.getSuperClass();
-                } while (c != null && !d.isDerivedFrom(c));
-                if (c == null) return ClassHelper.OBJECT_TYPE;
-                return c;
-            }
-            @Override
-            protected String getCommonSuperClass(String arg1, String arg2) {
-                ClassNode a = getClassNode(arg1.replace('/', '.'));
-                ClassNode b = getClassNode(arg2.replace('/', '.'));
-                return getCommonSuperClassNode(a,b).getName().replace('.','/');
-            }
-        };
+            });
+        }
+
+        private ClassNode getCommonSuperClassNode(ClassNode c, ClassNode d) {
+            if (c == null || d == null) throw new GroovyBugError("Unable to determine common super class of " + c + " and " + d);
+
+            // adapted from ClassWriter code
+            if (c.isDerivedFrom(d)) return d;
+            if (d.isDerivedFrom(c)) return c;
+            if (c.isInterface() || d.isInterface()) return ClassHelper.OBJECT_TYPE;
+            do {
+                c = c.getSuperClass();
+            } while (c != null && !d.isDerivedFrom(c));
+            return c == null ? ClassHelper.OBJECT_TYPE : c;
+        }
     }
 
     //---------------------------------------------------------------------------