Mon, 23 Aug 2010 11:56:53 -0700
6975005: improve JavacProcessingEnvironment.Round abstraction
Reviewed-by: darcy
1.1 --- a/src/share/classes/com/sun/tools/javac/main/JavaCompiler.java Mon Aug 23 17:00:07 2010 +0100 1.2 +++ b/src/share/classes/com/sun/tools/javac/main/JavaCompiler.java Mon Aug 23 11:56:53 2010 -0700 1.3 @@ -529,7 +529,7 @@ 1.4 log.error("warnings.and.werror"); 1.5 } 1.6 } 1.7 - return log.nerrors; 1.8 + return log.nerrors; 1.9 } 1.10 1.11 protected final <T> Queue<T> stopIfError(CompileState cs, Queue<T> queue) { 1.12 @@ -868,7 +868,7 @@ 1.13 /** 1.14 * Parses a list of files. 1.15 */ 1.16 - public List<JCCompilationUnit> parseFiles(List<JavaFileObject> fileObjects) throws IOException { 1.17 + public List<JCCompilationUnit> parseFiles(Iterable<JavaFileObject> fileObjects) throws IOException { 1.18 if (shouldStop(CompileState.PARSE)) 1.19 return List.nil(); 1.20 1.21 @@ -981,14 +981,13 @@ 1.22 */ 1.23 public JavaCompiler processAnnotations(List<JCCompilationUnit> roots, 1.24 List<String> classnames) 1.25 - throws IOException { // TODO: see TEMP note in JavacProcessingEnvironment 1.26 + throws IOException { // TODO: see TEMP note in JavacProcessingEnvironment 1.27 if (shouldStop(CompileState.PROCESS)) { 1.28 - // Errors were encountered. If todo is empty, then the 1.29 - // encountered errors were parse errors. Otherwise, the 1.30 - // errors were found during the enter phase which should 1.31 - // be ignored when processing annotations. 1.32 - 1.33 - if (todo.isEmpty()) 1.34 + // Errors were encountered. 1.35 + // If log.unrecoverableError is set, the errors were parse errors 1.36 + // or other errors during enter which cannot be fixed by running 1.37 + // any annotation processors. 1.38 + if (log.unrecoverableError) 1.39 return this; 1.40 } 1.41
2.1 --- a/src/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java Mon Aug 23 17:00:07 2010 +0100 2.2 +++ b/src/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java Mon Aug 23 11:56:53 2010 -0700 2.3 @@ -795,6 +795,13 @@ 2.4 final JavaCompiler compiler; 2.5 /** The log for the round. */ 2.6 final Log log; 2.7 + /** The number of warnings in the previous round. */ 2.8 + final int priorWarnings; 2.9 + 2.10 + /** The ASTs to be compiled. */ 2.11 + List<JCCompilationUnit> roots; 2.12 + /** The classes to be compiler that have were generated. */ 2.13 + Map<String, JavaFileObject> genClassFiles; 2.14 2.15 /** The set of annotations to be processed this round. */ 2.16 Set<TypeElement> annotationsPresent; 2.17 @@ -803,10 +810,12 @@ 2.18 /** The set of package-info files to be processed this round. */ 2.19 List<PackageSymbol> packageInfoFiles; 2.20 2.21 - /** Create a round. */ 2.22 - Round(Context context, int number) { 2.23 + /** Create a round (common code). */ 2.24 + private Round(Context context, int number, int priorWarnings) { 2.25 this.context = context; 2.26 this.number = number; 2.27 + this.priorWarnings = priorWarnings; 2.28 + 2.29 compiler = JavaCompiler.instance(context); 2.30 log = Log.instance(context); 2.31 2.32 @@ -814,15 +823,86 @@ 2.33 JavacProcessingEnvironment.this.context = context; 2.34 2.35 // the following will be populated as needed 2.36 - annotationsPresent = new LinkedHashSet<TypeElement>(); 2.37 topLevelClasses = List.nil(); 2.38 packageInfoFiles = List.nil(); 2.39 } 2.40 2.41 + /** Create the first round. */ 2.42 + Round(Context context, List<JCCompilationUnit> roots, List<ClassSymbol> classSymbols) { 2.43 + this(context, 1, 0); 2.44 + this.roots = roots; 2.45 + genClassFiles = new HashMap<String,JavaFileObject>(); 2.46 + 2.47 + compiler.todo.clear(); // free the compiler's resources 2.48 + 2.49 + // The reverse() in the following line is to maintain behavioural 2.50 + // compatibility with the previous revision of the code. Strictly speaking, 2.51 + // it should not be necessary, but a javah golden file test fails without it. 2.52 + topLevelClasses = 2.53 + getTopLevelClasses(roots).prependList(classSymbols.reverse()); 2.54 + 2.55 + packageInfoFiles = getPackageInfoFiles(roots); 2.56 + 2.57 + findAnnotationsPresent(); 2.58 + } 2.59 + 2.60 + /** Create a new round. */ 2.61 + private Round(Round prev, 2.62 + Set<JavaFileObject> newSourceFiles, Map<String,JavaFileObject> newClassFiles) 2.63 + throws IOException { 2.64 + this(prev.nextContext(), prev.number+1, prev.compiler.log.nwarnings); 2.65 + this.genClassFiles = prev.genClassFiles; 2.66 + 2.67 + updateProcessingState(); 2.68 + 2.69 + List<JCCompilationUnit> parsedFiles = compiler.parseFiles(newSourceFiles); 2.70 + roots = cleanTrees(prev.roots).appendList(parsedFiles); 2.71 + 2.72 + // Check for errors after parsing 2.73 + if (unrecoverableError()) 2.74 + return; 2.75 + 2.76 + enterClassFiles(genClassFiles); 2.77 + List<ClassSymbol> newClasses = enterClassFiles(newClassFiles); 2.78 + genClassFiles.putAll(newClassFiles); 2.79 + enterTrees(roots); 2.80 + 2.81 + if (unrecoverableError()) 2.82 + return; 2.83 + 2.84 + topLevelClasses = join( 2.85 + getTopLevelClasses(parsedFiles), 2.86 + getTopLevelClassesFromClasses(newClasses)); 2.87 + 2.88 + packageInfoFiles = join( 2.89 + getPackageInfoFiles(parsedFiles), 2.90 + getPackageInfoFilesFromClasses(newClasses)); 2.91 + 2.92 + findAnnotationsPresent(); 2.93 + } 2.94 + 2.95 /** Create the next round to be used. */ 2.96 - Round next() { 2.97 - compiler.close(false); 2.98 - return new Round(contextForNextRound(), number + 1); 2.99 + Round next(Set<JavaFileObject> newSourceFiles, Map<String, JavaFileObject> newClassFiles) 2.100 + throws IOException { 2.101 + try { 2.102 + return new Round(this, newSourceFiles, newClassFiles); 2.103 + } finally { 2.104 + compiler.close(false); 2.105 + } 2.106 + } 2.107 + 2.108 + /** Create the compiler to be used for the final compilation. */ 2.109 + JavaCompiler finalCompiler(boolean errorStatus) { 2.110 + try { 2.111 + JavaCompiler c = JavaCompiler.instance(nextContext()); 2.112 + if (errorStatus) { 2.113 + c.log.nwarnings += priorWarnings + compiler.log.nwarnings; 2.114 + c.log.nerrors += compiler.log.nerrors; 2.115 + } 2.116 + return c; 2.117 + } finally { 2.118 + compiler.close(false); 2.119 + } 2.120 } 2.121 2.122 /** Return the number of errors found so far in this round. 2.123 @@ -839,12 +919,16 @@ 2.124 2.125 /** Return whether or not an unrecoverable error has occurred. */ 2.126 boolean unrecoverableError() { 2.127 - return log.unrecoverableError; 2.128 + return log.unrecoverableError 2.129 + || messager.errorRaised() 2.130 + || (werror && log.nwarnings > 0) 2.131 + || (fatalErrors && log.nerrors > 0); 2.132 } 2.133 2.134 /** Find the set of annotations present in the set of top level 2.135 - * classes and package info files to be processed this round. */ 2.136 - void findAnnotationsPresent(ComputeAnnotationSet annotationComputer) { 2.137 + * classes and package info files to be processed this round. */ 2.138 + void findAnnotationsPresent() { 2.139 + ComputeAnnotationSet annotationComputer = new ComputeAnnotationSet(elementUtils); 2.140 // Use annotation processing to compute the set of annotations present 2.141 annotationsPresent = new LinkedHashSet<TypeElement>(); 2.142 for (ClassSymbol classSym : topLevelClasses) 2.143 @@ -853,26 +937,13 @@ 2.144 annotationComputer.scan(pkgSym, annotationsPresent); 2.145 } 2.146 2.147 - /** 2.148 - * Parse the latest set of generated source files created by the filer. 2.149 - */ 2.150 - List<JCCompilationUnit> parseNewSourceFiles() 2.151 - throws IOException { 2.152 - List<JavaFileObject> fileObjects = List.nil(); 2.153 - for (JavaFileObject jfo : filer.getGeneratedSourceFileObjects() ) { 2.154 - fileObjects = fileObjects.prepend(jfo); 2.155 - } 2.156 - 2.157 - return compiler.parseFiles(fileObjects); 2.158 - } 2.159 - 2.160 - /** Enter the latest set of generated class files created by the filer. */ 2.161 - List<ClassSymbol> enterNewClassFiles() { 2.162 + /** Enter a set of generated class files. */ 2.163 + List<ClassSymbol> enterClassFiles(Map<String, JavaFileObject> classFiles) { 2.164 ClassReader reader = ClassReader.instance(context); 2.165 Names names = Names.instance(context); 2.166 List<ClassSymbol> list = List.nil(); 2.167 2.168 - for (Map.Entry<String,JavaFileObject> entry : filer.getGeneratedClasses().entrySet()) { 2.169 + for (Map.Entry<String,JavaFileObject> entry : classFiles.entrySet()) { 2.170 Name name = names.fromString(entry.getKey()); 2.171 JavaFileObject file = entry.getValue(); 2.172 if (file.getKind() != JavaFileObject.Kind.CLASS) 2.173 @@ -900,11 +971,7 @@ 2.174 2.175 /** Run a processing round. */ 2.176 void run(boolean lastRound, boolean errorStatus) { 2.177 -// assert lastRound 2.178 -// ? (topLevelClasses.size() == 0 && annotationsPresent.size() == 0) 2.179 -// : (errorStatus == false); 2.180 -// 2.181 -// printRoundInfo(topLevelClasses, annotationsPresent, lastRound); 2.182 + printRoundInfo(lastRound); 2.183 2.184 TaskListener taskListener = context.get(TaskListener.class); 2.185 if (taskListener != null) 2.186 @@ -912,7 +979,6 @@ 2.187 2.188 try { 2.189 if (lastRound) { 2.190 - printRoundInfo(List.<ClassSymbol>nil(), Collections.<TypeElement>emptySet(), lastRound); 2.191 filer.setLastRound(true); 2.192 Set<Element> emptyRootElements = Collections.emptySet(); // immutable 2.193 RoundEnvironment renv = new JavacRoundEnvironment(true, 2.194 @@ -921,7 +987,6 @@ 2.195 JavacProcessingEnvironment.this); 2.196 discoveredProcs.iterator().runContributingProcs(renv); 2.197 } else { 2.198 - printRoundInfo(topLevelClasses, annotationsPresent, lastRound); 2.199 discoverAndRunProcs(context, annotationsPresent, topLevelClasses, packageInfoFiles); 2.200 } 2.201 } finally { 2.202 @@ -931,11 +996,7 @@ 2.203 } 2.204 2.205 /** Update the processing state for the current context. */ 2.206 - // Question: should this not be part of next()? 2.207 - // Note: Calling it from next() breaks some tests. There is an issue 2.208 - // whether the annotationComputer is using elementUtils with the 2.209 - // correct context. 2.210 - void updateProcessingState() { 2.211 + private void updateProcessingState() { 2.212 filer.newRound(context); 2.213 messager.newRound(context); 2.214 2.215 @@ -944,14 +1005,14 @@ 2.216 } 2.217 2.218 /** Print info about this round. */ 2.219 - private void printRoundInfo(List<ClassSymbol> topLevelClasses, 2.220 - Set<TypeElement> annotationsPresent, 2.221 - boolean lastRound) { 2.222 + private void printRoundInfo(boolean lastRound) { 2.223 if (printRounds || verbose) { 2.224 + List<ClassSymbol> tlc = lastRound ? List.<ClassSymbol>nil() : topLevelClasses; 2.225 + Set<TypeElement> ap = lastRound ? Collections.<TypeElement>emptySet() : annotationsPresent; 2.226 log.printNoteLines("x.print.rounds", 2.227 - (!lastRound ? number : number + 1), 2.228 - "{" + topLevelClasses.toString(", ") + "}", 2.229 - annotationsPresent, 2.230 + number, 2.231 + "{" + tlc.toString(", ") + "}", 2.232 + ap, 2.233 lastRound); 2.234 } 2.235 } 2.236 @@ -960,7 +1021,7 @@ 2.237 * Important values are propogated from round to round; 2.238 * other values are implicitly reset. 2.239 */ 2.240 - private Context contextForNextRound() { 2.241 + private Context nextContext() { 2.242 Context next = new Context(); 2.243 2.244 Options options = Options.instance(context); 2.245 @@ -1025,138 +1086,90 @@ 2.246 Iterable<? extends PackageSymbol> pckSymbols) 2.247 throws IOException { 2.248 2.249 + TaskListener taskListener = context.get(TaskListener.class); 2.250 log = Log.instance(context); 2.251 2.252 - Round round = new Round(context, 1); 2.253 - round.compiler.todo.clear(); // free the compiler's resources 2.254 - 2.255 - // The reverse() in the following line is to maintain behavioural 2.256 - // compatibility with the previous revision of the code. Strictly speaking, 2.257 - // it should not be necessary, but a javah golden file test fails without it. 2.258 - round.topLevelClasses = 2.259 - getTopLevelClasses(roots).prependList(classSymbols.reverse()); 2.260 - 2.261 - round.packageInfoFiles = getPackageInfoFiles(roots); 2.262 - 2.263 Set<PackageSymbol> specifiedPackages = new LinkedHashSet<PackageSymbol>(); 2.264 for (PackageSymbol psym : pckSymbols) 2.265 specifiedPackages.add(psym); 2.266 this.specifiedPackages = Collections.unmodifiableSet(specifiedPackages); 2.267 2.268 - ComputeAnnotationSet annotationComputer = new ComputeAnnotationSet(elementUtils); 2.269 - round.findAnnotationsPresent(annotationComputer); 2.270 + Round round = new Round(context, roots, classSymbols); 2.271 2.272 - boolean errorStatus = false; 2.273 - 2.274 - runAround: 2.275 - while (true) { 2.276 - if ((fatalErrors && round.errorCount() != 0) 2.277 - || (werror && round.warningCount() != 0)) { 2.278 - errorStatus = true; 2.279 - break runAround; 2.280 - } 2.281 - 2.282 + boolean errorStatus; 2.283 + boolean moreToDo; 2.284 + do { 2.285 + // Run processors for round n 2.286 round.run(false, false); 2.287 2.288 - /* 2.289 - * Processors for round n have run to completion. Prepare 2.290 - * for round (n+1) by checked for errors raised by 2.291 - * annotation processors and then checking for syntax 2.292 - * errors on any generated source files. 2.293 - */ 2.294 - if (messager.errorRaised()) { 2.295 + // Processors for round n have run to completion. 2.296 + // Check for errors and whether there is more work to do. 2.297 + errorStatus = round.unrecoverableError(); 2.298 + moreToDo = moreToDo(); 2.299 + 2.300 + // Set up next round. 2.301 + // Copy mutable collections returned from filer. 2.302 + round = round.next( 2.303 + new LinkedHashSet<JavaFileObject>(filer.getGeneratedSourceFileObjects()), 2.304 + new LinkedHashMap<String,JavaFileObject>(filer.getGeneratedClasses())); 2.305 + 2.306 + // Check for errors during setup. 2.307 + if (round.unrecoverableError()) 2.308 errorStatus = true; 2.309 - break runAround; 2.310 - } 2.311 2.312 - if (!moreToDo()) 2.313 - break runAround; // No new files 2.314 - 2.315 - round = round.next(); 2.316 - 2.317 - List<JCCompilationUnit> parsedFiles = round.parseNewSourceFiles(); 2.318 - roots = cleanTrees(roots).appendList(parsedFiles); 2.319 - 2.320 - // Check for errors after parsing 2.321 - if (round.unrecoverableError()) { 2.322 - errorStatus = true; 2.323 - break runAround; 2.324 - } 2.325 - 2.326 - List<ClassSymbol> newClasses = round.enterNewClassFiles(); 2.327 - round.enterTrees(roots); 2.328 - 2.329 - round.topLevelClasses = join( 2.330 - getTopLevelClasses(parsedFiles), 2.331 - getTopLevelClassesFromClasses(newClasses)); 2.332 - 2.333 - round.packageInfoFiles = join( 2.334 - getPackageInfoFiles(parsedFiles), 2.335 - getPackageInfoFilesFromClasses(newClasses)); 2.336 - 2.337 - round.findAnnotationsPresent(annotationComputer); 2.338 - round.updateProcessingState(); 2.339 - } 2.340 + } while (moreToDo && !errorStatus); 2.341 2.342 // run last round 2.343 round.run(true, errorStatus); 2.344 2.345 - // Add any sources generated during the last round to the set 2.346 - // of files to be compiled. 2.347 - if (moreToDo()) { 2.348 - List<JCCompilationUnit> parsedFiles = round.parseNewSourceFiles(); 2.349 - roots = cleanTrees(roots).appendList(parsedFiles); 2.350 - } 2.351 - 2.352 - // Set error status for any files compiled and generated in 2.353 - // the last round 2.354 - if (round.unrecoverableError() || (werror && round.warningCount() != 0)) 2.355 - errorStatus = true; 2.356 - 2.357 - round = round.next(); 2.358 - 2.359 filer.warnIfUnclosedFiles(); 2.360 warnIfUnmatchedOptions(); 2.361 2.362 - /* 2.363 - * If an annotation processor raises an error in a round, 2.364 - * that round runs to completion and one last round occurs. 2.365 - * The last round may also occur because no more source or 2.366 - * class files have been generated. Therefore, if an error 2.367 - * was raised on either of the last *two* rounds, the compile 2.368 - * should exit with a nonzero exit code. The current value of 2.369 - * errorStatus holds whether or not an error was raised on the 2.370 - * second to last round; errorRaised() gives the error status 2.371 - * of the last round. 2.372 - */ 2.373 - errorStatus = errorStatus || messager.errorRaised(); 2.374 + /* 2.375 + * If an annotation processor raises an error in a round, 2.376 + * that round runs to completion and one last round occurs. 2.377 + * The last round may also occur because no more source or 2.378 + * class files have been generated. Therefore, if an error 2.379 + * was raised on either of the last *two* rounds, the compile 2.380 + * should exit with a nonzero exit code. The current value of 2.381 + * errorStatus holds whether or not an error was raised on the 2.382 + * second to last round; errorRaised() gives the error status 2.383 + * of the last round. 2.384 + */ 2.385 + if (messager.errorRaised() 2.386 + || werror && round.warningCount() > 0 && round.errorCount() > 0) 2.387 + errorStatus = true; 2.388 + 2.389 + Set<JavaFileObject> newSourceFiles = 2.390 + new LinkedHashSet<JavaFileObject>(filer.getGeneratedSourceFileObjects()); 2.391 + roots = cleanTrees(round.roots); 2.392 + 2.393 + JavaCompiler compiler = round.finalCompiler(errorStatus); 2.394 + 2.395 + if (newSourceFiles.size() > 0) 2.396 + roots = roots.appendList(compiler.parseFiles(newSourceFiles)); 2.397 + 2.398 + errorStatus = errorStatus || (compiler.errorCount() > 0); 2.399 2.400 // Free resources 2.401 this.close(); 2.402 2.403 - TaskListener taskListener = this.context.get(TaskListener.class); 2.404 if (taskListener != null) 2.405 taskListener.finished(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING)); 2.406 2.407 - JavaCompiler compiler; 2.408 - 2.409 if (errorStatus) { 2.410 - compiler = round.compiler; 2.411 - compiler.log.nwarnings += messager.warningCount(); 2.412 - compiler.log.nerrors += messager.errorCount(); 2.413 if (compiler.errorCount() == 0) 2.414 compiler.log.nerrors++; 2.415 - } else if (procOnly && !foundTypeProcessors) { 2.416 - compiler = round.compiler; 2.417 + return compiler; 2.418 + } 2.419 + 2.420 + if (procOnly && !foundTypeProcessors) { 2.421 compiler.todo.clear(); 2.422 - } else { // Final compilation 2.423 - round = round.next(); 2.424 - round.updateProcessingState(); 2.425 - compiler = round.compiler; 2.426 + } else { 2.427 if (procOnly && foundTypeProcessors) 2.428 compiler.shouldStopPolicy = CompileState.FLOW; 2.429 2.430 - compiler.enterTrees(cleanTrees(roots)); 2.431 + compiler.enterTrees(roots); 2.432 } 2.433 2.434 return compiler; 2.435 @@ -1185,7 +1198,9 @@ 2.436 for (JCCompilationUnit unit : units) { 2.437 for (JCTree node : unit.defs) { 2.438 if (node.getTag() == JCTree.CLASSDEF) { 2.439 - classes = classes.prepend(((JCClassDecl) node).sym); 2.440 + ClassSymbol sym = ((JCClassDecl) node).sym; 2.441 + assert sym != null; 2.442 + classes = classes.prepend(sym); 2.443 } 2.444 } 2.445 }
3.1 --- a/test/tools/javac/T6358024.java Mon Aug 23 17:00:07 2010 +0100 3.2 +++ b/test/tools/javac/T6358024.java Mon Aug 23 11:56:53 2010 -0700 3.3 @@ -60,7 +60,7 @@ 3.4 new Option[] { new XOption("-XprintRounds"), 3.5 new Option("-processorpath", "."), 3.6 new Option("-processor", self) }, 3.7 - 11); 3.8 + 12); 3.9 } 3.10 3.11 static void test(JavacFileManager fm, JavaFileObject f, Option[] opts, int expect) throws Throwable {
4.1 --- a/test/tools/javac/T6403466.out Mon Aug 23 17:00:07 2010 +0100 4.2 +++ b/test/tools/javac/T6403466.out Mon Aug 23 11:56:53 2010 -0700 4.3 @@ -13,6 +13,10 @@ 4.4 Finished TaskEvent[ENTER,T6403466Wrapper.java,null] 4.5 Started TaskEvent[ANNOTATION_PROCESSING_ROUND,null,null] 4.6 Finished TaskEvent[ANNOTATION_PROCESSING_ROUND,null,null] 4.7 +Started TaskEvent[ENTER,T6403466.java,null] 4.8 +Started TaskEvent[ENTER,T6403466Wrapper.java,null] 4.9 +Finished TaskEvent[ENTER,T6403466.java,null] 4.10 +Finished TaskEvent[ENTER,T6403466Wrapper.java,null] 4.11 Started TaskEvent[ANNOTATION_PROCESSING_ROUND,null,null] 4.12 Finished TaskEvent[ANNOTATION_PROCESSING_ROUND,null,null] 4.13 Finished TaskEvent[ANNOTATION_PROCESSING,null,null]
5.1 --- a/test/tools/javac/processing/filer/TestLastRound.out Mon Aug 23 17:00:07 2010 +0100 5.2 +++ b/test/tools/javac/processing/filer/TestLastRound.out Mon Aug 23 11:56:53 2010 -0700 5.3 @@ -1,3 +1,4 @@ 5.4 - compiler.warn.proc.file.create.last.round: LastRound.java 5.5 - compiler.err.warnings.and.werror 5.6 1 error 5.7 +1 warning