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