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 6616c35  GROOVY-8962: Provide an option to generate Java stubs in 
memory for better compilation performance (closes #855)
6616c35 is described below

commit 6616c353b264e2c00d729d14983bcca53e6db950
Author: Daniel Sun <sun...@apache.org>
AuthorDate: Mon Jan 21 21:21:11 2019 +0800

    GROOVY-8962: Provide an option to generate Java stubs in memory for better 
compilation performance (closes #855)
---
 .../codehaus/groovy/control/CompilationUnit.java   |  13 +
 .../groovy/control/CompilerConfiguration.java      |  33 +-
 .../tools/javac/JavaAwareCompilationUnit.java      |  17 +-
 .../groovy/tools/javac/JavaStubGenerator.java      |  58 ++-
 .../groovy/tools/javac/JavacJavaCompiler.java      | 112 ++++-
 .../groovy/tools/javac/MemJavaFileObject.java      |  83 ++++
 src/test/groovy/bugs/Groovy8962.groovy             | 326 +++++++++++++++
 .../groovy/control/CompilerConfigurationTest.java  | 460 ++++++++++-----------
 .../TransformsAndCustomClassLoadersTest.groovy     |   3 +-
 9 files changed, 833 insertions(+), 272 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/control/CompilationUnit.java 
b/src/main/java/org/codehaus/groovy/control/CompilationUnit.java
index 5ecc461..765bb9e 100644
--- a/src/main/java/org/codehaus/groovy/control/CompilationUnit.java
+++ b/src/main/java/org/codehaus/groovy/control/CompilationUnit.java
@@ -53,6 +53,7 @@ import org.codehaus.groovy.transform.trait.TraitComposer;
 import org.objectweb.asm.ClassVisitor;
 import org.objectweb.asm.ClassWriter;
 
+import javax.tools.JavaFileObject;
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
@@ -61,10 +62,12 @@ import java.net.URL;
 import java.security.CodeSource;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * The CompilationUnit collects all compilation data as it is generated by the 
compiler system.
@@ -111,6 +114,9 @@ public class CompilationUnit extends ProcessingUnit {
     LinkedList[] phaseOperations;
     LinkedList[] newPhaseOperations;
 
+    private Set<JavaFileObject> javaCompilationUnitSet = new HashSet<>();
+
+
     /**
      * Initializes the CompilationUnit with defaults.
      */
@@ -1170,4 +1176,11 @@ public class CompilationUnit extends ProcessingUnit {
         this.classNodeResolver = classNodeResolver;
     }
 
+    public Set<JavaFileObject> getJavaCompilationUnitSet() {
+        return javaCompilationUnitSet;
+    }
+
+    public void addJavaCompilationUnits(Set<JavaFileObject> 
javaCompilationUnitSet) {
+        this.javaCompilationUnitSet.addAll(javaCompilationUnitSet);
+    }
 }
diff --git 
a/src/main/java/org/codehaus/groovy/control/CompilerConfiguration.java 
b/src/main/java/org/codehaus/groovy/control/CompilerConfiguration.java
index 828bc31..34dc138 100644
--- a/src/main/java/org/codehaus/groovy/control/CompilerConfiguration.java
+++ b/src/main/java/org/codehaus/groovy/control/CompilerConfiguration.java
@@ -55,6 +55,9 @@ public class CompilerConfiguration {
     /** This (<code>"runtimeGroovydoc"</code>) is the Optimization Option 
value for enabling attaching {@link groovy.lang.Groovydoc} annotation*/
     public static final String RUNTIME_GROOVYDOC = "runtimeGroovydoc";
 
+    /** This (<code>"memStub"</code>) is the Optimization Option value for 
enabling generating stubs in memory*/
+    public static final String MEM_STUB = "memStub";
+
     /** This (<code>"1.4"</code>) is the value for targetBytecode to compile 
for a JDK 1.4. **/
     public static final String JDK4 = "1.4";
     /** This (<code>"1.5"</code>) is the value for targetBytecode to compile 
for a JDK 1.5. **/
@@ -276,9 +279,12 @@ public class CompilerConfiguration {
         handleOptimizationOption(options, INVOKEDYNAMIC, "groovy.target.indy");
         handleOptimizationOption(options, GROOVYDOC, 
"groovy.attach.groovydoc");
         handleOptimizationOption(options, RUNTIME_GROOVYDOC, 
"groovy.attach.runtime.groovydoc");
-
         setOptimizationOptions(options);
 
+        Map<String, Object> jointCompilerOptions = new HashMap<>(4);
+        handleJointCompilationOption(jointCompilerOptions, MEM_STUB, 
"groovy.generate.stub.in.memory");
+        setJointCompilationOptions(jointCompilerOptions);
+
         try {
             String groovyAntlr4Opt = getSystemPropertySafe(GROOVY_ANTLR4_OPT);
 
@@ -301,6 +307,16 @@ public class CompilerConfiguration {
         }
     }
 
+    private void handleJointCompilationOption(Map<String, Object> options, 
String optionName, String sysOptionName) {
+        boolean optionEnabled = getBooleanSafe(sysOptionName);
+        if (DEFAULT != null && 
Boolean.TRUE.equals(DEFAULT.getJointCompilationOptions().get(optionName))) {
+            optionEnabled = true;
+        }
+        if (optionEnabled) {
+            options.put(optionName, Boolean.TRUE);
+        }
+    }
+
     /**
      * Copy constructor.  Use this if you have a mostly correct configuration
      * for your compilation but you want to make a some changes 
programatically.
@@ -972,6 +988,21 @@ public class CompilerConfiguration {
         return runtimeGroovydocEnabled;
     }
 
+    /**
+     * Check whether mem stub enabled
+     * @return the result
+     */
+    public boolean isMemStubEnabled() {
+        Object memStubEnabled = 
this.getJointCompilationOptions().get(MEM_STUB);
+
+        if (null == memStubEnabled) {
+            return false;
+        }
+
+        return "true".equals(memStubEnabled.toString());
+    }
+
+
 //       See 
http://groovy.329449.n5.nabble.com/What-the-static-compile-by-default-tt5750118.html
 //           https://issues.apache.org/jira/browse/GROOVY-8543
 //
diff --git 
a/src/main/java/org/codehaus/groovy/tools/javac/JavaAwareCompilationUnit.java 
b/src/main/java/org/codehaus/groovy/tools/javac/JavaAwareCompilationUnit.java
index 5a26b4c..e8486d8 100644
--- 
a/src/main/java/org/codehaus/groovy/tools/javac/JavaAwareCompilationUnit.java
+++ 
b/src/main/java/org/codehaus/groovy/tools/javac/JavaAwareCompilationUnit.java
@@ -48,6 +48,8 @@ public class JavaAwareCompilationUnit extends CompilationUnit 
{
     private JavaCompilerFactory compilerFactory = new JavacCompilerFactory();
     private final File generationGoal;
     private final boolean keepStubs;
+    private final CompilerConfiguration configuration;
+    private final boolean memStubEnabled;
 
     public JavaAwareCompilationUnit(CompilerConfiguration configuration) {
         this(configuration, null, null);
@@ -60,13 +62,16 @@ public class JavaAwareCompilationUnit extends 
CompilationUnit {
     public JavaAwareCompilationUnit(CompilerConfiguration configuration, 
GroovyClassLoader groovyClassLoader,
                                     GroovyClassLoader transformClassLoader) {
         super(configuration, null, groovyClassLoader, transformClassLoader);
-        javaSources = new LinkedList<String>();
+        this.configuration = configuration;
+        this.memStubEnabled = configuration.isMemStubEnabled();
+        this.javaSources = new LinkedList<String>();
         Map options = configuration.getJointCompilationOptions();
-        generationGoal = (File) options.get("stubDir");
+        this.generationGoal = memStubEnabled ? null : (File) 
options.get("stubDir");
         boolean useJava5 = 
CompilerConfiguration.isPostJDK5(configuration.getTargetBytecode());
                String encoding = configuration.getSourceEncoding();
-        stubGenerator = new JavaStubGenerator(generationGoal, false, useJava5, 
encoding);
-        keepStubs = Boolean.TRUE.equals(options.get("keepStubs"));
+
+        this.stubGenerator = new JavaStubGenerator(generationGoal, false, 
useJava5, encoding);
+        this.keepStubs = Boolean.TRUE.equals(options.get("keepStubs"));
 
         addPhaseOperation(new PrimaryClassNodeOperation() {
             public void call(SourceUnit source, GeneratorContext context, 
ClassNode node) throws CompilationFailedException {
@@ -106,6 +111,10 @@ public class JavaAwareCompilationUnit extends 
CompilationUnit {
                 module.setImportsResolved(false);
             }
             try {
+                if (memStubEnabled) {
+                    
this.addJavaCompilationUnits(stubGenerator.getJavaStubCompilationUnitSet()); // 
add java stubs
+                }
+
                 JavaCompiler compiler = 
compilerFactory.createCompiler(getConfiguration());
                 compiler.compile(javaSources, this);
             } finally {
diff --git 
a/src/main/java/org/codehaus/groovy/tools/javac/JavaStubGenerator.java 
b/src/main/java/org/codehaus/groovy/tools/javac/JavaStubGenerator.java
index 64b3ee9..8f4dabd 100644
--- a/src/main/java/org/codehaus/groovy/tools/javac/JavaStubGenerator.java
+++ b/src/main/java/org/codehaus/groovy/tools/javac/JavaStubGenerator.java
@@ -53,6 +53,7 @@ import org.codehaus.groovy.tools.Utilities;
 import org.codehaus.groovy.transform.trait.Traits;
 import org.objectweb.asm.Opcodes;
 
+import javax.tools.JavaFileObject;
 import java.io.BufferedOutputStream;
 import java.io.File;
 import java.io.FileNotFoundException;
@@ -64,9 +65,11 @@ import java.nio.charset.Charset;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 public class JavaStubGenerator {
     private final boolean java5;
@@ -84,7 +87,7 @@ public class JavaStubGenerator {
         this.requireSuperResolved = requireSuperResolved;
         this.java5 = java5;
         this.encoding = encoding;
-        outputPath.mkdirs();
+        if (null != outputPath) outputPath.mkdirs(); // when outputPath is 
null, we generate stubs in memory
     }
 
     public JavaStubGenerator(final File outputPath) {
@@ -98,6 +101,7 @@ public class JavaStubGenerator {
         dir.mkdirs();
     }
 
+
     private static final int DEFAULT_BUFFER_SIZE = 8 * 1024; // 8K
     public void generateClass(ClassNode classNode) throws 
FileNotFoundException {
         // Only attempt to render our self if our super-class is resolved, 
else wait for it
@@ -112,22 +116,40 @@ public class JavaStubGenerator {
         // don't generate stubs for private classes, as they are only visible 
in the same file
         if ((classNode.getModifiers() & Opcodes.ACC_PRIVATE) != 0) return;
 
+
+        if (null == outputPath) {
+            generateMemStub(classNode);
+        } else {
+            generateFileStub(classNode);
+        }
+    }
+
+    private void generateMemStub(ClassNode classNode) {
+        Writer writer = new StringBuilderWriter();
+        generateStubContent(classNode, writer);
+
+        javaStubCompilationUnitSet.add(new MemJavaFileObject(classNode, 
writer.toString()));
+    }
+
+    private void generateFileStub(ClassNode classNode) throws 
FileNotFoundException {
         String fileName = classNode.getName().replace('.', '/');
         mkdirs(outputPath, fileName);
         toCompile.add(fileName);
 
         File file = new File(outputPath, fileName + ".java");
-        Charset charset = Charset.forName(encoding);
-
-        try (PrintWriter out = new PrintWriter(
-                new OutputStreamWriter(
-                        new BufferedOutputStream(
-                                new FileOutputStream(file),
-                                DEFAULT_BUFFER_SIZE
-                        ),
-                        charset
-                )
-        )) {
+
+        Writer writer = new OutputStreamWriter(
+                new BufferedOutputStream(
+                        new FileOutputStream(file),
+                        DEFAULT_BUFFER_SIZE
+                ),
+                Charset.forName(encoding)
+        );
+        generateStubContent(classNode, writer);
+    }
+
+    private void generateStubContent(ClassNode classNode, Writer writer) {
+        try (PrintWriter out = new PrintWriter(writer)) {
             String packageName = classNode.getPackageName();
             if (packageName != null) {
                 out.println("package " + packageName + ";\n");
@@ -135,11 +157,10 @@ public class JavaStubGenerator {
 
             printImports(out, classNode);
             printClassContents(out, classNode);
-
         }
     }
 
-    private void printClassContents(PrintWriter out, ClassNode classNode) 
throws FileNotFoundException {
+    private void printClassContents(PrintWriter out, ClassNode classNode) {
         if (classNode instanceof InnerClassNode && ((InnerClassNode) 
classNode).isAnonymous()) {
             // if it is an anonymous inner class, don't generate the stub code 
for it.
             return;
@@ -988,6 +1009,8 @@ public class JavaStubGenerator {
         for (String path : toCompile) {
             new File(outputPath, path + ".java").delete();
         }
+
+        javaStubCompilationUnitSet.clear();
     }
 
     private static String escapeSpecialChars(String value) {
@@ -998,4 +1021,11 @@ public class JavaStubGenerator {
     private static boolean isInterfaceOrTrait(ClassNode cn) {
         return cn.isInterface() || Traits.isTrait(cn);
     }
+
+
+    private final Set<JavaFileObject> javaStubCompilationUnitSet = new 
HashSet<>();
+
+    public Set<JavaFileObject> getJavaStubCompilationUnitSet() {
+        return javaStubCompilationUnitSet;
+    }
 }
diff --git 
a/src/main/java/org/codehaus/groovy/tools/javac/JavacJavaCompiler.java 
b/src/main/java/org/codehaus/groovy/tools/javac/JavacJavaCompiler.java
index 908858c..c8a64b0 100644
--- a/src/main/java/org/codehaus/groovy/tools/javac/JavacJavaCompiler.java
+++ b/src/main/java/org/codehaus/groovy/tools/javac/JavacJavaCompiler.java
@@ -27,6 +27,9 @@ import org.codehaus.groovy.control.messages.ExceptionMessage;
 import org.codehaus.groovy.control.messages.SimpleMessage;
 import org.codehaus.groovy.runtime.DefaultGroovyMethods;
 
+import javax.tools.JavaFileObject;
+import javax.tools.StandardJavaFileManager;
+import javax.tools.ToolProvider;
 import java.io.File;
 import java.io.PrintWriter;
 import java.lang.reflect.InvocationTargetException;
@@ -34,47 +37,107 @@ import java.lang.reflect.Method;
 import java.net.URISyntaxException;
 import java.net.URL;
 import java.net.URLClassLoader;
+import java.nio.charset.Charset;
 import java.security.AccessController;
 import java.security.CodeSource;
 import java.security.PrivilegedAction;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
 
 public class JavacJavaCompiler implements JavaCompiler {
     private static final String[] EMPTY_STRING_ARRAY = new String[0];
     private final CompilerConfiguration config;
+    private final boolean memStubEnabled;
+    private final Charset charset;
 
     public JavacJavaCompiler(CompilerConfiguration config) {
         this.config = config;
+        this.memStubEnabled = config.isMemStubEnabled();
+        this.charset = Charset.forName(config.getSourceEncoding());
+    }
+
+    private int doCompileWithJavac(CompilationUnit cu, String[] 
javacParameters, StringBuilderWriter javacOutput) throws 
ClassNotFoundException, InvocationTargetException, IllegalAccessException, 
NoSuchMethodException {
+        int javacReturnValue = 0;
+
+        Class javac = findJavac(cu);
+        Method method = null;
+        try {
+            method = javac.getMethod("compile", String[].class, 
PrintWriter.class);
+            PrintWriter writer = new PrintWriter(javacOutput);
+            Object ret = method.invoke(null, javacParameters, writer);
+            javacReturnValue = (Integer) ret;
+        } catch (NoSuchMethodException e) { }
+        if (method == null) {
+            method = javac.getMethod("compile", String[].class);
+            Object ret = method.invoke(null, new Object[]{javacParameters});
+            javacReturnValue = (Integer) ret;
+        }
+
+        return javacReturnValue;
+    }
+
+    private boolean doCompileWithSystemJavaCompiler(CompilationUnit cu, 
List<String> files, String[] javacParameters, StringBuilderWriter javacOutput) {
+        javax.tools.JavaCompiler compiler = 
ToolProvider.getSystemJavaCompiler();
+        StandardJavaFileManager fileManager = 
compiler.getStandardFileManager(null, null, charset);
+
+        final Set<JavaFileObject> compilationUnitSet = 
cu.getJavaCompilationUnitSet(); // java stubs already added
+
+        // add java source files to compile
+        fileManager.getJavaFileObjectsFromFiles(
+                files.stream()
+                        .map(File::new)
+                        .collect(Collectors.toList())
+        ).forEach(compilationUnitSet::add);
+
+        javax.tools.JavaCompiler.CompilationTask compilationTask = 
compiler.getTask(
+                javacOutput,
+                fileManager,
+                null,
+                Arrays.asList(javacParameters),
+                Collections.emptyList(),
+                compilationUnitSet
+        );
+
+        return compilationTask.call();
     }
 
     public void compile(List<String> files, CompilationUnit cu) {
-        String[] javacParameters = makeParameters(files, cu.getClassLoader());
-        StringBuilderWriter javacOutput = null;
+        compile(files, cu, memStubEnabled);
+    }
+
+    private void compile(List<String> files, CompilationUnit cu, boolean 
toCompileStubInMem) {
+        String[] javacParameters = makeParameters(files, cu.getClassLoader(), 
toCompileStubInMem);
+        StringBuilderWriter javacOutput = new StringBuilderWriter();;
         int javacReturnValue = 0;
         try {
-            Class javac = findJavac(cu);
-            Method method = null;
-            try {
-                method = javac.getMethod("compile", String[].class, 
PrintWriter.class);
-                javacOutput = new StringBuilderWriter();
-                PrintWriter writer = new PrintWriter(javacOutput);
-                Object ret = method.invoke(null, javacParameters, writer);
-                javacReturnValue = (Integer) ret;
-            } catch (NoSuchMethodException e) { }
-            if (method == null) {
-                method = javac.getMethod("compile", String[].class);
-                Object ret = method.invoke(null, new 
Object[]{javacParameters});
-                javacReturnValue = (Integer) ret;
+            if (toCompileStubInMem) {
+                try {
+                    boolean successful = doCompileWithSystemJavaCompiler(cu, 
files, javacParameters, javacOutput);
+                    if (!successful) {
+                        javacReturnValue = 1;
+                    }
+                } catch (IllegalArgumentException e) {
+                    javacReturnValue = 2; // any of the options are invalid
+                    cu.getErrorCollector().addFatalError(new 
ExceptionMessage(e, true, cu));
+                }
+            } else {
+                try {
+                    javacReturnValue = doCompileWithJavac(cu, javacParameters, 
javacOutput);
+                } catch (InvocationTargetException ite) {
+                    cu.getErrorCollector().addFatalError(new 
ExceptionMessage((Exception) ite.getCause(), true, cu));
+                }
             }
-        } catch (InvocationTargetException ite) {
-            cu.getErrorCollector().addFatalError(new 
ExceptionMessage((Exception) ite.getCause(), true, cu));
         } catch (Exception e) {
             cu.getErrorCollector().addFatalError(new ExceptionMessage(e, true, 
cu));
         }
+
         if (javacReturnValue != 0) {
             switch (javacReturnValue) {
                 case 1: addJavacError("Compile error during compilation with 
javac.", cu, javacOutput); break;
@@ -101,7 +164,7 @@ public class JavacJavaCompiler implements JavaCompiler {
         cu.getErrorCollector().addFatalError(new SimpleMessage(header, cu));
     }
 
-    private String[] makeParameters(List<String> files, GroovyClassLoader 
parentClassLoader) {
+    private String[] makeParameters(List<String> files, GroovyClassLoader 
parentClassLoader, boolean toCompileStubInMem) {
         Map options = config.getJointCompilationOptions();
         LinkedList<String> paras = new LinkedList<String>();
 
@@ -111,8 +174,11 @@ public class JavacJavaCompiler implements JavaCompiler {
         // defaults
         paras.add("-d");
         paras.add(target.getAbsolutePath());
-        paras.add("-sourcepath");
-        paras.add(((File) options.get("stubDir")).getAbsolutePath());
+
+        if (!toCompileStubInMem) {
+            paras.add("-sourcepath");
+            paras.add(((File) options.get("stubDir")).getAbsolutePath());
+        }
 
         // add flags
         String[] flags = (String[]) options.get("flags");
@@ -170,8 +236,10 @@ public class JavacJavaCompiler implements JavaCompiler {
             paras.add(DefaultGroovyMethods.join((Iterable) paths, 
File.pathSeparator));
         }
 
-        // files to compile
-        paras.addAll(files);
+        if (!toCompileStubInMem) {
+            // files to compile
+            paras.addAll(files);
+        }
 
         return paras.toArray(EMPTY_STRING_ARRAY);
     }
diff --git 
a/src/main/java/org/codehaus/groovy/tools/javac/MemJavaFileObject.java 
b/src/main/java/org/codehaus/groovy/tools/javac/MemJavaFileObject.java
new file mode 100644
index 0000000..ca01472
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/tools/javac/MemJavaFileObject.java
@@ -0,0 +1,83 @@
+/*
+ *  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.tools.javac;
+
+import groovy.lang.GroovyRuntimeException;
+import org.codehaus.groovy.ast.ClassNode;
+
+import javax.tools.JavaFileObject;
+import javax.tools.SimpleJavaFileObject;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Objects;
+
+/**
+ * Represents a Java source file in memory to compile
+ * @since 3.0.0
+ */
+public class MemJavaFileObject extends SimpleJavaFileObject {
+    private final String className;
+    private final String src;
+
+    /**
+     * Construct a MemJavaFileObject instance with given groovy class node and 
stub source code
+     *
+     * @param classNode  the groovy class node
+     * @param src  the stub source code
+     */
+    protected MemJavaFileObject(ClassNode classNode, String src) {
+        super(createURI(classNode), JavaFileObject.Kind.SOURCE);
+        this.className = classNode.getName();
+        this.src = src;
+    }
+
+    private static URI createURI(ClassNode classNode) {
+        try {
+            String packageName = classNode.getPackageName();
+            String className = classNode.getNameWithoutPackage();
+
+            return new URI("string:///" + (null == packageName ? "" : 
(packageName.replace('.', '/') + "/")) + className + ".java");
+        } catch (URISyntaxException e) {
+            throw new GroovyRuntimeException(e);
+        }
+    }
+
+    @Override
+    public CharSequence getCharContent(boolean ignoreEncodingErrors) {
+        return src;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof MemJavaFileObject)) return false;
+        MemJavaFileObject that = (MemJavaFileObject) o;
+        return Objects.equals(className, that.className);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(className);
+    }
+
+    @Override
+    public String toString() {
+        return className;
+    }
+}
diff --git a/src/test/groovy/bugs/Groovy8962.groovy 
b/src/test/groovy/bugs/Groovy8962.groovy
new file mode 100644
index 0000000..1756dfa
--- /dev/null
+++ b/src/test/groovy/bugs/Groovy8962.groovy
@@ -0,0 +1,326 @@
+/*
+ *  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 groovy.bugs
+
+import junit.framework.TestCase
+import org.codehaus.groovy.control.CompilerConfiguration
+import org.codehaus.groovy.tools.javac.JavaAwareCompilationUnit
+
+class Groovy8962 extends GroovyTestCase {
+    void testShouldCompileProperly_memStub() {
+        def config = new CompilerConfiguration()
+        config.with {
+            targetDirectory = createTempDir()
+            jointCompilationOptions = [memStub: true]
+        }
+
+        File parentDir = createTempDir()
+        try {
+            def a = new File(parentDir, 'A.groovy')
+            a.write '''
+            class A {
+                Map<String, Map<String, Integer[]>> columnsMap = [:]
+            }
+            '''
+
+            def b = new File(parentDir, 'B.java')
+            b.write '''
+            public class B {
+                public void f(A a) {
+                    System.out.println(a.getColumnsMap());
+                }
+            }
+        '''
+            def loader = new GroovyClassLoader(this.class.classLoader)
+            def cu = new JavaAwareCompilationUnit(config, loader)
+            cu.addSources([a, b] as File[])
+            cu.compile()
+        } finally {
+            parentDir.deleteDir()
+            config.targetDirectory?.deleteDir()
+        }
+
+    }
+
+    void testShouldCompileProperly2_memStub() {
+        def config = new CompilerConfiguration()
+        config.with {
+            targetDirectory = createTempDir()
+            jointCompilationOptions = [memStub: true]
+        }
+
+        File parentDir = createTempDir()
+        try {
+            def a = new File(parentDir, 'A.groovy')
+            a.write '''
+            class A {
+                Map<String, Map<String, List<Integer[]>[]>> columnsMap = [:]
+            }
+            '''
+
+            def b = new File(parentDir, 'B.java')
+            b.write '''
+            public class B {
+                public void f(A a) {
+                    System.out.println(a.getColumnsMap());
+                }
+            }
+        '''
+            def loader = new GroovyClassLoader(this.class.classLoader)
+            def cu = new JavaAwareCompilationUnit(config, loader)
+            cu.addSources([a, b] as File[])
+            cu.compile()
+        } finally {
+            parentDir.deleteDir()
+            config.targetDirectory?.deleteDir()
+        }
+
+    }
+
+    void testShouldCompileProperly3_memStub() {
+        def config = new CompilerConfiguration()
+        config.with {
+            targetDirectory = createTempDir()
+            jointCompilationOptions = [memStub: true]
+        }
+
+        File parentDir = createTempDir()
+        try {
+            def a = new File(parentDir, 'A.groovy')
+            a.write '''
+            package x
+            import y.B
+            class A {
+                Map<String, Map<String, List<Integer[]>[]>> columnsMap = [:]
+                B b = null
+            }
+            '''
+
+            def b = new File(parentDir, 'B.java')
+            b.write '''
+            package y;
+            import x.A;
+            public class B {
+                public void f(A a) {
+                    System.out.println(a.getColumnsMap());
+                }
+            }
+        '''
+            def loader = new GroovyClassLoader(this.class.classLoader)
+            def cu = new JavaAwareCompilationUnit(config, loader)
+            cu.addSources([a, b] as File[])
+            cu.compile()
+        } finally {
+            parentDir.deleteDir()
+            config.targetDirectory?.deleteDir()
+        }
+
+    }
+
+    void testShouldAllowConstantInSwitch_memStub() {
+        def config = new CompilerConfiguration()
+        config.with {
+            targetDirectory = createTempDir()
+            jointCompilationOptions = [memStub: true]
+        }
+
+        File parentDir = createTempDir()
+        try {
+            def b = new File(parentDir, 'B.java')
+            b.write '''
+            public class B {
+                public static void main(String...args) {
+                    int x = 4;
+                    switch (x) {
+                        case groovy.bugs.Groovy8962.Constants.constant: x=1;
+                    }
+                }
+            }
+        '''
+            def loader = new GroovyClassLoader(this.class.classLoader)
+            addToClassPath(loader)
+            def cu = new JavaAwareCompilationUnit(config, loader)
+            cu.addSources([b] as File[])
+            cu.compile()
+        } finally {
+            parentDir.deleteDir()
+            config.targetDirectory.deleteDir()
+        }
+    }
+
+    void testShouldAllowConstantInSwitchWithStubs_memStub() {
+        def config = new CompilerConfiguration()
+        config.with {
+            targetDirectory = createTempDir()
+            jointCompilationOptions = [memStub: true]
+        }
+
+        File parentDir = createTempDir()
+        try {
+            def a = new File(parentDir, 'A.groovy')
+            a.write '''
+                class A {
+                    public static final int constant = 1
+                }
+            '''
+            def b = new File(parentDir, 'B.java')
+            b.write '''
+            public class B {
+                public static void main(String...args) {
+                    int x = 4;
+                    switch (x) {
+                        case A.constant: x=1;
+                    }
+                }
+            }
+        '''
+            def loader = new GroovyClassLoader(this.class.classLoader)
+            addToClassPath(loader)
+            def cu = new JavaAwareCompilationUnit(config, loader)
+            cu.addSources([a, b] as File[])
+            cu.compile()
+        } finally {
+            parentDir.deleteDir()
+            config.targetDirectory.deleteDir()
+        }
+    }
+
+    static addToClassPath(GroovyClassLoader loader) {
+        loader.addURL(this.getProtectionDomain().getCodeSource().getLocation())
+        
loader.addURL(GroovyTestCase.class.getProtectionDomain().getCodeSource().getLocation())
+        
loader.addURL(TestCase.class.getProtectionDomain().getCodeSource().getLocation())
+    }
+
+    void testShouldAllowCharConstantInSwitchWithoutStubs_memStub() {
+        def config = new CompilerConfiguration()
+        config.with {
+            targetDirectory = createTempDir()
+            jointCompilationOptions = [memStub: true]
+        }
+
+        File parentDir = createTempDir()
+        try {
+            def b = new File(parentDir, 'B.java')
+            b.write '''
+            public class B {
+                public static void main(String...args) {
+                    char x = 'z';
+                    switch (x) {
+                        case groovy.bugs.Groovy8962.Constants.FOOCHAR: x='y';
+                    }
+                }
+            }
+        '''
+            def loader = new GroovyClassLoader(this.class.classLoader)
+            config.setClasspathList([getClasspathElement(this.class), 
getClasspathElement(GroovyTestCase), getClasspathElement(TestCase)])
+            def cu = new JavaAwareCompilationUnit(config, loader)
+            cu.addSources([b] as File[])
+            cu.compile()
+        } finally {
+            parentDir.deleteDir()
+            config.targetDirectory.deleteDir()
+        }
+    }
+
+    private static getClasspathElement(Class c) {
+        def codeSource = c.protectionDomain.codeSource
+        def file = new File(codeSource.getLocation().toURI()).getPath()
+        return file.toString()
+    }
+
+    void testShouldAllowCharConstantInSwitchWithStubs_memStub() {
+        def config = new CompilerConfiguration()
+        config.with {
+            targetDirectory = createTempDir()
+            jointCompilationOptions = [memStub: true]
+        }
+
+        File parentDir = createTempDir()
+        try {
+            def a = new File(parentDir, 'A.groovy')
+            a.write '''
+                class A {
+                    public static final char constant = 'x'
+                }
+            '''
+            def b = new File(parentDir, 'B.java')
+            b.write '''
+            public class B {
+                public static void main(String...args) {
+                    char x = 'z';
+                    switch (x) {
+                        case A.constant: x='y';
+                    }
+                }
+            }
+        '''
+            def loader = new GroovyClassLoader(this.class.classLoader)
+            def cu = new JavaAwareCompilationUnit(config, loader)
+            cu.addSources([a, b] as File[])
+            cu.compile()
+        } finally {
+            parentDir.deleteDir()
+            config.targetDirectory.deleteDir()
+        }
+    }
+
+    void testAccessConstantStringFromJavaClass_memStub() {
+        def config = new CompilerConfiguration()
+        config.with {
+            targetDirectory = createTempDir()
+            jointCompilationOptions = [memStub: true]
+        }
+
+        File parentDir = createTempDir()
+        try {
+            def a = new File(parentDir, 'A.groovy')
+            a.write '''
+                class A {
+                    public static final String CONSTANT = "hello, world!"
+                }
+            '''
+            def b = new File(parentDir, 'B.java')
+            b.write '''
+            public class B {
+                public static void main(String...args) {
+                    if (!"hello, world!".equals(A.CONSTANT)) throw new 
RuntimeException("Constant should not be: ["+A.CONSTANT+"]");
+                }
+            }
+        '''
+            def loader = new GroovyClassLoader(this.class.classLoader)
+            def cu = new JavaAwareCompilationUnit(config, loader)
+            cu.addSources([a, b] as File[])
+            cu.compile()
+            Class clazz = loader.loadClass("B")
+            clazz.newInstance().main()
+        } finally {
+            parentDir.deleteDir()
+            config.targetDirectory.deleteDir()
+        }
+    }
+
+    private static File createTempDir() {
+        File.createTempDir("groovyTest${System.currentTimeMillis()}", "")
+    }
+
+    static class Constants {
+        public static final int constant = 2
+        public static final char FOOCHAR = 'x'
+    }
+}
diff --git 
a/src/test/org/codehaus/groovy/control/CompilerConfigurationTest.java 
b/src/test/org/codehaus/groovy/control/CompilerConfigurationTest.java
index d1f7632..50816ab 100644
--- a/src/test/org/codehaus/groovy/control/CompilerConfigurationTest.java
+++ b/src/test/org/codehaus/groovy/control/CompilerConfigurationTest.java
@@ -1,230 +1,230 @@
-/*
- *  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 groovy.util.GroovyTestCase;
-import org.codehaus.groovy.control.messages.WarningMessage;
-
-import java.io.File;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Properties;
-
-/**
- * Make sure CompilerConfiguration works.
- */
-public class CompilerConfigurationTest extends GroovyTestCase {
-    Properties savedProperties;
-
-    // Use setUp/tearDown to avoid mucking with system properties for other 
tests...
-
-    public void setUp() {
-        savedProperties = System.getProperties();
-        System.setProperties(new Properties(savedProperties));
-    }
-
-    public void tearDown() {
-        System.setProperties(savedProperties);
-    }
-
-    public void testDefaultConstructor() {
-        final CompilerConfiguration config = CompilerConfiguration.DEFAULT;
-
-        assertEquals(WarningMessage.LIKELY_ERRORS, config.getWarningLevel());
-        assertEquals(Boolean.getBoolean("groovy.output.debug"), 
config.getDebug());
-        assertEquals(Boolean.getBoolean("groovy.output.verbose"), 
config.getVerbose());
-        assertEquals(false, config.getDebug());
-        assertEquals(false, config.getVerbose());
-        assertEquals(10, config.getTolerance());
-        assertEquals(100, config.getMinimumRecompilationInterval());
-        assertNull(config.getScriptBaseClass());
-        assertEquals(getSystemEncoding(), config.getSourceEncoding());
-        assertEquals(getVMVersion(), config.getTargetBytecode());
-        assertEquals(false, config.getRecompileGroovySource());
-        {
-            final List listCP = config.getClasspath();
-            assertNotNull(listCP);
-            assertEquals(0, listCP.size());
-        }
-        assertNull(config.getTargetDirectory());
-        assertEquals(".groovy", config.getDefaultScriptExtension());
-        assertNull(config.getJointCompilationOptions());
-        assertNotNull(config.getPluginFactory());
-    }
-
-    private String getSystemEncoding() {
-        return System.getProperty("file.encoding", 
CompilerConfiguration.DEFAULT_SOURCE_ENCODING);
-    }
-
-    private static String getVMVersion() {
-        return CompilerConfiguration.JDK8;
-    }
-
-    public void testSetViaSystemProperties() {
-        System.setProperty("groovy.warnings", "PaRaNoiA");
-        System.setProperty("groovy.output.verbose", "trUE");
-        System.setProperty("groovy.recompile.minimumInterval", "867892345");
-
-        assertEquals("PaRaNoiA", System.getProperty("groovy.warnings"));
-
-        final CompilerConfiguration config = new 
CompilerConfiguration(System.getProperties());
-
-        assertEquals(WarningMessage.PARANOIA, config.getWarningLevel());
-        assertEquals(false, config.getDebug());
-        assertEquals(true, config.getVerbose());
-        assertEquals(10, config.getTolerance());
-        assertEquals(867892345, config.getMinimumRecompilationInterval());
-        assertNull(config.getScriptBaseClass());
-        assertEquals(getSystemEncoding(), config.getSourceEncoding());
-        assertEquals(getVMVersion(), config.getTargetBytecode());
-        assertEquals(false, config.getRecompileGroovySource());
-        {
-            final List listCP = config.getClasspath();
-            assertNotNull(listCP);
-            assertEquals(0, listCP.size());
-        }
-        assertNull(config.getTargetDirectory());
-        assertEquals(".groovy", config.getDefaultScriptExtension());
-        assertNull(config.getJointCompilationOptions());
-        assertNotNull(config.getPluginFactory());
-    }
-
-    public void testCopyConstructor1() {
-        final CompilerConfiguration init = new CompilerConfiguration();
-
-        init.setWarningLevel(WarningMessage.POSSIBLE_ERRORS);
-        init.setDebug(true);
-        init.setParameters(true);
-        init.setVerbose(false);
-        init.setTolerance(720);
-        init.setMinimumRecompilationInterval(234);
-        init.setScriptBaseClass("blarg.foo.WhatSit");
-        init.setSourceEncoding("LEAD-123");
-        init.setTargetBytecode(CompilerConfiguration.POST_JDK5);
-        init.setRecompileGroovySource(true);
-        init.setClasspath("File1" + File.pathSeparator + "Somewhere");
-
-        final File initTDFile = new File("A wandering path");
-        init.setTargetDirectory(initTDFile);
-        init.setDefaultScriptExtension(".jpp");
-
-        final Map initJoint = new HashMap();
-        initJoint.put("somekey", "somevalue");
-        init.setJointCompilationOptions(initJoint);
-
-        final ParserPluginFactory initPPF = ParserPluginFactory.newInstance();
-        init.setPluginFactory(initPPF);
-
-        assertEquals(WarningMessage.POSSIBLE_ERRORS, init.getWarningLevel());
-        assertEquals(true, init.getDebug());
-        assertEquals(true, init.getParameters());
-        assertEquals(false, init.getVerbose());
-        assertEquals(720, init.getTolerance());
-        assertEquals(234, init.getMinimumRecompilationInterval());
-        assertEquals("blarg.foo.WhatSit", init.getScriptBaseClass());
-        assertEquals("LEAD-123", init.getSourceEncoding());
-        assertEquals(CompilerConfiguration.POST_JDK5, 
init.getTargetBytecode());
-        assertEquals(true, init.getRecompileGroovySource());
-        {
-            final List listCP = init.getClasspath();
-            assertEquals("File1", listCP.get(0));
-            assertEquals("Somewhere", listCP.get(1));
-        }
-        assertEquals(initTDFile, init.getTargetDirectory());
-        assertEquals(".jpp", init.getDefaultScriptExtension());
-        assertEquals(initJoint, init.getJointCompilationOptions());
-        assertEquals(initPPF, init.getPluginFactory());
-
-        final CompilerConfiguration config = new CompilerConfiguration(init);
-
-        assertEquals(WarningMessage.POSSIBLE_ERRORS, config.getWarningLevel());
-        assertEquals(true, config.getDebug());
-        assertEquals(false, config.getVerbose());
-        assertEquals(720, config.getTolerance());
-        assertEquals(234, config.getMinimumRecompilationInterval());
-        assertEquals("blarg.foo.WhatSit", config.getScriptBaseClass());
-        assertEquals("LEAD-123", config.getSourceEncoding());
-        assertEquals(CompilerConfiguration.POST_JDK5, 
config.getTargetBytecode());
-        assertEquals(true, config.getRecompileGroovySource());
-        {
-            final List listCP = config.getClasspath();
-            assertEquals("File1", listCP.get(0));
-            assertEquals("Somewhere", listCP.get(1));
-        }
-        assertEquals(initTDFile, config.getTargetDirectory());
-        assertEquals(".jpp", config.getDefaultScriptExtension());
-        assertEquals(initJoint, config.getJointCompilationOptions());
-        assertEquals(initPPF, config.getPluginFactory());
-
-    }
-
-    public void testCopyConstructor2() {
-        final CompilerConfiguration init = new CompilerConfiguration();
-
-        init.setWarningLevel(WarningMessage.POSSIBLE_ERRORS);
-        init.setDebug(false);
-        init.setParameters(false);
-        init.setVerbose(true);
-        init.setTolerance(55);
-        init.setMinimumRecompilationInterval(975);
-        init.setScriptBaseClass("");
-        init.setSourceEncoding("Gutenberg");
-        init.setTargetBytecode(CompilerConfiguration.PRE_JDK5);
-        init.setRecompileGroovySource(false);
-        init.setClasspath("");
-
-        final File initTDFile = new File("A wandering path");
-        init.setTargetDirectory(initTDFile);
-
-        assertEquals(WarningMessage.POSSIBLE_ERRORS, init.getWarningLevel());
-        assertEquals(false, init.getDebug());
-        assertEquals(false, init.getParameters());
-        assertEquals(true, init.getVerbose());
-        assertEquals(55, init.getTolerance());
-        assertEquals(975, init.getMinimumRecompilationInterval());
-        assertEquals("", init.getScriptBaseClass());
-        assertEquals("Gutenberg", init.getSourceEncoding());
-        assertEquals(CompilerConfiguration.PRE_JDK5, init.getTargetBytecode());
-        assertEquals(false, init.getRecompileGroovySource());
-        {
-            final List listCP = init.getClasspath();
-            assertNotNull(listCP);
-            assertEquals(0, listCP.size());
-        }
-        assertEquals(initTDFile, init.getTargetDirectory());
-
-        final CompilerConfiguration config = new CompilerConfiguration(init);
-
-        assertEquals(WarningMessage.POSSIBLE_ERRORS, config.getWarningLevel());
-        assertEquals(false, config.getDebug());
-        assertEquals(true, config.getVerbose());
-        assertEquals(55, config.getTolerance());
-        assertEquals(975, config.getMinimumRecompilationInterval());
-        assertEquals("", config.getScriptBaseClass());
-        assertEquals("Gutenberg", config.getSourceEncoding());
-        assertEquals(CompilerConfiguration.PRE_JDK5, 
config.getTargetBytecode());
-        assertEquals(false, config.getRecompileGroovySource());
-        {
-            final List listCP = config.getClasspath();
-            assertEquals(0, listCP.size());
-        }
-        assertEquals(initTDFile, config.getTargetDirectory());
-    }
-}
+/*
+ *  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 groovy.util.GroovyTestCase;
+import org.codehaus.groovy.control.messages.WarningMessage;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * Make sure CompilerConfiguration works.
+ */
+public class CompilerConfigurationTest extends GroovyTestCase {
+    Properties savedProperties;
+
+    // Use setUp/tearDown to avoid mucking with system properties for other 
tests...
+
+    public void setUp() {
+        savedProperties = System.getProperties();
+        System.setProperties(new Properties(savedProperties));
+    }
+
+    public void tearDown() {
+        System.setProperties(savedProperties);
+    }
+
+    public void testDefaultConstructor() {
+        final CompilerConfiguration config = CompilerConfiguration.DEFAULT;
+
+        assertEquals(WarningMessage.LIKELY_ERRORS, config.getWarningLevel());
+        assertEquals(Boolean.getBoolean("groovy.output.debug"), 
config.getDebug());
+        assertEquals(Boolean.getBoolean("groovy.output.verbose"), 
config.getVerbose());
+        assertEquals(false, config.getDebug());
+        assertEquals(false, config.getVerbose());
+        assertEquals(10, config.getTolerance());
+        assertEquals(100, config.getMinimumRecompilationInterval());
+        assertNull(config.getScriptBaseClass());
+        assertEquals(getSystemEncoding(), config.getSourceEncoding());
+        assertEquals(getVMVersion(), config.getTargetBytecode());
+        assertEquals(false, config.getRecompileGroovySource());
+        {
+            final List listCP = config.getClasspath();
+            assertNotNull(listCP);
+            assertEquals(0, listCP.size());
+        }
+        assertNull(config.getTargetDirectory());
+        assertEquals(".groovy", config.getDefaultScriptExtension());
+        assertTrue(0 == config.getJointCompilationOptions().size());
+        assertNotNull(config.getPluginFactory());
+    }
+
+    private String getSystemEncoding() {
+        return System.getProperty("file.encoding", 
CompilerConfiguration.DEFAULT_SOURCE_ENCODING);
+    }
+
+    private static String getVMVersion() {
+        return CompilerConfiguration.JDK8;
+    }
+
+    public void testSetViaSystemProperties() {
+        System.setProperty("groovy.warnings", "PaRaNoiA");
+        System.setProperty("groovy.output.verbose", "trUE");
+        System.setProperty("groovy.recompile.minimumInterval", "867892345");
+
+        assertEquals("PaRaNoiA", System.getProperty("groovy.warnings"));
+
+        final CompilerConfiguration config = new 
CompilerConfiguration(System.getProperties());
+
+        assertEquals(WarningMessage.PARANOIA, config.getWarningLevel());
+        assertEquals(false, config.getDebug());
+        assertEquals(true, config.getVerbose());
+        assertEquals(10, config.getTolerance());
+        assertEquals(867892345, config.getMinimumRecompilationInterval());
+        assertNull(config.getScriptBaseClass());
+        assertEquals(getSystemEncoding(), config.getSourceEncoding());
+        assertEquals(getVMVersion(), config.getTargetBytecode());
+        assertEquals(false, config.getRecompileGroovySource());
+        {
+            final List listCP = config.getClasspath();
+            assertNotNull(listCP);
+            assertEquals(0, listCP.size());
+        }
+        assertNull(config.getTargetDirectory());
+        assertEquals(".groovy", config.getDefaultScriptExtension());
+        assertTrue(0 == config.getJointCompilationOptions().size());
+        assertNotNull(config.getPluginFactory());
+    }
+
+    public void testCopyConstructor1() {
+        final CompilerConfiguration init = new CompilerConfiguration();
+
+        init.setWarningLevel(WarningMessage.POSSIBLE_ERRORS);
+        init.setDebug(true);
+        init.setParameters(true);
+        init.setVerbose(false);
+        init.setTolerance(720);
+        init.setMinimumRecompilationInterval(234);
+        init.setScriptBaseClass("blarg.foo.WhatSit");
+        init.setSourceEncoding("LEAD-123");
+        init.setTargetBytecode(CompilerConfiguration.POST_JDK5);
+        init.setRecompileGroovySource(true);
+        init.setClasspath("File1" + File.pathSeparator + "Somewhere");
+
+        final File initTDFile = new File("A wandering path");
+        init.setTargetDirectory(initTDFile);
+        init.setDefaultScriptExtension(".jpp");
+
+        final Map initJoint = new HashMap();
+        initJoint.put("somekey", "somevalue");
+        init.setJointCompilationOptions(initJoint);
+
+        final ParserPluginFactory initPPF = ParserPluginFactory.newInstance();
+        init.setPluginFactory(initPPF);
+
+        assertEquals(WarningMessage.POSSIBLE_ERRORS, init.getWarningLevel());
+        assertEquals(true, init.getDebug());
+        assertEquals(true, init.getParameters());
+        assertEquals(false, init.getVerbose());
+        assertEquals(720, init.getTolerance());
+        assertEquals(234, init.getMinimumRecompilationInterval());
+        assertEquals("blarg.foo.WhatSit", init.getScriptBaseClass());
+        assertEquals("LEAD-123", init.getSourceEncoding());
+        assertEquals(CompilerConfiguration.POST_JDK5, 
init.getTargetBytecode());
+        assertEquals(true, init.getRecompileGroovySource());
+        {
+            final List listCP = init.getClasspath();
+            assertEquals("File1", listCP.get(0));
+            assertEquals("Somewhere", listCP.get(1));
+        }
+        assertEquals(initTDFile, init.getTargetDirectory());
+        assertEquals(".jpp", init.getDefaultScriptExtension());
+        assertEquals(initJoint, init.getJointCompilationOptions());
+        assertEquals(initPPF, init.getPluginFactory());
+
+        final CompilerConfiguration config = new CompilerConfiguration(init);
+
+        assertEquals(WarningMessage.POSSIBLE_ERRORS, config.getWarningLevel());
+        assertEquals(true, config.getDebug());
+        assertEquals(false, config.getVerbose());
+        assertEquals(720, config.getTolerance());
+        assertEquals(234, config.getMinimumRecompilationInterval());
+        assertEquals("blarg.foo.WhatSit", config.getScriptBaseClass());
+        assertEquals("LEAD-123", config.getSourceEncoding());
+        assertEquals(CompilerConfiguration.POST_JDK5, 
config.getTargetBytecode());
+        assertEquals(true, config.getRecompileGroovySource());
+        {
+            final List listCP = config.getClasspath();
+            assertEquals("File1", listCP.get(0));
+            assertEquals("Somewhere", listCP.get(1));
+        }
+        assertEquals(initTDFile, config.getTargetDirectory());
+        assertEquals(".jpp", config.getDefaultScriptExtension());
+        assertEquals(initJoint, config.getJointCompilationOptions());
+        assertEquals(initPPF, config.getPluginFactory());
+
+    }
+
+    public void testCopyConstructor2() {
+        final CompilerConfiguration init = new CompilerConfiguration();
+
+        init.setWarningLevel(WarningMessage.POSSIBLE_ERRORS);
+        init.setDebug(false);
+        init.setParameters(false);
+        init.setVerbose(true);
+        init.setTolerance(55);
+        init.setMinimumRecompilationInterval(975);
+        init.setScriptBaseClass("");
+        init.setSourceEncoding("Gutenberg");
+        init.setTargetBytecode(CompilerConfiguration.PRE_JDK5);
+        init.setRecompileGroovySource(false);
+        init.setClasspath("");
+
+        final File initTDFile = new File("A wandering path");
+        init.setTargetDirectory(initTDFile);
+
+        assertEquals(WarningMessage.POSSIBLE_ERRORS, init.getWarningLevel());
+        assertEquals(false, init.getDebug());
+        assertEquals(false, init.getParameters());
+        assertEquals(true, init.getVerbose());
+        assertEquals(55, init.getTolerance());
+        assertEquals(975, init.getMinimumRecompilationInterval());
+        assertEquals("", init.getScriptBaseClass());
+        assertEquals("Gutenberg", init.getSourceEncoding());
+        assertEquals(CompilerConfiguration.PRE_JDK5, init.getTargetBytecode());
+        assertEquals(false, init.getRecompileGroovySource());
+        {
+            final List listCP = init.getClasspath();
+            assertNotNull(listCP);
+            assertEquals(0, listCP.size());
+        }
+        assertEquals(initTDFile, init.getTargetDirectory());
+
+        final CompilerConfiguration config = new CompilerConfiguration(init);
+
+        assertEquals(WarningMessage.POSSIBLE_ERRORS, config.getWarningLevel());
+        assertEquals(false, config.getDebug());
+        assertEquals(true, config.getVerbose());
+        assertEquals(55, config.getTolerance());
+        assertEquals(975, config.getMinimumRecompilationInterval());
+        assertEquals("", config.getScriptBaseClass());
+        assertEquals("Gutenberg", config.getSourceEncoding());
+        assertEquals(CompilerConfiguration.PRE_JDK5, 
config.getTargetBytecode());
+        assertEquals(false, config.getRecompileGroovySource());
+        {
+            final List listCP = config.getClasspath();
+            assertEquals(0, listCP.size());
+        }
+        assertEquals(initTDFile, config.getTargetDirectory());
+    }
+}
diff --git 
a/src/test/org/codehaus/groovy/transform/classloading/TransformsAndCustomClassLoadersTest.groovy
 
b/src/test/org/codehaus/groovy/transform/classloading/TransformsAndCustomClassLoadersTest.groovy
index 456f8ab..18030fe 100644
--- 
a/src/test/org/codehaus/groovy/transform/classloading/TransformsAndCustomClassLoadersTest.groovy
+++ 
b/src/test/org/codehaus/groovy/transform/classloading/TransformsAndCustomClassLoadersTest.groovy
@@ -29,6 +29,7 @@ import 
org.codehaus.groovy.transform.GroovyASTTransformationClass
 import org.codehaus.groovy.transform.ASTTransformation
 import org.codehaus.groovy.transform.GroovyASTTransformation
 import org.codehaus.groovy.transform.GlobalTestTransformClassLoader
+import org.codehaus.groovy.vmplugin.VMPluginFactory
 import org.objectweb.asm.ClassVisitor
 
 import java.lang.annotation.Retention
@@ -43,7 +44,7 @@ import java.lang.annotation.ElementType
  */
 class TransformsAndCustomClassLoadersTest extends GroovyTestCase {
     URL[] urls = collectUrls(getClass().classLoader) + addGroovyUrls()
-    GroovyClassLoader dependencyLoader = new GroovyClassLoader(new 
URLClassLoader(urls, (ClassLoader)null))
+    GroovyClassLoader dependencyLoader = new GroovyClassLoader(new 
URLClassLoader(urls, (ClassLoader) ( VMPluginFactory.getPlugin().getVersion() 
>= 9 ? ClassLoader.getPlatformClassLoader() : null )))
     GroovyClassLoader transformLoader = new GroovyClassLoader(new 
URLClassLoader(urls, new GroovyOnlyClassLoader()))
 
     private static addGroovyUrls() {

Reply via email to