diff -r d02e088255a0 -r 88930a1c409e langtools/src/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java --- a/langtools/src/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java Thu Jul 29 19:27:11 2010 -0700 +++ b/langtools/src/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java Thu Jul 29 19:30:35 2010 -0700 @@ -68,7 +68,6 @@ import com.sun.tools.javac.util.Context; import com.sun.tools.javac.util.Convert; import com.sun.tools.javac.util.List; -import com.sun.tools.javac.util.ListBuffer; import com.sun.tools.javac.util.Log; import com.sun.tools.javac.util.JavacMessages; import com.sun.tools.javac.util.Name; @@ -759,7 +758,7 @@ } @Override - public Set scan(Element e, Set p) { + public Set scan(Element e, Set p) { for (AnnotationMirror annotationMirror : elements.getAllAnnotationMirrors(e) ) { Element e2 = annotationMirror.getAnnotationType().asElement(); @@ -784,6 +783,237 @@ } } + /** + * Helper object for a single round of annotation processing. + */ + class Round { + /** The round number. */ + final int number; + /** The context for the round. */ + final Context context; + /** The compiler for the round. */ + final JavaCompiler compiler; + /** The log for the round. */ + final Log log; + + /** The set of annotations to be processed this round. */ + Set annotationsPresent; + /** The set of top level classes to be processed this round. */ + List topLevelClasses; + /** The set of package-info files to be processed this round. */ + List packageInfoFiles; + + /** Create a round. */ + Round(Context context, int number) { + this.context = context; + this.number = number; + compiler = JavaCompiler.instance(context); + log = Log.instance(context); + + // the following is for the benefit of JavacProcessingEnvironment.getContext() + JavacProcessingEnvironment.this.context = context; + + // the following will be populated as needed + annotationsPresent = new LinkedHashSet(); + topLevelClasses = List.nil(); + packageInfoFiles = List.nil(); + } + + /** Create the next round to be used. */ + Round next() { + compiler.close(false); + return new Round(contextForNextRound(), number + 1); + } + + /** Return the number of errors found so far in this round. + * This may include uncoverable errors, such as parse errors, + * and transient errors, such as missing symbols. */ + int errorCount() { + return compiler.errorCount(); + } + + /** Return the number of warnings found so far in this round. */ + int warningCount() { + return compiler.warningCount(); + } + + /** Return whether or not an unrecoverable error has occurred. */ + boolean unrecoverableError() { + return log.unrecoverableError; + } + + /** Find the set of annotations present in the set of top level + * classes and package info files to be processed this round. */ + void findAnnotationsPresent(ComputeAnnotationSet annotationComputer) { + // Use annotation processing to compute the set of annotations present + annotationsPresent = new LinkedHashSet(); + for (ClassSymbol classSym : topLevelClasses) + annotationComputer.scan(classSym, annotationsPresent); + for (PackageSymbol pkgSym : packageInfoFiles) + annotationComputer.scan(pkgSym, annotationsPresent); + } + + /** + * Parse the latest set of generated source files created by the filer. + */ + List parseNewSourceFiles() + throws IOException { + List fileObjects = List.nil(); + for (JavaFileObject jfo : filer.getGeneratedSourceFileObjects() ) { + fileObjects = fileObjects.prepend(jfo); + } + + return compiler.parseFiles(fileObjects); + } + + /** Enter the latest set of generated class files created by the filer. */ + List enterNewClassFiles() { + ClassReader reader = ClassReader.instance(context); + Names names = Names.instance(context); + List list = List.nil(); + + for (Map.Entry entry : filer.getGeneratedClasses().entrySet()) { + Name name = names.fromString(entry.getKey()); + JavaFileObject file = entry.getValue(); + if (file.getKind() != JavaFileObject.Kind.CLASS) + throw new AssertionError(file); + ClassSymbol cs; + if (isPkgInfo(file, JavaFileObject.Kind.CLASS)) { + Name packageName = Convert.packagePart(name); + PackageSymbol p = reader.enterPackage(packageName); + if (p.package_info == null) + p.package_info = reader.enterClass(Convert.shortName(name), p); + cs = p.package_info; + if (cs.classfile == null) + cs.classfile = file; + } else + cs = reader.enterClass(name, file); + list = list.prepend(cs); + } + return list.reverse(); + } + + /** Enter a set of syntax trees. */ + void enterTrees(List roots) { + compiler.enterTrees(roots); + } + + /** Run a processing round. */ + void run(boolean lastRound, boolean errorStatus) { + assert lastRound + ? (topLevelClasses.size() == 0 && annotationsPresent.size() == 0) + : (errorStatus == false); + + printRoundInfo(topLevelClasses, annotationsPresent, lastRound); + + TaskListener taskListener = context.get(TaskListener.class); + if (taskListener != null) + taskListener.started(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING_ROUND)); + + try { + if (lastRound) { + filer.setLastRound(true); + Set emptyRootElements = Collections.emptySet(); // immutable + RoundEnvironment renv = new JavacRoundEnvironment(true, + errorStatus, + emptyRootElements, + JavacProcessingEnvironment.this); + discoveredProcs.iterator().runContributingProcs(renv); + } else { + discoverAndRunProcs(context, annotationsPresent, topLevelClasses, packageInfoFiles); + } + } finally { + if (taskListener != null) + taskListener.finished(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING_ROUND)); + } + } + + /** Update the processing state for the current context. */ + // Question: should this not be part of next()? + // Note: Calling it from next() breaks some tests. There is an issue + // whether the annotationComputer is using elementUtils with the + // correct context. + void updateProcessingState() { + filer.newRound(context); + messager.newRound(context); + + elementUtils.setContext(context); + typeUtils.setContext(context); + } + + /** Print info about this round. */ + private void printRoundInfo(List topLevelClasses, + Set annotationsPresent, + boolean lastRound) { + if (printRounds || verbose) { + log.printNoteLines("x.print.rounds", + number, + "{" + topLevelClasses.toString(", ") + "}", + annotationsPresent, + lastRound); + } + } + + /** Get the context for the next round of processing. + * Important values are propogated from round to round; + * other values are implicitly reset. + */ + private Context contextForNextRound() { + Context next = new Context(); + + Options options = Options.instance(context); + assert options != null; + next.put(Options.optionsKey, options); + + PrintWriter out = context.get(Log.outKey); + assert out != null; + next.put(Log.outKey, out); + + final boolean shareNames = true; + if (shareNames) { + Names names = Names.instance(context); + assert names != null; + next.put(Names.namesKey, names); + } + + DiagnosticListener dl = context.get(DiagnosticListener.class); + if (dl != null) + next.put(DiagnosticListener.class, dl); + + TaskListener tl = context.get(TaskListener.class); + if (tl != null) + next.put(TaskListener.class, tl); + + JavaFileManager jfm = context.get(JavaFileManager.class); + assert jfm != null; + next.put(JavaFileManager.class, jfm); + if (jfm instanceof JavacFileManager) { + ((JavacFileManager)jfm).setContext(next); + } + + Names names = Names.instance(context); + assert names != null; + next.put(Names.namesKey, names); + + Keywords keywords = Keywords.instance(context); + assert(keywords != null); + next.put(Keywords.keywordsKey, keywords); + + JavaCompiler oldCompiler = JavaCompiler.instance(context); + JavaCompiler nextCompiler = JavaCompiler.instance(next); + nextCompiler.initRound(oldCompiler); + + JavacTaskImpl task = context.get(JavacTaskImpl.class); + if (task != null) { + next.put(JavacTaskImpl.class, task); + task.updateContext(next); + } + + context.clear(); + return next; + } + } + // TODO: internal catch clauses?; catch and rethrow an annotation // processing error @@ -794,60 +1024,37 @@ throws IOException { log = Log.instance(context); - TaskListener taskListener = context.get(TaskListener.class); - JavaCompiler compiler = JavaCompiler.instance(context); - compiler.todo.clear(); // free the compiler's resources - - int round = 0; + Round round = new Round(context, 1); + round.compiler.todo.clear(); // free the compiler's resources - // List annotationsPresentInSource = collector.findAnnotations(roots); - List topLevelClasses = getTopLevelClasses(roots); + // The reverse() in the following line is to maintain behavioural + // compatibility with the previous revision of the code. Strictly speaking, + // it should not be necessary, but a javah golden file test fails without it. + round.topLevelClasses = + getTopLevelClasses(roots).prependList(classSymbols.reverse()); - for (ClassSymbol classSym : classSymbols) - topLevelClasses = topLevelClasses.prepend(classSym); - List packageInfoFiles = - getPackageInfoFiles(roots); + round.packageInfoFiles = getPackageInfoFiles(roots); Set specifiedPackages = new LinkedHashSet(); for (PackageSymbol psym : pckSymbols) specifiedPackages.add(psym); this.specifiedPackages = Collections.unmodifiableSet(specifiedPackages); - // Use annotation processing to compute the set of annotations present - Set annotationsPresent = new LinkedHashSet(); ComputeAnnotationSet annotationComputer = new ComputeAnnotationSet(elementUtils); - for (ClassSymbol classSym : topLevelClasses) - annotationComputer.scan(classSym, annotationsPresent); - for (PackageSymbol pkgSym : packageInfoFiles) - annotationComputer.scan(pkgSym, annotationsPresent); + round.findAnnotationsPresent(annotationComputer); - Context currentContext = context; - - int roundNumber = 0; boolean errorStatus = false; runAround: - while(true) { - if ((fatalErrors && compiler.errorCount() != 0) - || (werror && compiler.warningCount() != 0)) { + while (true) { + if ((fatalErrors && round.errorCount() != 0) + || (werror && round.warningCount() != 0)) { errorStatus = true; break runAround; } - this.context = currentContext; - roundNumber++; - printRoundInfo(roundNumber, topLevelClasses, annotationsPresent, false); - - if (taskListener != null) - taskListener.started(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING_ROUND)); - - try { - discoverAndRunProcs(currentContext, annotationsPresent, topLevelClasses, packageInfoFiles); - } finally { - if (taskListener != null) - taskListener.finished(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING_ROUND)); - } + round.run(false, false); /* * Processors for round n have run to completion. Prepare @@ -858,65 +1065,54 @@ if (messager.errorRaised()) { errorStatus = true; break runAround; - } else { - if (moreToDo()) { - // annotationsPresentInSource = List.nil(); - annotationsPresent = new LinkedHashSet(); - topLevelClasses = List.nil(); - packageInfoFiles = List.nil(); + } - compiler.close(false); - currentContext = contextForNextRound(currentContext, true); + if (!moreToDo()) + break runAround; // No new files - JavaFileManager fileManager = currentContext.get(JavaFileManager.class); + round = round.next(); - compiler = JavaCompiler.instance(currentContext); - List parsedFiles = sourcesToParsedFiles(compiler); - roots = cleanTrees(roots).appendList(parsedFiles); + List parsedFiles = round.parseNewSourceFiles(); + roots = cleanTrees(roots).appendList(parsedFiles); - // Check for errors after parsing - if (log.unrecoverableError) { - errorStatus = true; - break runAround; - } else { - List newClasses = enterNewClassFiles(currentContext); - compiler.enterTrees(roots); + // Check for errors after parsing + if (round.unrecoverableError()) { + errorStatus = true; + break runAround; + } + + List newClasses = round.enterNewClassFiles(); + round.enterTrees(roots); - // annotationsPresentInSource = - // collector.findAnnotations(parsedFiles); - ListBuffer tlc = new ListBuffer(); - tlc.appendList(getTopLevelClasses(parsedFiles)); - tlc.appendList(getTopLevelClassesFromClasses(newClasses)); - topLevelClasses = tlc.toList(); + round.topLevelClasses = join( + getTopLevelClasses(parsedFiles), + getTopLevelClassesFromClasses(newClasses)); - ListBuffer pif = new ListBuffer(); - pif.appendList(getPackageInfoFiles(parsedFiles)); - pif.appendList(getPackageInfoFilesFromClasses(newClasses)); - packageInfoFiles = pif.toList(); + round.packageInfoFiles = join( + getPackageInfoFiles(parsedFiles), + getPackageInfoFilesFromClasses(newClasses)); - annotationsPresent = new LinkedHashSet(); - for (ClassSymbol classSym : topLevelClasses) - annotationComputer.scan(classSym, annotationsPresent); - for (PackageSymbol pkgSym : packageInfoFiles) - annotationComputer.scan(pkgSym, annotationsPresent); + round.findAnnotationsPresent(annotationComputer); + round.updateProcessingState(); + } + + // run last round + round.run(true, errorStatus); - updateProcessingState(currentContext, false); - } - } else - break runAround; // No new files - } + // Add any sources generated during the last round to the set + // of files to be compiled. + if (moreToDo()) { + List parsedFiles = round.parseNewSourceFiles(); + roots = cleanTrees(roots).appendList(parsedFiles); } - roots = runLastRound(roundNumber, errorStatus, compiler, roots, taskListener); + // Set error status for any files compiled and generated in // the last round - if (log.unrecoverableError || (werror && compiler.warningCount() != 0)) + if (round.unrecoverableError() || (werror && round.warningCount() != 0)) errorStatus = true; - compiler.close(false); - currentContext = contextForNextRound(currentContext, true); - compiler = JavaCompiler.instance(currentContext); + round = round.next(); - filer.newRound(currentContext, true); filer.warnIfUnclosedFiles(); warnIfUnmatchedOptions(); @@ -933,142 +1129,43 @@ */ errorStatus = errorStatus || messager.errorRaised(); - // Free resources this.close(); + TaskListener taskListener = this.context.get(TaskListener.class); if (taskListener != null) taskListener.finished(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING)); + JavaCompiler compiler; + if (errorStatus) { + compiler = round.compiler; compiler.log.nwarnings += messager.warningCount(); compiler.log.nerrors += messager.errorCount(); if (compiler.errorCount() == 0) compiler.log.nerrors++; } else if (procOnly && !foundTypeProcessors) { + compiler = round.compiler; compiler.todo.clear(); } else { // Final compilation - compiler.close(false); - currentContext = contextForNextRound(currentContext, true); - this.context = currentContext; - updateProcessingState(currentContext, true); - compiler = JavaCompiler.instance(currentContext); + round = round.next(); + round.updateProcessingState(); + compiler = round.compiler; if (procOnly && foundTypeProcessors) compiler.shouldStopPolicy = CompileState.FLOW; - if (true) { - compiler.enterTrees(cleanTrees(roots)); - } else { - List fileObjects = List.nil(); - for (JCCompilationUnit unit : roots) - fileObjects = fileObjects.prepend(unit.getSourceFile()); - roots = null; - compiler.enterTrees(compiler.parseFiles(fileObjects.reverse())); - } + compiler.enterTrees(cleanTrees(roots)); } return compiler; } - private List sourcesToParsedFiles(JavaCompiler compiler) - throws IOException { - List fileObjects = List.nil(); - for (JavaFileObject jfo : filer.getGeneratedSourceFileObjects() ) { - fileObjects = fileObjects.prepend(jfo); - } - - return compiler.parseFiles(fileObjects); - } - - // Call the last round of annotation processing - private List runLastRound(int roundNumber, - boolean errorStatus, - JavaCompiler compiler, - List roots, - TaskListener taskListener) throws IOException { - roundNumber++; - List noTopLevelClasses = List.nil(); - Set noAnnotations = Collections.emptySet(); - printRoundInfo(roundNumber, noTopLevelClasses, noAnnotations, true); - - Set emptyRootElements = Collections.emptySet(); // immutable - RoundEnvironment renv = new JavacRoundEnvironment(true, - errorStatus, - emptyRootElements, - JavacProcessingEnvironment.this); - if (taskListener != null) - taskListener.started(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING_ROUND)); - - try { - discoveredProcs.iterator().runContributingProcs(renv); - } finally { - if (taskListener != null) - taskListener.finished(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING_ROUND)); - } - - // Add any sources generated during the last round to the set - // of files to be compiled. - if (moreToDo()) { - List parsedFiles = sourcesToParsedFiles(compiler); - roots = cleanTrees(roots).appendList(parsedFiles); - } - - return roots; - } - - private void updateProcessingState(Context currentContext, boolean lastRound) { - filer.newRound(currentContext, lastRound); - messager.newRound(currentContext); - - elementUtils.setContext(currentContext); - typeUtils.setContext(currentContext); - } - private void warnIfUnmatchedOptions() { if (!unmatchedProcessorOptions.isEmpty()) { log.warning("proc.unmatched.processor.options", unmatchedProcessorOptions.toString()); } } - private void printRoundInfo(int roundNumber, - List topLevelClasses, - Set annotationsPresent, - boolean lastRound) { - if (printRounds || verbose) { - log.printNoteLines("x.print.rounds", - roundNumber, - "{" + topLevelClasses.toString(", ") + "}", - annotationsPresent, - lastRound); - } - } - - private List enterNewClassFiles(Context currentContext) { - ClassReader reader = ClassReader.instance(currentContext); - Names names = Names.instance(currentContext); - List list = List.nil(); - - for (Map.Entry entry : filer.getGeneratedClasses().entrySet()) { - Name name = names.fromString(entry.getKey()); - JavaFileObject file = entry.getValue(); - if (file.getKind() != JavaFileObject.Kind.CLASS) - throw new AssertionError(file); - ClassSymbol cs; - if (isPkgInfo(file, JavaFileObject.Kind.CLASS)) { - Name packageName = Convert.packagePart(name); - PackageSymbol p = reader.enterPackage(packageName); - if (p.package_info == null) - p.package_info = reader.enterClass(Convert.shortName(name), p); - cs = p.package_info; - if (cs.classfile == null) - cs.classfile = file; - } else - cs = reader.enterClass(name, file); - list = list.prepend(cs); - } - return list.reverse(); - } - /** * Free resources related to annotation processing. */ @@ -1123,6 +1220,11 @@ return packages.reverse(); } + // avoid unchecked warning from use of varargs + private static List join(List list1, List list2) { + return list1.appendList(list2); + } + private boolean isPkgInfo(JavaFileObject fo, JavaFileObject.Kind kind) { return fo.isNameCompatible("package-info", kind); } @@ -1131,62 +1233,6 @@ return isPkgInfo(sym.classfile, JavaFileObject.Kind.CLASS) && (sym.packge().package_info == sym); } - private Context contextForNextRound(Context context, boolean shareNames) - throws IOException - { - Context next = new Context(); - - Options options = Options.instance(context); - assert options != null; - next.put(Options.optionsKey, options); - - PrintWriter out = context.get(Log.outKey); - assert out != null; - next.put(Log.outKey, out); - - if (shareNames) { - Names names = Names.instance(context); - assert names != null; - next.put(Names.namesKey, names); - } - - DiagnosticListener dl = context.get(DiagnosticListener.class); - if (dl != null) - next.put(DiagnosticListener.class, dl); - - TaskListener tl = context.get(TaskListener.class); - if (tl != null) - next.put(TaskListener.class, tl); - - JavaFileManager jfm = context.get(JavaFileManager.class); - assert jfm != null; - next.put(JavaFileManager.class, jfm); - if (jfm instanceof JavacFileManager) { - ((JavacFileManager)jfm).setContext(next); - } - - Names names = Names.instance(context); - assert names != null; - next.put(Names.namesKey, names); - - Keywords keywords = Keywords.instance(context); - assert(keywords != null); - next.put(Keywords.keywordsKey, keywords); - - JavaCompiler oldCompiler = JavaCompiler.instance(context); - JavaCompiler nextCompiler = JavaCompiler.instance(next); - nextCompiler.initRound(oldCompiler); - - JavacTaskImpl task = context.get(JavacTaskImpl.class); - if (task != null) { - next.put(JavacTaskImpl.class, task); - task.updateContext(next); - } - - context.clear(); - return next; - } - /* * Called retroactively to determine if a class loader was required, * after we have failed to create one.