You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by pa...@apache.org on 2026/08/24 01:01:12 UTC

(groovy) branch master updated: GROOVY-12288: Cache ClassWriter getCommonSuperClass lookups per class

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

paulk-asert 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 3190f42b54 GROOVY-12288: Cache ClassWriter getCommonSuperClass lookups per class
3190f42b54 is described below

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

    GROOVY-12288: Cache ClassWriter getCommonSuperClass lookups per class
---
 .../codehaus/groovy/control/CompilationUnit.java   | 100 +++++--
 .../control/ClassWriterCommonSuperClassTest.groovy | 182 ++++++++++++
 .../groovy/perf/ClassWriterBytecodeGenBench.java   | 309 +++++++++++++++++++++
 3 files changed, 566 insertions(+), 25 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..efdab03e58 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,36 +906,85 @@ 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) {
-                // try classes under compilation
-                CompileUnit cu = getAST();
-                ClassNode cn = cu.getClass(name);
-                if (cn != null) return cn;
+        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<>(8);
+        private final Map<String, Map<String, String>> commonSuperByPair = new HashMap<>(8);
+
+        private CachingClassWriter() {
+            super(COMPUTE_MAXS | COMPUTE_FRAMES);
+        }
+
+        @Override
+        protected String getCommonSuperClass(final String type1, final String type2) {
+            Map<String, String> bySecond = commonSuperByPair.get(type1);
+            if (bySecond != null) {
+                String cached = bySecond.get(type2);
+                if (cached != null) {
+                    return cached;
+                }
+            }
+            ClassNode class1 = getClassNode(type1);
+            ClassNode class2 = getClassNode(type2);
+            if (class1 == null || class2 == null) {
+                throw new GroovyBugError("Unable to determine common super class of " + type1 + " and " + type2);
+            }
+            ClassNode commonNode = getCommonSuperClassNode(class1, class2);
+            String common;
+            if (commonNode == class1) {
+                common = type1;
+            } else if (commonNode == class2) {
+                common = type2;
+            } else if (ClassHelper.isObjectType(commonNode)) {
+                common = "java/lang/Object";
+            } else {
+                common = commonNode.getName().replace('.', '/');
+            }
+            commonSuperByPair.computeIfAbsent(type1, k -> new HashMap<>(4)).put(type2, common);
+            commonSuperByPair.computeIfAbsent(type2, k -> new HashMap<>(4)).put(type1, common);
+            return common;
+        }
+
+        private ClassNode getClassNode(final String internalName) {
+            ClassNode cached = classNodeByInternalName.get(internalName);
+            if (cached != null) {
+                return cached;
+            }
+            String name = internalName.replace('/', '.');
+            // try classes under compilation
+            CompileUnit cu = getAST();
+            ClassNode cn = cu.getClass(name);
+            if (cn == null) {
                 // try inner classes
                 cn = cu.getGeneratedInnerClass(name);
-                if (cn != null) return cn;
+            }
+            if (cn == null) {
                 ClassNodeResolver.LookupResult lookupResult = getClassNodeResolver().resolveName(name, CompilationUnit.this);
-                return lookupResult == null ? null : lookupResult.getClassNode();
+                cn = 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;
+            if (cn != null) {
+                classNodeByInternalName.put(internalName, cn);
             }
-            @Override
-            protected String getCommonSuperClass(String arg1, String arg2) {
-                ClassNode a = getClassNode(arg1.replace('/', '.'));
-                ClassNode b = getClassNode(arg2.replace('/', '.'));
-                return getCommonSuperClassNode(a,b).getName().replace('.','/');
-            }
-        };
+            return cn;
+        }
+
+        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));
+            return c == null ? ClassHelper.OBJECT_TYPE : c;
+        }
     }
 
     //---------------------------------------------------------------------------
