You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by su...@apache.org on 2019/05/04 06:21:39 UTC

[groovy] branch master updated: GROOVY-9067: Compile Java stubs with standard API(closes #904)

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 3f87c18  GROOVY-9067: Compile Java stubs with standard API(closes #904)
3f87c18 is described below

commit 3f87c184120667ff3ccc4a5b71a0c9cec62751e0
Author: Daniel Sun <su...@apache.org>
AuthorDate: Sat May 4 14:20:29 2019 +0800

    GROOVY-9067: Compile Java stubs with standard API(closes #904)
---
 .../tools/javac/JavaAwareCompilationUnit.java      |   4 +-
 .../groovy/tools/javac/JavaStubGenerator.java      |  16 ++-
 .../groovy/tools/javac/JavacJavaCompiler.java      | 158 +++++----------------
 .../groovy/tools/javac/MemJavaFileObject.java      |   6 +-
 .../groovy/tools/javac/RawJavaFileObject.java      |  67 +++++++++
 src/test/groovy/bugs/Groovy7721Bug.groovy          |   8 +-
 6 files changed, 126 insertions(+), 133 deletions(-)

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 80e722f..dccc80d 100644
--- a/src/main/java/org/codehaus/groovy/tools/javac/JavaAwareCompilationUnit.java
+++ b/src/main/java/org/codehaus/groovy/tools/javac/JavaAwareCompilationUnit.java
@@ -109,9 +109,7 @@ public class JavaAwareCompilationUnit extends CompilationUnit {
                 module.setImportsResolved(false);
             }
             try {
-                if (memStubEnabled) {
-                    this.addJavaCompilationUnits(stubGenerator.getJavaStubCompilationUnitSet()); // add java stubs
-                }
+                this.addJavaCompilationUnits(stubGenerator.getJavaStubCompilationUnitSet()); // add java stubs
 
                 JavaCompiler compiler = compilerFactory.createCompiler(getConfiguration());
                 compiler.compile(javaSources, this);
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 8f4dabd..394bb2d 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.FileObject;
 import javax.tools.JavaFileObject;
 import java.io.BufferedOutputStream;
 import java.io.File;
@@ -76,7 +77,6 @@ public class JavaStubGenerator {
     private final String encoding;
     private final boolean requireSuperResolved;
     private final File outputPath;
-    private final List<String> toCompile = new ArrayList<String>();
     private final ArrayList<MethodNode> propertyMethods = new ArrayList<MethodNode>();
     private final Map<String, MethodNode> propertyMethodsWithSigs = new HashMap<String, MethodNode>();
     private final ArrayList<ConstructorNode> constructors = new ArrayList<ConstructorNode>();
@@ -134,9 +134,8 @@ public class JavaStubGenerator {
     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");
+        File file = createJavaStubFile(fileName);
 
         Writer writer = new OutputStreamWriter(
                 new BufferedOutputStream(
@@ -146,6 +145,8 @@ public class JavaStubGenerator {
                 Charset.forName(encoding)
         );
         generateStubContent(classNode, writer);
+
+        javaStubCompilationUnitSet.add(new RawJavaFileObject(createJavaStubFile(fileName).toPath().toUri()));
     }
 
     private void generateStubContent(ClassNode classNode, Writer writer) {
@@ -1006,13 +1007,14 @@ public class JavaStubGenerator {
     }
 
     public void clean() {
-        for (String path : toCompile) {
-            new File(outputPath, path + ".java").delete();
-        }
-
+        javaStubCompilationUnitSet.parallelStream().peek(FileObject::delete);
         javaStubCompilationUnitSet.clear();
     }
 
+    private File createJavaStubFile(String path) {
+        return new File(outputPath, path + ".java");
+    }
+
     private static String escapeSpecialChars(String value) {
         return InvokerHelper.escapeBackslashes(value).replace("\"", "\\\"");
 
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 631a1cd..4024f42 100644
--- a/src/main/java/org/codehaus/groovy/tools/javac/JavacJavaCompiler.java
+++ b/src/main/java/org/codehaus/groovy/tools/javac/JavacJavaCompiler.java
@@ -27,14 +27,12 @@ import org.codehaus.groovy.control.messages.ExceptionMessage;
 import org.codehaus.groovy.control.messages.SimpleMessage;
 import org.codehaus.groovy.runtime.DefaultGroovyMethods;
 
+import javax.tools.JavaCompiler.CompilationTask;
 import javax.tools.JavaFileObject;
 import javax.tools.StandardJavaFileManager;
 import javax.tools.ToolProvider;
 import java.io.File;
 import java.io.IOException;
-import java.io.PrintWriter;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
 import java.net.URISyntaxException;
 import java.net.URL;
 import java.net.URLClassLoader;
@@ -54,39 +52,52 @@ import java.util.stream.Collectors;
 
 public class JavacJavaCompiler implements JavaCompiler {
     private static final String[] EMPTY_STRING_ARRAY = new String[0];
+    private static final Locale DEFAULT_LOCALE = Locale.ENGLISH;
     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 {
+    public void compile(List<String> files, CompilationUnit cu) {
+        String[] javacParameters = makeParameters(cu.getClassLoader());
+        StringBuilderWriter javacOutput = new StringBuilderWriter();
         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;
+            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));
+            } catch (IOException e) {
+                javacReturnValue = 1;
+                cu.getErrorCollector().addFatalError(new ExceptionMessage(e, true, cu));
+            }
+
+        } catch (Exception e) {
+            cu.getErrorCollector().addFatalError(new ExceptionMessage(e, true, cu));
         }
 
-        return javacReturnValue;
+        if (javacReturnValue != 0) {
+            switch (javacReturnValue) {
+                case 1: addJavacError("Compile error during compilation with javac.", cu, javacOutput); break;
+                case 2: addJavacError("Invalid commandline usage for javac.", cu, javacOutput); break;
+                default: addJavacError("unexpected return value by javac.", cu, javacOutput); break;
+            }
+        } else {
+            // print warnings if any
+            System.out.print(javacOutput);
+        }
     }
 
     private boolean doCompileWithSystemJavaCompiler(CompilationUnit cu, List<String> files, String[] javacParameters, StringBuilderWriter javacOutput) throws IOException {
         javax.tools.JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
-        try (StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, charset)) {
+        try (StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, DEFAULT_LOCALE, charset)) {
             final Set<JavaFileObject> compilationUnitSet = cu.getJavaCompilationUnitSet(); // java stubs already added
 
             // add java source files to compile
@@ -96,7 +107,7 @@ public class JavacJavaCompiler implements JavaCompiler {
                             .collect(Collectors.toList())
             ).forEach(compilationUnitSet::add);
 
-            javax.tools.JavaCompiler.CompilationTask compilationTask = compiler.getTask(
+            CompilationTask compilationTask = compiler.getTask(
                     javacOutput,
                     fileManager,
                     null,
@@ -104,58 +115,12 @@ public class JavacJavaCompiler implements JavaCompiler {
                     Collections.emptyList(),
                     compilationUnitSet
             );
+            compilationTask.setLocale(DEFAULT_LOCALE);
 
             return compilationTask.call();
         }
     }
 
-    public void compile(List<String> files, CompilationUnit cu) {
-        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 {
-            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));
-                } catch (IOException e) {
-                    javacReturnValue = 1;
-                    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 (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;
-                case 2: addJavacError("Invalid commandline usage for javac.", cu, javacOutput); break;
-                case 3: addJavacError("System error during compilation with javac.", cu, javacOutput); break;
-                case 4: addJavacError("Abnormal termination of javac.", cu, javacOutput); break;
-                default: addJavacError("unexpected return value by javac.", cu, javacOutput); break;
-            }
-        } else {
-            // print warnings if any
-            System.out.print(javacOutput);
-        }
-    }
-
     private static void addJavacError(String header, CompilationUnit cu, StringBuilderWriter msg) {
         if (msg != null) {
             header = header + "\n" + msg.getBuilder().toString();
@@ -168,7 +133,7 @@ public class JavacJavaCompiler implements JavaCompiler {
         cu.getErrorCollector().addFatalError(new SimpleMessage(header, cu));
     }
 
-    private String[] makeParameters(List<String> files, GroovyClassLoader parentClassLoader, boolean toCompileStubInMem) {
+    private String[] makeParameters(GroovyClassLoader parentClassLoader) {
         Map options = config.getJointCompilationOptions();
         LinkedList<String> paras = new LinkedList<String>();
 
@@ -179,11 +144,6 @@ public class JavacJavaCompiler implements JavaCompiler {
         paras.add("-d");
         paras.add(target.getAbsolutePath());
 
-        if (!toCompileStubInMem) {
-            paras.add("-sourcepath");
-            paras.add(((File) options.get("stubDir")).getAbsolutePath());
-        }
-
         // add flags
         String[] flags = (String[]) options.get("flags");
         if (flags != null) {
@@ -223,12 +183,10 @@ public class JavacJavaCompiler implements JavaCompiler {
             }
 
             try {
-                CodeSource codeSource =AccessController.doPrivileged(new PrivilegedAction<CodeSource>() {
-                    @Override
-                    public CodeSource run() {
-                        return GroovyObject.class.getProtectionDomain().getCodeSource();
-                    }
-                });
+                CodeSource codeSource =
+                        AccessController.doPrivileged(
+                                (PrivilegedAction<CodeSource>) () -> GroovyObject.class.getProtectionDomain().getCodeSource()
+                        );
                 if (codeSource != null) {
                     paths.add(new File(codeSource.getLocation().toURI()).getPath());
                 }
@@ -240,47 +198,7 @@ public class JavacJavaCompiler implements JavaCompiler {
             paras.add(DefaultGroovyMethods.join((Iterable) paths, File.pathSeparator));
         }
 
-        if (!toCompileStubInMem) {
-            // files to compile
-            paras.addAll(files);
-        }
-
         return paras.toArray(EMPTY_STRING_ARRAY);
     }
 
-    private Class findJavac(CompilationUnit cu) throws ClassNotFoundException {
-        String main = "com.sun.tools.javac.Main";
-        try {
-            return Class.forName(main);
-        } catch (ClassNotFoundException e) {}
-
-        try {
-            ClassLoader cl = this.getClass().getClassLoader();
-            return cl.loadClass(main);
-        } catch (ClassNotFoundException e) {}
-
-        try {
-            return ClassLoader.getSystemClassLoader().loadClass(main);
-        } catch (ClassNotFoundException e) {}
-
-        try {
-            return cu.getClassLoader().getParent().loadClass(main);
-        } catch (ClassNotFoundException e3) {}
-
-
-        // couldn't find compiler - try to find tools.jar
-        // based on java.home setting
-        String javaHome = System.getProperty("java.home");
-        if (javaHome.toLowerCase(Locale.US).endsWith("jre")) {
-            javaHome = javaHome.substring(0, javaHome.length() - 4);
-        }
-        File toolsJar = new File((javaHome + "/lib/tools.jar"));
-        if (toolsJar.exists()) {
-            GroovyClassLoader loader = cu.getClassLoader();
-            loader.addClasspath(toolsJar.getAbsolutePath());
-            return loader.loadClass(main);
-        }
-
-        throw new ClassNotFoundException("unable to locate the java compiler com.sun.tools.javac.Main, please change your classloader settings");
-    }
 }
diff --git a/src/main/java/org/codehaus/groovy/tools/javac/MemJavaFileObject.java b/src/main/java/org/codehaus/groovy/tools/javac/MemJavaFileObject.java
index ca01472..3ae8fbd 100644
--- a/src/main/java/org/codehaus/groovy/tools/javac/MemJavaFileObject.java
+++ b/src/main/java/org/codehaus/groovy/tools/javac/MemJavaFileObject.java
@@ -41,7 +41,7 @@ public class MemJavaFileObject extends SimpleJavaFileObject {
      * @param classNode  the groovy class node
      * @param src  the stub source code
      */
-    protected MemJavaFileObject(ClassNode classNode, String src) {
+    public MemJavaFileObject(ClassNode classNode, String src) {
         super(createURI(classNode), JavaFileObject.Kind.SOURCE);
         this.className = classNode.getName();
         this.src = src;
@@ -78,6 +78,8 @@ public class MemJavaFileObject extends SimpleJavaFileObject {
 
     @Override
     public String toString() {
-        return className;
+        return "MemJavaFileObject{" +
+                "className=" + className +
+                '}';
     }
 }
diff --git a/src/main/java/org/codehaus/groovy/tools/javac/RawJavaFileObject.java b/src/main/java/org/codehaus/groovy/tools/javac/RawJavaFileObject.java
new file mode 100644
index 0000000..ce0be98
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/tools/javac/RawJavaFileObject.java
@@ -0,0 +1,67 @@
+package org.codehaus.groovy.tools.javac;
+
+import org.codehaus.groovy.control.CompilerConfiguration;
+
+import javax.tools.JavaFileObject;
+import javax.tools.SimpleJavaFileObject;
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Objects;
+
+/**
+ * Represents a Java source file in file to compile
+ * @since 3.0.0
+ */
+public class RawJavaFileObject extends SimpleJavaFileObject {
+    private static final Charset DEFAULT_CHARSET = Charset.forName(CompilerConfiguration.DEFAULT.getSourceEncoding());
+    private final Path javaFilePath;
+    private String src;
+
+    /**
+     * Construct a RawJavaFileObject of the given kind and with the
+     * given URI.
+     *
+     * @param uri  the URI for this file object
+     */
+    public RawJavaFileObject(URI uri) {
+        super(uri, JavaFileObject.Kind.SOURCE);
+        this.javaFilePath = Paths.get(uri);
+    }
+
+    public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
+        return null != src ? src : (src = new String(Files.readAllBytes(javaFilePath), DEFAULT_CHARSET));
+    }
+
+    /**
+     * delete the Java source file
+     * @return <code>true</code> if deleted successfully
+     */
+    public boolean delete() {
+        return new File(uri).delete();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof RawJavaFileObject)) return false;
+        RawJavaFileObject that = (RawJavaFileObject) o;
+        return Objects.equals(uri, that.uri);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(uri);
+    }
+
+    @Override
+    public String toString() {
+        return "RawJavaFileObject{" +
+                "uri=" + uri +
+                '}';
+    }
+}
diff --git a/src/test/groovy/bugs/Groovy7721Bug.groovy b/src/test/groovy/bugs/Groovy7721Bug.groovy
index f316a2a..4e41958 100644
--- a/src/test/groovy/bugs/Groovy7721Bug.groovy
+++ b/src/test/groovy/bugs/Groovy7721Bug.groovy
@@ -41,10 +41,16 @@ class Groovy7721Bug extends GroovyTestCase {
                     }
 
                 '''
+
+            //    We should declare interface B as public, or B can not be accessed from outside package(Note: C is in the default package)
+            //
+            //    See the error message:
+            //    /tmp/groovyTest15544748311926307266330586729753/C.java:17: error: B is not public in pack; cannot be accessed from outside package
+            //    public static  java.lang.Object bar(pack.B b) { return null;}
             def b = new File(parentDir, 'B.java')
             b.write '''
                     package pack;
-                    interface B extends A {
+                    public interface B extends A {
                         @Override
                         String[] bar();
                     }