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();
+ }
+}