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 76c7708bcd GROOVY-9742: GroovyClassLoader.parseClass()
StampedCommonCache.getAndPut() hang (provide constructor to enable alternative
sourceCaches to be set)
76c7708bcd is described below
commit 76c7708bcd009ea4e5932a0777c7eb2c03169624
Author: Paul King <[email protected]>
AuthorDate: Sun Apr 13 13:05:10 2025 +1000
GROOVY-9742: GroovyClassLoader.parseClass() StampedCommonCache.getAndPut()
hang (provide constructor to enable alternative sourceCaches to be set)
---
src/main/java/groovy/lang/GroovyClassLoader.java | 19 +++++--
.../bugs/groovy9742/CustomGroovyClassLoader.java | 59 +++++-----------------
...oader.java => DelegatingGroovyClassLoader.java} | 6 +--
src/test/groovy/bugs/groovy9742/Groovy9742.groovy | 16 ++++--
4 files changed, 42 insertions(+), 58 deletions(-)
diff --git a/src/main/java/groovy/lang/GroovyClassLoader.java
b/src/main/java/groovy/lang/GroovyClassLoader.java
index 1c98269766..8fa0f5fc80 100644
--- a/src/main/java/groovy/lang/GroovyClassLoader.java
+++ b/src/main/java/groovy/lang/GroovyClassLoader.java
@@ -100,7 +100,7 @@ public class GroovyClassLoader extends URLClassLoader {
/**
* This cache contains mappings of file name to class. It is used to
bypass compilation.
*/
- protected final FlexibleEvictableCache<String, Class> sourceCache = new
ConcurrentCommonCache<>();
+ protected final FlexibleEvictableCache<String, Class> sourceCache;
private final CompilerConfiguration config;
private final String sourceEncoding;
@@ -140,15 +140,28 @@ public class GroovyClassLoader extends URLClassLoader {
}
/**
- * Creates a GroovyClassLoader.
+ * Creates a GroovyClassLoader with the default sourceCache.
*
* @param parent the parent class loader
* @param config the compiler configuration
* @param useConfigurationClasspath determines if the configurations
classpath should be added
*/
public GroovyClassLoader(final ClassLoader parent, final
CompilerConfiguration config, final boolean useConfigurationClasspath) {
+ this(parent, config, useConfigurationClasspath, new
ConcurrentCommonCache<>());
+ }
+
+ /**
+ * Creates a GroovyClassLoader with a custom sourceCache.
+ *
+ * @param parent the parent class loader
+ * @param config the compiler configuration
+ * @param useConfigurationClasspath determines if the configurations
classpath should be added
+ * @param sourceCache the source cache to use
+ */
+ public GroovyClassLoader(final ClassLoader parent, final
CompilerConfiguration config, final boolean useConfigurationClasspath,
FlexibleEvictableCache<String, Class> sourceCache) {
super(EMPTY_URL_ARRAY, parent);
this.config = (config != null ? config :
CompilerConfiguration.DEFAULT);
+ this.sourceCache = sourceCache;
if (useConfigurationClasspath) {
for (String path : this.config.getClasspath()) {
addClasspath(path);
@@ -233,7 +246,7 @@ public class GroovyClassLoader extends URLClassLoader {
private static void collect(final ClassCollector collector, final
LinkedHashMap<ClassNode, ClassVisitor> generatedClasses) {
// GROOVY-10687: drive ClassCollector after classgen -- interfaces
first
- var classes = new ArrayList<ClassNode>(generatedClasses.keySet());
+ var classes = new ArrayList<>(generatedClasses.keySet());
classes.sort(Comparator.comparingInt(cn -> {
int n;
if (cn.isInterface()) {
diff --git a/src/test/groovy/bugs/groovy9742/CustomGroovyClassLoader.java
b/src/test/groovy/bugs/groovy9742/CustomGroovyClassLoader.java
index 2bc87f7ab1..346941e650 100644
--- a/src/test/groovy/bugs/groovy9742/CustomGroovyClassLoader.java
+++ b/src/test/groovy/bugs/groovy9742/CustomGroovyClassLoader.java
@@ -19,55 +19,20 @@
package groovy.bugs.groovy9742;
import groovy.lang.GroovyClassLoader;
-import java.io.File;
-import java.io.IOException;
-import java.net.URISyntaxException;
+import groovy.lang.GroovyShell;
+import org.codehaus.groovy.runtime.memoize.StampedCommonCache;
-public class CustomGroovyClassLoader extends ClassLoader {
- public int loadedCount = 0;
- public CustomGroovyClassLoader(ClassLoader parent) {
- super(parent);
- groovyClassLoader = new GroovyClassLoader(this);
- }
- private static final File srcDir;
-
- static {
- try {
- srcDir = new
File(CustomGroovyClassLoader.class.getResource("/").toURI());
- } catch (URISyntaxException e) {
- throw new RuntimeException(e);
- }
+/**
+ * A custom groovyClassLoader that users a StampedCommonCache as the
sourceCache
+ */
+public class CustomGroovyClassLoader extends GroovyClassLoader {
+ public CustomGroovyClassLoader() {
+ super(Thread.currentThread().getContextClassLoader(), null, true, new
StampedCommonCache<>());
}
- private final GroovyClassLoader groovyClassLoader;
- @Override
- protected Class<?> loadClass(String name, boolean resolve) throws
ClassNotFoundException {
- synchronized (getClassLoadingLock(name)) {
- Class<?> c = doFindClass(name);
- if (c != null) {
- if (resolve) {
- resolveClass(c);
- }
- return c;
- }
- }
- return super.loadClass(name, resolve);
- }
- private Class<?> doFindClass(String name) {
- File classFile = new File(srcDir, name.replace('.', '/') + ".groovy");
- if (classFile.exists()) {
- try {
-// System.out.println("PARSE\t: " + name);
- Class<?> clz = groovyClassLoader.parseClass(classFile);
- loadedCount++;
-// System.out.println("PARSED\t: " + clz);
- return clz;
- }
- catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
- return null;
+ public Object evaluate(final String scriptText) {
+ GroovyClassLoader gcl = new CustomGroovyClassLoader();
+ GroovyShell gsh = new GroovyShell(gcl);
+ return gsh.evaluate(scriptText);
}
}
-
diff --git a/src/test/groovy/bugs/groovy9742/CustomGroovyClassLoader.java
b/src/test/groovy/bugs/groovy9742/DelegatingGroovyClassLoader.java
similarity index 91%
copy from src/test/groovy/bugs/groovy9742/CustomGroovyClassLoader.java
copy to src/test/groovy/bugs/groovy9742/DelegatingGroovyClassLoader.java
index 2bc87f7ab1..ef6bb9f380 100644
--- a/src/test/groovy/bugs/groovy9742/CustomGroovyClassLoader.java
+++ b/src/test/groovy/bugs/groovy9742/DelegatingGroovyClassLoader.java
@@ -23,9 +23,9 @@ import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
-public class CustomGroovyClassLoader extends ClassLoader {
+public class DelegatingGroovyClassLoader extends ClassLoader {
public int loadedCount = 0;
- public CustomGroovyClassLoader(ClassLoader parent) {
+ public DelegatingGroovyClassLoader(ClassLoader parent) {
super(parent);
groovyClassLoader = new GroovyClassLoader(this);
}
@@ -33,7 +33,7 @@ public class CustomGroovyClassLoader extends ClassLoader {
static {
try {
- srcDir = new
File(CustomGroovyClassLoader.class.getResource("/").toURI());
+ srcDir = new
File(DelegatingGroovyClassLoader.class.getResource("/").toURI());
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
diff --git a/src/test/groovy/bugs/groovy9742/Groovy9742.groovy
b/src/test/groovy/bugs/groovy9742/Groovy9742.groovy
index 6747a0e6ec..e35d21dd6d 100644
--- a/src/test/groovy/bugs/groovy9742/Groovy9742.groovy
+++ b/src/test/groovy/bugs/groovy9742/Groovy9742.groovy
@@ -31,13 +31,19 @@ class Groovy9742 {
void testDeadLock() {
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(1);
Future future = fixedThreadPool.submit((Callable<Class<?>>) () -> {
- CustomGroovyClassLoader ccl = new
CustomGroovyClassLoader(Groovy9742.class.getClassLoader())
- Class<?> clz = ccl.loadClass("groovy.bugs.groovy9742.Foo")
- assert ccl.loadedCount == 2
- return clz
- })
+ DelegatingGroovyClassLoader ccl = new
DelegatingGroovyClassLoader(Groovy9742.class.getClassLoader())
+ Class<?> clz = ccl.loadClass("groovy.bugs.groovy9742.Foo")
+ assert ccl.loadedCount == 2
+ return clz
+ })
Class c = future.get(3000, TimeUnit.MILLISECONDS)
assert c instanceof Class
fixedThreadPool.shutdownNow()
}
+
+ @Test
+ void testCustomGroovyClassLoader() {
+ CustomGroovyClassLoader gcl = new CustomGroovyClassLoader()
+ assert gcl.evaluate('1 + 1') == 2
+ }
}