diff --git a/src/test/groovy/org/codehaus/groovy/control/ClassWriterCommonSuperClassTest.groovy b/src/test/groovy/org/codehaus/groovy/control/ClassWriterCommonSuperClassTest.groovy
new file mode 100644
index 0000000000..27c87ba6e2
--- /dev/null
+++ b/src/test/groovy/org/codehaus/groovy/control/ClassWriterCommonSuperClassTest.groovy
@@ -0,0 +1,182 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.codehaus.groovy.control
+
+import org.codehaus.groovy.tools.GroovyClass
+import org.junit.jupiter.api.Test
+
+import static org.junit.jupiter.api.Assertions.assertEquals
+import static org.junit.jupiter.api.Assertions.assertNotNull
+import static org.junit.jupiter.api.Assertions.assertTrue
+
+/**
+ * Tests for {@link CompilationUnit}'s ClassWriter and getCommonSuperClass lookups (GROOVY-12288).
+ */
+class ClassWriterCommonSuperClassTest {
+
+    @Test
+    void testBasicClassHierarchyMergeAndExecution() {
+        GroovyClassLoader gcl = new GroovyClassLoader()
+        String script = '''
+            package test.pkg
+            
+            class Base {}
+            class ChildA extends Base {}
+            class ChildB extends Base {}
+            
+            class Tester {
+                static Base choose(boolean flag) {
+                    return flag ? new ChildA() : new ChildB()
+                }
+            }
+        '''
+        CompilationUnit cu = new CompilationUnit(CompilerConfiguration.DEFAULT, null, gcl)
+        cu.addSource("test/pkg/Test.groovy", script)
+        cu.compile(Phases.CLASS_GENERATION)
+
+        List<GroovyClass> classes = cu.getClasses()
+        assertNotNull(classes)
+        assertTrue(classes.size() >= 4)
+
+        for (GroovyClass gc : classes) {
+            gcl.defineClass(gc.getName(), gc.getBytes())
+        }
+
+        Class<?> testerClass = gcl.loadClass('test.pkg.Tester')
+        def resA = testerClass.getMethod('choose', boolean.class).invoke(null, true)
+        def resB = testerClass.getMethod('choose', boolean.class).invoke(null, false)
+        assertEquals('test.pkg.ChildA', resA.class.name)
+        assertEquals('test.pkg.ChildB', resB.class.name)
+    }
+
+    @Test
+    void testDeepHierarchyAndMultiCatchExecution() {
+        GroovyClassLoader gcl = new GroovyClassLoader()
+        String script = '''
+            package test.deep
+            
+            class L0 {}
+            class L1 extends L0 {}
+            class L2 extends L1 {}
+            class L3 extends L2 {}
+            class Leaf1 extends L3 {}
+            class Leaf2 extends L3 {}
+            
+            class DeepTester {
+                static Object process(int mode, boolean flag) {
+                    def val = null
+                    try {
+                        if (mode == 1) throw new java.io.FileNotFoundException("fnf")
+                        if (mode == 2) throw new java.io.EOFException("eof")
+                        val = flag ? new Leaf1() : new Leaf2()
+                    } catch (java.io.FileNotFoundException | java.io.EOFException e) {
+                        val = flag ? new java.util.ArrayList() : new java.util.LinkedList()
+                    } catch (Exception e) {
+                        val = flag ? new java.util.HashMap() : new java.util.TreeMap()
+                    }
+                    return val
+                }
+            }
+        '''
+        CompilationUnit cu = new CompilationUnit(CompilerConfiguration.DEFAULT, null, gcl)
+        cu.addSource("test/deep/DeepTest.groovy", script)
+        cu.compile(Phases.CLASS_GENERATION)
+
+        for (GroovyClass gc : cu.getClasses()) {
+            gcl.defineClass(gc.getName(), gc.getBytes())
+        }
+
+        Class<?> testerClass = gcl.loadClass('test.deep.DeepTester')
+        def res0 = testerClass.getMethod('process', int.class, boolean.class).invoke(null, 0, true)
+        def res1 = testerClass.getMethod('process', int.class, boolean.class).invoke(null, 1, false)
+        def res2 = testerClass.getMethod('process', int.class, boolean.class).invoke(null, 2, true)
+
+        assertEquals('test.deep.Leaf1', res0.class.name)
+        assertEquals(java.util.LinkedList.class, res1.class)
+        assertEquals(java.util.ArrayList.class, res2.class)
+    }
+
+    @Test
+    void testStaticCompileWithComplexGenericsAndLoops() {
+        GroovyClassLoader gcl = new GroovyClassLoader()
+        String script = '''
+            package test.sc
+            import groovy.transform.CompileStatic
+            
+            @CompileStatic
+            class StaticTester {
+                static Object mergeComplex(int count, boolean flag) {
+                    Object result = flag ? new java.util.ArrayList<String>() : new java.util.LinkedList<String>()
+                    for (int i = 0; i < count; i++) {
+                        Object inner = (i % 2 == 0) ? (flag ? new java.util.HashSet<Integer>() : new java.util.TreeSet<Integer>())
+                                                    : (flag ? new StringBuilder() : new StringBuffer())
+                        if (i == count - 1) {
+                            result = inner
+                        }
+                    }
+                    return result
+                }
+            }
+        '''
+        CompilationUnit cu = new CompilationUnit(CompilerConfiguration.DEFAULT, null, gcl)
+        cu.addSource("test/sc/StaticTester.groovy", script)
+        cu.compile(Phases.CLASS_GENERATION)
+
+        for (GroovyClass gc : cu.getClasses()) {
+            gcl.defineClass(gc.getName(), gc.getBytes())
+        }
+
+        Class<?> testerClass = gcl.loadClass('test.sc.StaticTester')
+        def res = testerClass.getMethod('mergeComplex', int.class, boolean.class).invoke(null, 4, true)
+        assertEquals(StringBuilder.class, res.class)
+    }
+
+    @Test
+    void testInterfaceAndNestedClosureCompilation() {
+        GroovyClassLoader gcl = new GroovyClassLoader()
+        String script = '''
+            package test.closure
+            
+            interface IntfA {}
+            interface IntfB {}
+            class ImplA implements IntfA {}
+            class ImplB implements IntfB {}
+            
+            class ClosureTester {
+                static List evaluate() {
+                    def list = [1, 2, 3, 4]
+                    def c1 = { int x -> (x % 2 == 0) ? new ImplA() : new ImplB() }
+                    def c2 = { int x -> (x % 2 == 0) ? new java.util.ArrayList() : new java.util.HashSet() }
+                    return list.collect(c1) + list.collect(c2)
+                }
+            }
+        '''
+        CompilationUnit cu = new CompilationUnit(CompilerConfiguration.DEFAULT, null, gcl)
+        cu.addSource("test/closure/ClosureTester.groovy", script)
+        cu.compile(Phases.CLASS_GENERATION)
+
+        for (GroovyClass gc : cu.getClasses()) {
+            gcl.defineClass(gc.getName(), gc.getBytes())
+        }
+
+        Class<?> testerClass = gcl.loadClass('test.closure.ClosureTester')
+        List res = (List) testerClass.getMethod('evaluate').invoke(null)
+        assertEquals(8, res.size())
+    }
+}
diff --git a/subprojects/performance/src/jmh/groovy/org/apache/groovy/perf/ClassWriterBytecodeGenBench.java b/subprojects/performance/src/jmh/groovy/org/apache/groovy/perf/ClassWriterBytecodeGenBench.java
new file mode 100644
index 0000000000..e3a5fe98b9
--- /dev/null
+++ b/subprojects/performance/src/jmh/groovy/org/apache/groovy/perf/ClassWriterBytecodeGenBench.java
@@ -0,0 +1,309 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.perf;
+
+import org.codehaus.groovy.control.CompilationUnit;
+import org.codehaus.groovy.control.CompilerConfiguration;
+import org.codehaus.groovy.control.Phases;
+import org.codehaus.groovy.tools.GroovyClass;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Level;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Warmup;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Benchmark measuring bytecode generation phase ({@link Phases#CLASS_GENERATION}) performance,
+ * specifically targeting ASM {@code ClassWriter(COMPUTE_FRAMES)} and {@code getCommonSuperClass}
+ * lookups at control-flow merge points (GROOVY-12288).
+ */
+@Warmup(iterations = 3, time = 2, timeUnit = TimeUnit.SECONDS)
+@Measurement(iterations = 5, time = 2, timeUnit = TimeUnit.SECONDS)
+@Fork(2)
+@BenchmarkMode(Mode.AverageTime)
+@OutputTimeUnit(TimeUnit.MILLISECONDS)
+@State(Scope.Benchmark)
+public class ClassWriterBytecodeGenBench {
+
+    private static final int CLASS_COUNT = 30;
+
+    @Param({"mergeHeavy", "deepHierarchy", "polymorphicCollections", "staticCompileMerge", "closureHeavy", "exceptionHierarchy", "deeplyNestedBranches", "largeScaleClass"})
+    private String scenario;
+
+    private Map<String, String> corpus;
+
+    @Setup(Level.Trial)
+    public void generateCorpus() {
+        corpus = new LinkedHashMap<>();
+        for (int i = 0; i < CLASS_COUNT; i++) {
+            corpus.put("GeneratedClass" + i, generateSource(i));
+        }
+    }
+
+    @Benchmark
+    public long compileBytecode() {
+        CompilerConfiguration config = new CompilerConfiguration();
+        CompilationUnit unit = new CompilationUnit(config);
+        for (Map.Entry<String, String> source : corpus.entrySet()) {
+            unit.addSource(source.getKey() + ".groovy", source.getValue());
+        }
+        unit.compile(Phases.CLASS_GENERATION);
+        long totalBytes = 0;
+        List<GroovyClass> classes = unit.getClasses();
+        for (GroovyClass gc : classes) {
+            totalBytes += gc.getBytes().length;
+        }
+        return totalBytes;
+    }
+
+    private String generateSource(final int index) {
+        switch (scenario) {
+            case "mergeHeavy":
+                return generateMergeHeavySource(index);
+            case "deepHierarchy":
+                return generateDeepHierarchySource(index);
+            case "polymorphicCollections":
+                return generatePolymorphicCollectionsSource(index);
+            case "staticCompileMerge":
+                return generateStaticCompileMergeSource(index);
+            case "closureHeavy":
+                return generateClosureHeavySource(index);
+            case "exceptionHierarchy":
+                return generateExceptionHierarchySource(index);
+            case "deeplyNestedBranches":
+                return generateDeeplyNestedBranchesSource(index);
+            case "largeScaleClass":
+                return generateLargeScaleClassSource(index);
+            default:
+                throw new IllegalArgumentException("Unknown scenario: " + scenario);
+        }
+    }
+
+    private String generateMergeHeavySource(final int index) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("package pkg").append(index).append("\n\n");
+        sb.append("class BranchMergeClass").append(index).append(" {\n");
+        for (int m = 0; m < 5; m++) {
+            sb.append("    def method").append(m).append("(int x, boolean f1, boolean f2, boolean f3) {\n");
+            sb.append("        def list = f1 ? new java.util.ArrayList() : new java.util.LinkedList()\n");
+            sb.append("        def map = f2 ? new java.util.HashMap() : new java.util.TreeMap()\n");
+            sb.append("        def seq = f3 ? new StringBuilder() : new StringBuffer()\n");
+            sb.append("        for (int i = 0; i < 10; i++) {\n");
+            sb.append("            def item = (i % 2 == 0) ? (f1 ? new java.util.ArrayList() : new java.util.LinkedList()) : (f2 ? new java.util.HashSet() : new java.util.TreeSet())\n");
+            sb.append("            if (x > 5) {\n");
+            sb.append("                def num = f3 ? Integer.valueOf(1) : Double.valueOf(2.0)\n");
+            sb.append("            } else {\n");
+            sb.append("                def s = f1 ? 'hello' : 'world'\n");
+            sb.append("            }\n");
+            sb.append("        }\n");
+            sb.append("        try {\n");
+            sb.append("            def stream = f1 ? new java.io.ByteArrayInputStream(new byte[0]) : new java.io.BufferedInputStream(new java.io.ByteArrayInputStream(new byte[0]))\n");
+            sb.append("            return stream\n");
+            sb.append("        } catch (Exception e) {\n");
+            sb.append("            return f2 ? new java.io.StringReader('a') : new java.io.CharArrayReader(new char[0])\n");
+            sb.append("        }\n");
+            sb.append("    }\n");
+        }
+        sb.append("}\n");
+        return sb.toString();
+    }
+
+    private String generateDeepHierarchySource(final int index) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("package pkg").append(index).append("\n\n");
+        sb.append("class BaseL0_").append(index).append(" {}\n");
+        sb.append("class BaseL1_").append(index).append(" extends BaseL0_").append(index).append(" {}\n");
+        sb.append("class BaseL2_").append(index).append(" extends BaseL1_").append(index).append(" {}\n");
+        sb.append("class BaseL3_").append(index).append(" extends BaseL2_").append(index).append(" {}\n");
+        sb.append("class BaseL4_").append(index).append(" extends BaseL3_").append(index).append(" {}\n");
+        sb.append("class BaseL5_").append(index).append(" extends BaseL4_").append(index).append(" {}\n");
+        sb.append("class LeafA_").append(index).append(" extends BaseL5_").append(index).append(" {}\n");
+        sb.append("class LeafB_").append(index).append(" extends BaseL5_").append(index).append(" {}\n");
+        sb.append("class LeafC_").append(index).append(" extends BaseL5_").append(index).append(" {}\n");
+        sb.append("class LeafD_").append(index).append(" extends BaseL5_").append(index).append(" {}\n");
+        sb.append("class DeepHierarchyClass").append(index).append(" {\n");
+        for (int m = 0; m < 5; m++) {
+            sb.append("    def testHierarchy").append(m).append("(boolean f1, boolean f2, boolean f3) {\n");
+            sb.append("        def a = f1 ? new LeafA_").append(index).append("() : new LeafB_").append(index).append("()\n");
+            sb.append("        def b = f2 ? new LeafC_").append(index).append("() : new LeafD_").append(index).append("()\n");
+            sb.append("        def c = f3 ? a : b\n");
+            sb.append("        for (int i = 0; i < 10; i++) {\n");
+            sb.append("            def d = (i % 2 == 0) ? new LeafA_").append(index).append("() : (i % 3 == 0 ? new LeafC_").append(index).append("() : new LeafD_").append(index).append("())\n");
+            sb.append("        }\n");
+            sb.append("        return c\n");
+            sb.append("    }\n");
+        }
+        sb.append("}\n");
+        return sb.toString();
+    }
+
+    private String generatePolymorphicCollectionsSource(final int index) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("package pkg").append(index).append("\n\n");
+        sb.append("class PolymorphicCollClass").append(index).append(" {\n");
+        for (int m = 0; m < 5; m++) {
+            sb.append("    def mergeCollections").append(m).append("(int mode, boolean flag) {\n");
+            sb.append("        def c1 = flag ? new java.util.ArrayList() : new java.util.Vector()\n");
+            sb.append("        def c2 = flag ? new java.util.HashSet() : new java.util.LinkedHashSet()\n");
+            sb.append("        def c3 = flag ? new java.util.ArrayDeque() : new java.util.LinkedList()\n");
+            sb.append("        def m1 = flag ? new java.util.HashMap() : new java.util.LinkedHashMap()\n");
+            sb.append("        def m2 = flag ? new java.util.TreeMap() : new java.util.concurrent.ConcurrentHashMap()\n");
+            sb.append("        def res = (mode == 0) ? c1 : ((mode == 1) ? c2 : c3)\n");
+            sb.append("        def mapRes = flag ? m1 : m2\n");
+            sb.append("        return [res, mapRes]\n");
+            sb.append("    }\n");
+        }
+        sb.append("}\n");
+        return sb.toString();
+    }
+
+    private String generateStaticCompileMergeSource(final int index) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("package pkg").append(index).append("\n\n");
+        sb.append("@groovy.transform.CompileStatic\n");
+        sb.append("class StaticCompileMergeClass").append(index).append(" {\n");
+        for (int m = 0; m < 5; m++) {
+            sb.append("    Object mergeMethod").append(m).append("(int x, boolean f1, boolean f2) {\n");
+            sb.append("        Object c = f1 ? new java.util.ArrayList<String>() : new java.util.LinkedList<String>()\n");
+            sb.append("        Object m = f2 ? new java.util.HashMap<String, Object>() : new java.util.TreeMap<String, Object>()\n");
+            sb.append("        for (int i = 0; i < 10; i++) {\n");
+            sb.append("            Object tmp = (i % 2 == 0) ? (f1 ? new java.util.HashSet<String>() : new java.util.TreeSet<String>()) : (f2 ? new java.util.ArrayList<String>() : new java.util.Vector<String>())\n");
+            sb.append("        }\n");
+            sb.append("        return f1 ? c : m\n");
+            sb.append("    }\n");
+        }
+        sb.append("}\n");
+        return sb.toString();
+    }
+
+    private String generateClosureHeavySource(final int index) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("package pkg").append(index).append("\n\n");
+        sb.append("class ClosureHeavyClass").append(index).append(" {\n");
+        for (int m = 0; m < 5; m++) {
+            sb.append("    def runClosures").append(m).append("(boolean f1, boolean f2) {\n");
+            sb.append("        def list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\n");
+            sb.append("        def c1 = { int val -> (val % 2 == 0) ? (f1 ? new java.util.ArrayList() : new java.util.LinkedList()) : (f2 ? new java.util.HashSet() : new java.util.TreeSet()) }\n");
+            sb.append("        def c2 = { int val -> (val % 3 == 0) ? (f1 ? new java.util.HashMap() : new java.util.TreeMap()) : (f2 ? new java.util.ArrayDeque() : new java.util.LinkedList()) }\n");
+            sb.append("        def c3 = { int val -> (val > 5) ? (f1 ? new StringBuilder() : new StringBuffer()) : (f2 ? 'foo' : 'bar') }\n");
+            sb.append("        return list.collect(c1) + list.collect(c2) + list.collect(c3)\n");
+        }
+        sb.append("}\n");
+        return sb.toString();
+    }
+
+    private String generateExceptionHierarchySource(final int index) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("package pkg").append(index).append("\n\n");
+        sb.append("class ExceptionHierarchyClass").append(index).append(" {\n");
+        for (int m = 0; m < 5; m++) {
+            sb.append("    def handleErrors").append(m).append("(int code, boolean flag) {\n");
+            sb.append("        def result = null\n");
+            sb.append("        try {\n");
+            sb.append("            if (code == 1) throw new java.io.FileNotFoundException('not found')\n");
+            sb.append("            if (code == 2) throw new java.io.EOFException('eof')\n");
+            sb.append("            if (code == 3) throw new java.net.SocketTimeoutException('timeout')\n");
+            sb.append("            if (code == 4) throw new java.net.ConnectException('connect')\n");
+            sb.append("            if (code == 5) throw new IllegalArgumentException('arg')\n");
+            sb.append("            if (code == 6) throw new IllegalStateException('state')\n");
+            sb.append("            result = flag ? new java.util.ArrayList() : new java.util.LinkedList()\n");
+            sb.append("        } catch (java.io.FileNotFoundException | java.io.EOFException e) {\n");
+            sb.append("            result = flag ? new java.io.ByteArrayInputStream(new byte[0]) : new java.io.BufferedInputStream(new java.io.ByteArrayInputStream(new byte[0]))\n");
+            sb.append("        } catch (java.net.SocketTimeoutException | java.net.ConnectException e) {\n");
+            sb.append("            result = flag ? new java.io.StringReader('error') : new java.io.CharArrayReader(new char[0])\n");
+            sb.append("        } catch (IllegalArgumentException | IllegalStateException e) {\n");
+            sb.append("            result = flag ? new java.util.HashSet() : new java.util.TreeSet()\n");
+            sb.append("        } catch (Exception e) {\n");
+            sb.append("            result = flag ? new java.util.HashMap() : new java.util.TreeMap()\n");
+            sb.append("        } finally {\n");
+            sb.append("            def cleanup = flag ? new StringBuilder('done') : new StringBuffer('done')\n");
+            sb.append("        }\n");
+            sb.append("        return result\n");
+            sb.append("    }\n");
+        }
+        sb.append("}\n");
+        return sb.toString();
+    }
+
+    private String generateDeeplyNestedBranchesSource(final int index) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("package pkg").append(index).append("\n\n");
+        sb.append("class NestedBranchesClass").append(index).append(" {\n");
+        for (int m = 0; m < 5; m++) {
+            sb.append("    def evaluateComplex").append(m).append("(int a, int b, int c, boolean flag) {\n");
+            sb.append("        def res = (a > 0) ? (\n");
+            sb.append("            (b > 0) ? (\n");
+            sb.append("                (c > 0) ? (flag ? new java.util.ArrayList() : new java.util.LinkedList())\n");
+            sb.append("                        : (flag ? new java.util.Vector() : new java.util.ArrayDeque())\n");
+            sb.append("            ) : (\n");
+            sb.append("                (c > 0) ? (flag ? new java.util.HashSet() : new java.util.TreeSet())\n");
+            sb.append("                        : (flag ? new java.util.LinkedHashSet() : new java.util.concurrent.CopyOnWriteArraySet())\n");
+            sb.append("            )\n");
+            sb.append("        ) : (\n");
+            sb.append("            (b > 0) ? (\n");
+            sb.append("                (c > 0) ? (flag ? new java.util.HashMap() : new java.util.TreeMap())\n");
+            sb.append("                        : (flag ? new java.util.LinkedHashMap() : new java.util.concurrent.ConcurrentHashMap())\n");
+            sb.append("            ) : (\n");
+            sb.append("                (c > 0) ? (flag ? new StringBuilder('A') : new StringBuffer('B'))\n");
+            sb.append("                        : (flag ? new java.io.ByteArrayOutputStream() : new java.io.DataOutputStream(new java.io.ByteArrayOutputStream()))\n");
+            sb.append("            )\n");
+            sb.append("        )\n");
+            sb.append("        return res\n");
+            sb.append("    }\n");
+        }
+        sb.append("}\n");
+        return sb.toString();
+    }
+
+    private String generateLargeScaleClassSource(final int index) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("package pkg").append(index).append("\n\n");
+        sb.append("class LargeScaleClass").append(index).append(" {\n");
+        for (int m = 0; m < 25; m++) {
+            sb.append("    def method").append(m).append("(int x, boolean f1, boolean f2, boolean f3) {\n");
+            sb.append("        def list = f1 ? new java.util.ArrayList() : new java.util.LinkedList()\n");
+            sb.append("        def map = f2 ? new java.util.HashMap() : new java.util.TreeMap()\n");
+            sb.append("        def seq = f3 ? new StringBuilder() : new StringBuffer()\n");
+            sb.append("        for (int i = 0; i < 5; i++) {\n");
+            sb.append("            def item = (i % 2 == 0) ? (f1 ? new java.util.ArrayList() : new java.util.LinkedList()) : (f2 ? new java.util.HashSet() : new java.util.TreeSet())\n");
+            sb.append("            if (x > 5) {\n");
+            sb.append("                def num = f3 ? Integer.valueOf(1) : Double.valueOf(2.0)\n");
+            sb.append("            } else {\n");
+            sb.append("                def s = f1 ? 'hello' : 'world'\n");
+            sb.append("            }\n");
+            sb.append("        }\n");
+            sb.append("        return [list, map, seq]\n");
+            sb.append("    }\n");
+        }
+        sb.append("}\n");
+        return sb.toString();
+    }
+}