Wed, 21 Sep 2011 21:56:53 -0700
7092965: javac should not close processorClassLoader before end of compilation
Reviewed-by: darcy
1.1 --- a/src/share/classes/com/sun/tools/javac/main/JavaCompiler.java Tue Sep 20 12:08:48 2011 -0700 1.2 +++ b/src/share/classes/com/sun/tools/javac/main/JavaCompiler.java Wed Sep 21 21:56:53 2011 -0700 1.3 @@ -488,6 +488,10 @@ 1.4 */ 1.5 public Todo todo; 1.6 1.7 + /** A list of items to be closed when the compilation is complete. 1.8 + */ 1.9 + public List<Closeable> closeables = List.nil(); 1.10 + 1.11 /** Ordered list of compiler phases for each compilation unit. */ 1.12 public enum CompileState { 1.13 PARSE(1), 1.14 @@ -1581,6 +1585,19 @@ 1.15 if (names != null && disposeNames) 1.16 names.dispose(); 1.17 names = null; 1.18 + 1.19 + for (Closeable c: closeables) { 1.20 + try { 1.21 + c.close(); 1.22 + } catch (IOException e) { 1.23 + // When javac uses JDK 7 as a baseline, this code would be 1.24 + // better written to set any/all exceptions from all the 1.25 + // Closeables as suppressed exceptions on the FatalError 1.26 + // that is thrown. 1.27 + JCDiagnostic msg = diagFactory.fragment("fatal.err.cant.close"); 1.28 + throw new FatalError(msg, e); 1.29 + } 1.30 + } 1.31 } 1.32 } 1.33 1.34 @@ -1615,6 +1632,8 @@ 1.35 keepComments = prev.keepComments; 1.36 start_msec = prev.start_msec; 1.37 hasBeenUsed = true; 1.38 + closeables = prev.closeables; 1.39 + prev.closeables = List.nil(); 1.40 } 1.41 1.42 public static void enableLogging() {
2.1 --- a/src/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java Tue Sep 20 12:08:48 2011 -0700 2.2 +++ b/src/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java Wed Sep 21 21:56:53 2011 -0700 2.3 @@ -225,6 +225,11 @@ 2.4 ? fileManager.getClassLoader(ANNOTATION_PROCESSOR_PATH) 2.5 : fileManager.getClassLoader(CLASS_PATH); 2.6 2.7 + if (processorClassLoader != null && processorClassLoader instanceof Closeable) { 2.8 + JavaCompiler compiler = JavaCompiler.instance(context); 2.9 + compiler.closeables = compiler.closeables.prepend((Closeable) processorClassLoader); 2.10 + } 2.11 + 2.12 /* 2.13 * If the "-processor" option is used, search the appropriate 2.14 * path for the named class. Otherwise, use a service 2.15 @@ -1211,14 +1216,6 @@ 2.16 if (discoveredProcs != null) // Make calling close idempotent 2.17 discoveredProcs.close(); 2.18 discoveredProcs = null; 2.19 - if (processorClassLoader != null && processorClassLoader instanceof Closeable) { 2.20 - try { 2.21 - ((Closeable) processorClassLoader).close(); 2.22 - } catch (IOException e) { 2.23 - JCDiagnostic msg = diags.fragment("fatal.err.cant.close.loader"); 2.24 - throw new FatalError(msg, e); 2.25 - } 2.26 - } 2.27 } 2.28 2.29 private List<ClassSymbol> getTopLevelClasses(List<? extends JCCompilationUnit> units) {
3.1 --- a/src/share/classes/com/sun/tools/javac/resources/compiler.properties Tue Sep 20 12:08:48 2011 -0700 3.2 +++ b/src/share/classes/com/sun/tools/javac/resources/compiler.properties Wed Sep 21 21:56:53 2011 -0700 3.3 @@ -895,8 +895,8 @@ 3.4 compiler.misc.fatal.err.cant.locate.ctor=\ 3.5 Fatal Error: Unable to find constructor for {0} 3.6 3.7 -compiler.misc.fatal.err.cant.close.loader=\ 3.8 - Fatal Error: Cannot close class loader for annotation processors 3.9 +compiler.misc.fatal.err.cant.close=\ 3.10 + Fatal Error: Cannot close compiler resources 3.11 3.12 ##### 3.13
4.1 --- a/test/tools/javac/diags/examples.not-yet.txt Tue Sep 20 12:08:48 2011 -0700 4.2 +++ b/test/tools/javac/diags/examples.not-yet.txt Wed Sep 21 21:56:53 2011 -0700 4.3 @@ -60,7 +60,7 @@ 4.4 compiler.misc.fatal.err.cant.locate.ctor # Resolve, from Lower 4.5 compiler.misc.fatal.err.cant.locate.field # Resolve, from Lower 4.6 compiler.misc.fatal.err.cant.locate.meth # Resolve, from Lower 4.7 -compiler.misc.fatal.err.cant.close.loader # JavacProcessingEnvironment 4.8 +compiler.misc.fatal.err.cant.close # JavaCompiler 4.9 compiler.misc.file.does.not.contain.package 4.10 compiler.misc.illegal.start.of.class.file 4.11 compiler.misc.kindname.annotation
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 5.2 +++ b/test/tools/javac/processing/loader/testClose/TestClose.java Wed Sep 21 21:56:53 2011 -0700 5.3 @@ -0,0 +1,229 @@ 5.4 +/* 5.5 + * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. 5.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5.7 + * 5.8 + * This code is free software; you can redistribute it and/or modify it 5.9 + * under the terms of the GNU General Public License version 2 only, as 5.10 + * published by the Free Software Foundation. 5.11 + * 5.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 5.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 5.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 5.15 + * version 2 for more details (a copy is included in the LICENSE file that 5.16 + * accompanied this code). 5.17 + * 5.18 + * You should have received a copy of the GNU General Public License version 5.19 + * 2 along with this work; if not, write to the Free Software Foundation, 5.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 5.21 + * 5.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 5.23 + * or visit www.oracle.com if you need additional information or have any 5.24 + * questions. 5.25 + */ 5.26 + 5.27 +/* 5.28 + * @test 5.29 + * @bug 7092965 5.30 + * @summary javac should not close processorClassLoader before end of compilation 5.31 + */ 5.32 + 5.33 +import com.sun.source.util.JavacTask; 5.34 +import com.sun.source.util.TaskEvent; 5.35 +import com.sun.source.util.TaskListener; 5.36 +import com.sun.tools.javac.api.ClientCodeWrapper.Trusted; 5.37 +import com.sun.tools.javac.api.JavacTool; 5.38 +import com.sun.tools.javac.processing.JavacProcessingEnvironment; 5.39 +import com.sun.tools.javac.util.Context; 5.40 +import java.io.ByteArrayOutputStream; 5.41 +import java.io.File; 5.42 +import java.io.IOException; 5.43 +import java.io.PrintStream; 5.44 +import java.lang.reflect.Field; 5.45 +import java.net.URI; 5.46 +import java.util.ArrayList; 5.47 +import java.util.Arrays; 5.48 +import java.util.Collections; 5.49 +import java.util.List; 5.50 +import javax.annotation.processing.ProcessingEnvironment; 5.51 +import javax.tools.JavaFileObject; 5.52 +import javax.tools.SimpleJavaFileObject; 5.53 +import javax.tools.StandardJavaFileManager; 5.54 +import javax.tools.StandardLocation; 5.55 +import javax.tools.ToolProvider; 5.56 + 5.57 +/* 5.58 + * The test compiles an annotation processor and a helper class into a 5.59 + * custom classes directory. 5.60 + * 5.61 + * It then uses them while compiling a dummy file, with the custom classes 5.62 + * directory on the processor path, thus guaranteeing that references to 5.63 + * these class are satisfied by the processor class loader. 5.64 + * 5.65 + * The annotation processor uses the javac TaskListener to run code 5.66 + * after annotation processing has completed, to verify that the classloader 5.67 + * is not closed until the end of the compilation. 5.68 + */ 5.69 + 5.70 +@Trusted // avoids use of ClientCodeWrapper 5.71 +public class TestClose implements TaskListener { 5.72 + public static final String annoProc = 5.73 + "import java.util.*;\n" + 5.74 + "import javax.annotation.processing.*;\n" + 5.75 + "import javax.lang.model.*;\n" + 5.76 + "import javax.lang.model.element.*;\n" + 5.77 + "import com.sun.source.util.*;\n" + 5.78 + "import com.sun.tools.javac.processing.*;\n" + 5.79 + "import com.sun.tools.javac.util.*;\n" + 5.80 + "@SupportedAnnotationTypes(\"*\")\n" + 5.81 + "public class AnnoProc extends AbstractProcessor {\n" + 5.82 + " @Override\n" + 5.83 + " public SourceVersion getSupportedSourceVersion() {\n" + 5.84 + " return SourceVersion.latest();\n" + 5.85 + " }\n" + 5.86 + " @Override\n" + 5.87 + " public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {\n" + 5.88 + " System.out.println(\"in AnnoProc.process\");\n" + 5.89 + " final ClassLoader cl = getClass().getClassLoader();\n" + 5.90 + " if (roundEnv.processingOver()) {\n" + 5.91 + " TestClose.add(processingEnv, new Runnable() {\n" + 5.92 + " public void run() {\n" + 5.93 + " System.out.println(getClass().getName() + \": run()\");\n" + 5.94 + " try {\n" + 5.95 + " cl.loadClass(\"Callback\")\n" + 5.96 + " .asSubclass(Runnable.class)\n" + 5.97 + " .newInstance()\n" + 5.98 + " .run();\n" + 5.99 + " } catch (ReflectiveOperationException e) {\n" + 5.100 + " throw new Error(e);\n" + 5.101 + " }\n" + 5.102 + " }\n" + 5.103 + " });\n" + 5.104 + " }\n" + 5.105 + " return true;\n" + 5.106 + " }\n" + 5.107 + "}\n"; 5.108 + 5.109 + public static final String callback = 5.110 + "public class Callback implements Runnable {\n" + 5.111 + " public void run() {\n" + 5.112 + " System.out.println(getClass().getName() + \": run()\");\n" + 5.113 + " }\n" + 5.114 + "}"; 5.115 + 5.116 + public static void main(String... args) throws Exception { 5.117 + new TestClose().run(); 5.118 + } 5.119 + 5.120 + void run() throws IOException { 5.121 + JavacTool tool = (JavacTool) ToolProvider.getSystemJavaCompiler(); 5.122 + StandardJavaFileManager fm = tool.getStandardFileManager(null, null, null); 5.123 + 5.124 + File classes = new File("classes"); 5.125 + classes.mkdirs(); 5.126 + File extraClasses = new File("extraClasses"); 5.127 + extraClasses.mkdirs(); 5.128 + 5.129 + System.out.println("compiling classes to extraClasses"); 5.130 + { // setup class in extraClasses 5.131 + fm.setLocation(StandardLocation.CLASS_OUTPUT, 5.132 + Collections.singleton(extraClasses)); 5.133 + List<? extends JavaFileObject> files = Arrays.asList( 5.134 + new MemFile("AnnoProc.java", annoProc), 5.135 + new MemFile("Callback.java", callback)); 5.136 + JavacTask task = tool.getTask(null, fm, null, null, null, files); 5.137 + check(task.call()); 5.138 + } 5.139 + 5.140 + System.out.println("compiling dummy to classes with anno processor"); 5.141 + { // use that class in a TaskListener after processing has completed 5.142 + PrintStream prev = System.out; 5.143 + String out; 5.144 + ByteArrayOutputStream baos = new ByteArrayOutputStream(); 5.145 + try (PrintStream ps = new PrintStream(baos)) { 5.146 + System.setOut(ps); 5.147 + File testClasses = new File(System.getProperty("test.classes")); 5.148 + fm.setLocation(StandardLocation.CLASS_OUTPUT, 5.149 + Collections.singleton(classes)); 5.150 + fm.setLocation(StandardLocation.ANNOTATION_PROCESSOR_PATH, 5.151 + Arrays.asList(extraClasses, testClasses)); 5.152 + List<? extends JavaFileObject> files = Arrays.asList( 5.153 + new MemFile("my://dummy", "class Dummy { }")); 5.154 + List<String> options = Arrays.asList("-processor", "AnnoProc"); 5.155 + JavacTask task = tool.getTask(null, fm, null, options, null, files); 5.156 + task.setTaskListener(this); 5.157 + check(task.call()); 5.158 + } finally { 5.159 + System.setOut(prev); 5.160 + out = baos.toString(); 5.161 + if (!out.isEmpty()) 5.162 + System.out.println(out); 5.163 + } 5.164 + check(out.contains("AnnoProc$1: run()")); 5.165 + check(out.contains("Callback: run()")); 5.166 + } 5.167 + } 5.168 + 5.169 + @Override 5.170 + public void started(TaskEvent e) { 5.171 + System.out.println("Started: " + e); 5.172 + } 5.173 + 5.174 + @Override 5.175 + public void finished(TaskEvent e) { 5.176 + System.out.println("Finished: " + e); 5.177 + if (e.getKind() == TaskEvent.Kind.ANALYZE) { 5.178 + for (Runnable r: runnables) { 5.179 + System.out.println("running " + r); 5.180 + r.run(); 5.181 + } 5.182 + } 5.183 + } 5.184 + 5.185 + void check(boolean b) { 5.186 + if (!b) 5.187 + throw new AssertionError(); 5.188 + } 5.189 + 5.190 + 5.191 + public static void add(ProcessingEnvironment env, Runnable r) { 5.192 + try { 5.193 + Context c = ((JavacProcessingEnvironment) env).getContext(); 5.194 + Object o = c.get(TaskListener.class); 5.195 + // The TaskListener is an instanceof TestClose, but when using the 5.196 + // default class loaders. the taskListener uses a different 5.197 + // instance of Class<TestClose> than the anno processor. 5.198 + // If you try to evaluate 5.199 + // TestClose tc = (TestClose) (o). 5.200 + // you get the following somewhat confusing error: 5.201 + // java.lang.ClassCastException: TestClose cannot be cast to TestClose 5.202 + // The workaround is to access the fields of TestClose with reflection. 5.203 + Field f = o.getClass().getField("runnables"); 5.204 + @SuppressWarnings("unchecked") 5.205 + List<Runnable> runnables = (List<Runnable>) f.get(o); 5.206 + runnables.add(r); 5.207 + } catch (Throwable t) { 5.208 + System.err.println(t); 5.209 + } 5.210 + } 5.211 + 5.212 + public List<Runnable> runnables = new ArrayList<>(); 5.213 + 5.214 + class MemFile extends SimpleJavaFileObject { 5.215 + public final String text; 5.216 + 5.217 + MemFile(String name, String text) { 5.218 + super(URI.create(name), JavaFileObject.Kind.SOURCE); 5.219 + this.text = text; 5.220 + } 5.221 + 5.222 + @Override 5.223 + public String getName() { 5.224 + return uri.toString(); 5.225 + } 5.226 + 5.227 + @Override 5.228 + public String getCharContent(boolean ignoreEncodingErrors) { 5.229 + return text; 5.230 + } 5.231 + } 5.232 +}
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 6.2 +++ b/test/tools/javac/processing/loader/testClose/TestClose2.java Wed Sep 21 21:56:53 2011 -0700 6.3 @@ -0,0 +1,141 @@ 6.4 +/* 6.5 + * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. 6.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 6.7 + * 6.8 + * This code is free software; you can redistribute it and/or modify it 6.9 + * under the terms of the GNU General Public License version 2 only, as 6.10 + * published by the Free Software Foundation. 6.11 + * 6.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 6.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 6.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 6.15 + * version 2 for more details (a copy is included in the LICENSE file that 6.16 + * accompanied this code). 6.17 + * 6.18 + * You should have received a copy of the GNU General Public License version 6.19 + * 2 along with this work; if not, write to the Free Software Foundation, 6.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 6.21 + * 6.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 6.23 + * or visit www.oracle.com if you need additional information or have any 6.24 + * questions. 6.25 + */ 6.26 + 6.27 +/* 6.28 + * @test 6.29 + * @bug 7092965 6.30 + * @summary javac should not close processorClassLoader before end of compilation 6.31 + */ 6.32 + 6.33 +import com.sun.source.util.JavacTask; 6.34 +import com.sun.source.util.TaskEvent; 6.35 +import com.sun.source.util.TaskListener; 6.36 +import com.sun.tools.javac.api.JavacTool; 6.37 +import com.sun.tools.javac.file.JavacFileManager; 6.38 +import com.sun.tools.javac.util.Context; 6.39 +import java.io.File; 6.40 +import java.io.IOException; 6.41 +import java.net.URL; 6.42 +import java.net.URLClassLoader; 6.43 +import java.util.Arrays; 6.44 +import java.util.Collections; 6.45 +import java.util.List; 6.46 +import java.util.Set; 6.47 +import javax.annotation.processing.AbstractProcessor; 6.48 +import javax.annotation.processing.Messager; 6.49 +import javax.annotation.processing.RoundEnvironment; 6.50 +import javax.annotation.processing.SupportedAnnotationTypes; 6.51 +import javax.lang.model.SourceVersion; 6.52 +import javax.lang.model.element.TypeElement; 6.53 +import javax.tools.Diagnostic; 6.54 +import javax.tools.JavaFileObject; 6.55 +import javax.tools.StandardJavaFileManager; 6.56 +import javax.tools.StandardLocation; 6.57 +import javax.tools.ToolProvider; 6.58 + 6.59 +@SupportedAnnotationTypes("*") 6.60 +public class TestClose2 extends AbstractProcessor implements TaskListener { 6.61 + 6.62 + public static void main(String... args) throws Exception { 6.63 + new TestClose2().run(); 6.64 + } 6.65 + 6.66 + void run() throws IOException { 6.67 + File testSrc = new File(System.getProperty("test.src")); 6.68 + File testClasses = new File(System.getProperty("test.classes")); 6.69 + 6.70 + JavacTool tool = (JavacTool) ToolProvider.getSystemJavaCompiler(); 6.71 + final ClassLoader cl = getClass().getClassLoader(); 6.72 + Context c = new Context(); 6.73 + StandardJavaFileManager fm = new JavacFileManager(c, true, null) { 6.74 + @Override 6.75 + protected ClassLoader getClassLoader(URL[] urls) { 6.76 + return new URLClassLoader(urls, cl) { 6.77 + @Override 6.78 + public void close() throws IOException { 6.79 + System.err.println(getClass().getName() + " closing"); 6.80 + TestClose2.this.closedCount++; 6.81 + TestClose2.this.closedIsLast = true; 6.82 + super.close(); 6.83 + } 6.84 + }; 6.85 + } 6.86 + }; 6.87 + 6.88 + fm.setLocation(StandardLocation.CLASS_OUTPUT, 6.89 + Collections.singleton(new File("."))); 6.90 + fm.setLocation(StandardLocation.ANNOTATION_PROCESSOR_PATH, 6.91 + Collections.singleton(testClasses)); 6.92 + Iterable<? extends JavaFileObject> files = 6.93 + fm.getJavaFileObjects(new File(testSrc, TestClose2.class.getName() + ".java")); 6.94 + List<String> options = Arrays.asList( 6.95 + "-processor", TestClose2.class.getName()); 6.96 + 6.97 + JavacTask task = tool.getTask(null, fm, null, options, null, files); 6.98 + task.setTaskListener(this); 6.99 + 6.100 + if (!task.call()) 6.101 + throw new Error("compilation failed"); 6.102 + 6.103 + if (closedCount == 0) 6.104 + throw new Error("no closing message"); 6.105 + else if (closedCount > 1) 6.106 + throw new Error(closedCount + " closed messages"); 6.107 + 6.108 + if (!closedIsLast) 6.109 + throw new Error("closing message not last"); 6.110 + } 6.111 + 6.112 + // AbstractProcessor methods 6.113 + 6.114 + @Override 6.115 + public SourceVersion getSupportedSourceVersion() { 6.116 + return SourceVersion.latest(); 6.117 + } 6.118 + 6.119 + @Override 6.120 + public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { 6.121 + Messager messager = processingEnv.getMessager(); 6.122 + messager.printMessage(Diagnostic.Kind.NOTE, "processing"); 6.123 + return true; 6.124 + } 6.125 + 6.126 + // TaskListener methods 6.127 + 6.128 + @Override 6.129 + public void started(TaskEvent e) { 6.130 + System.err.println("Started: " + e); 6.131 + closedIsLast = false; 6.132 + } 6.133 + 6.134 + @Override 6.135 + public void finished(TaskEvent e) { 6.136 + System.err.println("Finished: " + e); 6.137 + closedIsLast = false; 6.138 + } 6.139 + 6.140 + // 6.141 + 6.142 + int closedCount = 0; 6.143 + boolean closedIsLast = false; 6.144 +}