src/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java

changeset 642
6b95dd682538
parent 623
6318230cdb82
child 663
eb7c263aab73
equal deleted inserted replaced
641:594b3c2ef585 642:6b95dd682538
793 final Context context; 793 final Context context;
794 /** The compiler for the round. */ 794 /** The compiler for the round. */
795 final JavaCompiler compiler; 795 final JavaCompiler compiler;
796 /** The log for the round. */ 796 /** The log for the round. */
797 final Log log; 797 final Log log;
798 /** The number of warnings in the previous round. */
799 final int priorWarnings;
800
801 /** The ASTs to be compiled. */
802 List<JCCompilationUnit> roots;
803 /** The classes to be compiler that have were generated. */
804 Map<String, JavaFileObject> genClassFiles;
798 805
799 /** The set of annotations to be processed this round. */ 806 /** The set of annotations to be processed this round. */
800 Set<TypeElement> annotationsPresent; 807 Set<TypeElement> annotationsPresent;
801 /** The set of top level classes to be processed this round. */ 808 /** The set of top level classes to be processed this round. */
802 List<ClassSymbol> topLevelClasses; 809 List<ClassSymbol> topLevelClasses;
803 /** The set of package-info files to be processed this round. */ 810 /** The set of package-info files to be processed this round. */
804 List<PackageSymbol> packageInfoFiles; 811 List<PackageSymbol> packageInfoFiles;
805 812
806 /** Create a round. */ 813 /** Create a round (common code). */
807 Round(Context context, int number) { 814 private Round(Context context, int number, int priorWarnings) {
808 this.context = context; 815 this.context = context;
809 this.number = number; 816 this.number = number;
817 this.priorWarnings = priorWarnings;
818
810 compiler = JavaCompiler.instance(context); 819 compiler = JavaCompiler.instance(context);
811 log = Log.instance(context); 820 log = Log.instance(context);
812 821
813 // the following is for the benefit of JavacProcessingEnvironment.getContext() 822 // the following is for the benefit of JavacProcessingEnvironment.getContext()
814 JavacProcessingEnvironment.this.context = context; 823 JavacProcessingEnvironment.this.context = context;
815 824
816 // the following will be populated as needed 825 // the following will be populated as needed
817 annotationsPresent = new LinkedHashSet<TypeElement>();
818 topLevelClasses = List.nil(); 826 topLevelClasses = List.nil();
819 packageInfoFiles = List.nil(); 827 packageInfoFiles = List.nil();
820 } 828 }
821 829
830 /** Create the first round. */
831 Round(Context context, List<JCCompilationUnit> roots, List<ClassSymbol> classSymbols) {
832 this(context, 1, 0);
833 this.roots = roots;
834 genClassFiles = new HashMap<String,JavaFileObject>();
835
836 compiler.todo.clear(); // free the compiler's resources
837
838 // The reverse() in the following line is to maintain behavioural
839 // compatibility with the previous revision of the code. Strictly speaking,
840 // it should not be necessary, but a javah golden file test fails without it.
841 topLevelClasses =
842 getTopLevelClasses(roots).prependList(classSymbols.reverse());
843
844 packageInfoFiles = getPackageInfoFiles(roots);
845
846 findAnnotationsPresent();
847 }
848
849 /** Create a new round. */
850 private Round(Round prev,
851 Set<JavaFileObject> newSourceFiles, Map<String,JavaFileObject> newClassFiles)
852 throws IOException {
853 this(prev.nextContext(), prev.number+1, prev.compiler.log.nwarnings);
854 this.genClassFiles = prev.genClassFiles;
855
856 updateProcessingState();
857
858 List<JCCompilationUnit> parsedFiles = compiler.parseFiles(newSourceFiles);
859 roots = cleanTrees(prev.roots).appendList(parsedFiles);
860
861 // Check for errors after parsing
862 if (unrecoverableError())
863 return;
864
865 enterClassFiles(genClassFiles);
866 List<ClassSymbol> newClasses = enterClassFiles(newClassFiles);
867 genClassFiles.putAll(newClassFiles);
868 enterTrees(roots);
869
870 if (unrecoverableError())
871 return;
872
873 topLevelClasses = join(
874 getTopLevelClasses(parsedFiles),
875 getTopLevelClassesFromClasses(newClasses));
876
877 packageInfoFiles = join(
878 getPackageInfoFiles(parsedFiles),
879 getPackageInfoFilesFromClasses(newClasses));
880
881 findAnnotationsPresent();
882 }
883
822 /** Create the next round to be used. */ 884 /** Create the next round to be used. */
823 Round next() { 885 Round next(Set<JavaFileObject> newSourceFiles, Map<String, JavaFileObject> newClassFiles)
824 compiler.close(false); 886 throws IOException {
825 return new Round(contextForNextRound(), number + 1); 887 try {
888 return new Round(this, newSourceFiles, newClassFiles);
889 } finally {
890 compiler.close(false);
891 }
892 }
893
894 /** Create the compiler to be used for the final compilation. */
895 JavaCompiler finalCompiler(boolean errorStatus) {
896 try {
897 JavaCompiler c = JavaCompiler.instance(nextContext());
898 if (errorStatus) {
899 c.log.nwarnings += priorWarnings + compiler.log.nwarnings;
900 c.log.nerrors += compiler.log.nerrors;
901 }
902 return c;
903 } finally {
904 compiler.close(false);
905 }
826 } 906 }
827 907
828 /** Return the number of errors found so far in this round. 908 /** Return the number of errors found so far in this round.
829 * This may include uncoverable errors, such as parse errors, 909 * This may include uncoverable errors, such as parse errors,
830 * and transient errors, such as missing symbols. */ 910 * and transient errors, such as missing symbols. */
837 return compiler.warningCount(); 917 return compiler.warningCount();
838 } 918 }
839 919
840 /** Return whether or not an unrecoverable error has occurred. */ 920 /** Return whether or not an unrecoverable error has occurred. */
841 boolean unrecoverableError() { 921 boolean unrecoverableError() {
842 return log.unrecoverableError; 922 return log.unrecoverableError
923 || messager.errorRaised()
924 || (werror && log.nwarnings > 0)
925 || (fatalErrors && log.nerrors > 0);
843 } 926 }
844 927
845 /** Find the set of annotations present in the set of top level 928 /** Find the set of annotations present in the set of top level
846 * classes and package info files to be processed this round. */ 929 * classes and package info files to be processed this round. */
847 void findAnnotationsPresent(ComputeAnnotationSet annotationComputer) { 930 void findAnnotationsPresent() {
931 ComputeAnnotationSet annotationComputer = new ComputeAnnotationSet(elementUtils);
848 // Use annotation processing to compute the set of annotations present 932 // Use annotation processing to compute the set of annotations present
849 annotationsPresent = new LinkedHashSet<TypeElement>(); 933 annotationsPresent = new LinkedHashSet<TypeElement>();
850 for (ClassSymbol classSym : topLevelClasses) 934 for (ClassSymbol classSym : topLevelClasses)
851 annotationComputer.scan(classSym, annotationsPresent); 935 annotationComputer.scan(classSym, annotationsPresent);
852 for (PackageSymbol pkgSym : packageInfoFiles) 936 for (PackageSymbol pkgSym : packageInfoFiles)
853 annotationComputer.scan(pkgSym, annotationsPresent); 937 annotationComputer.scan(pkgSym, annotationsPresent);
854 } 938 }
855 939
856 /** 940 /** Enter a set of generated class files. */
857 * Parse the latest set of generated source files created by the filer. 941 List<ClassSymbol> enterClassFiles(Map<String, JavaFileObject> classFiles) {
858 */
859 List<JCCompilationUnit> parseNewSourceFiles()
860 throws IOException {
861 List<JavaFileObject> fileObjects = List.nil();
862 for (JavaFileObject jfo : filer.getGeneratedSourceFileObjects() ) {
863 fileObjects = fileObjects.prepend(jfo);
864 }
865
866 return compiler.parseFiles(fileObjects);
867 }
868
869 /** Enter the latest set of generated class files created by the filer. */
870 List<ClassSymbol> enterNewClassFiles() {
871 ClassReader reader = ClassReader.instance(context); 942 ClassReader reader = ClassReader.instance(context);
872 Names names = Names.instance(context); 943 Names names = Names.instance(context);
873 List<ClassSymbol> list = List.nil(); 944 List<ClassSymbol> list = List.nil();
874 945
875 for (Map.Entry<String,JavaFileObject> entry : filer.getGeneratedClasses().entrySet()) { 946 for (Map.Entry<String,JavaFileObject> entry : classFiles.entrySet()) {
876 Name name = names.fromString(entry.getKey()); 947 Name name = names.fromString(entry.getKey());
877 JavaFileObject file = entry.getValue(); 948 JavaFileObject file = entry.getValue();
878 if (file.getKind() != JavaFileObject.Kind.CLASS) 949 if (file.getKind() != JavaFileObject.Kind.CLASS)
879 throw new AssertionError(file); 950 throw new AssertionError(file);
880 ClassSymbol cs; 951 ClassSymbol cs;
898 compiler.enterTrees(roots); 969 compiler.enterTrees(roots);
899 } 970 }
900 971
901 /** Run a processing round. */ 972 /** Run a processing round. */
902 void run(boolean lastRound, boolean errorStatus) { 973 void run(boolean lastRound, boolean errorStatus) {
903 // assert lastRound 974 printRoundInfo(lastRound);
904 // ? (topLevelClasses.size() == 0 && annotationsPresent.size() == 0)
905 // : (errorStatus == false);
906 //
907 // printRoundInfo(topLevelClasses, annotationsPresent, lastRound);
908 975
909 TaskListener taskListener = context.get(TaskListener.class); 976 TaskListener taskListener = context.get(TaskListener.class);
910 if (taskListener != null) 977 if (taskListener != null)
911 taskListener.started(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING_ROUND)); 978 taskListener.started(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING_ROUND));
912 979
913 try { 980 try {
914 if (lastRound) { 981 if (lastRound) {
915 printRoundInfo(List.<ClassSymbol>nil(), Collections.<TypeElement>emptySet(), lastRound);
916 filer.setLastRound(true); 982 filer.setLastRound(true);
917 Set<Element> emptyRootElements = Collections.emptySet(); // immutable 983 Set<Element> emptyRootElements = Collections.emptySet(); // immutable
918 RoundEnvironment renv = new JavacRoundEnvironment(true, 984 RoundEnvironment renv = new JavacRoundEnvironment(true,
919 errorStatus, 985 errorStatus,
920 emptyRootElements, 986 emptyRootElements,
921 JavacProcessingEnvironment.this); 987 JavacProcessingEnvironment.this);
922 discoveredProcs.iterator().runContributingProcs(renv); 988 discoveredProcs.iterator().runContributingProcs(renv);
923 } else { 989 } else {
924 printRoundInfo(topLevelClasses, annotationsPresent, lastRound);
925 discoverAndRunProcs(context, annotationsPresent, topLevelClasses, packageInfoFiles); 990 discoverAndRunProcs(context, annotationsPresent, topLevelClasses, packageInfoFiles);
926 } 991 }
927 } finally { 992 } finally {
928 if (taskListener != null) 993 if (taskListener != null)
929 taskListener.finished(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING_ROUND)); 994 taskListener.finished(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING_ROUND));
930 } 995 }
931 } 996 }
932 997
933 /** Update the processing state for the current context. */ 998 /** Update the processing state for the current context. */
934 // Question: should this not be part of next()? 999 private void updateProcessingState() {
935 // Note: Calling it from next() breaks some tests. There is an issue
936 // whether the annotationComputer is using elementUtils with the
937 // correct context.
938 void updateProcessingState() {
939 filer.newRound(context); 1000 filer.newRound(context);
940 messager.newRound(context); 1001 messager.newRound(context);
941 1002
942 elementUtils.setContext(context); 1003 elementUtils.setContext(context);
943 typeUtils.setContext(context); 1004 typeUtils.setContext(context);
944 } 1005 }
945 1006
946 /** Print info about this round. */ 1007 /** Print info about this round. */
947 private void printRoundInfo(List<ClassSymbol> topLevelClasses, 1008 private void printRoundInfo(boolean lastRound) {
948 Set<TypeElement> annotationsPresent,
949 boolean lastRound) {
950 if (printRounds || verbose) { 1009 if (printRounds || verbose) {
1010 List<ClassSymbol> tlc = lastRound ? List.<ClassSymbol>nil() : topLevelClasses;
1011 Set<TypeElement> ap = lastRound ? Collections.<TypeElement>emptySet() : annotationsPresent;
951 log.printNoteLines("x.print.rounds", 1012 log.printNoteLines("x.print.rounds",
952 (!lastRound ? number : number + 1), 1013 number,
953 "{" + topLevelClasses.toString(", ") + "}", 1014 "{" + tlc.toString(", ") + "}",
954 annotationsPresent, 1015 ap,
955 lastRound); 1016 lastRound);
956 } 1017 }
957 } 1018 }
958 1019
959 /** Get the context for the next round of processing. 1020 /** Get the context for the next round of processing.
960 * Important values are propogated from round to round; 1021 * Important values are propogated from round to round;
961 * other values are implicitly reset. 1022 * other values are implicitly reset.
962 */ 1023 */
963 private Context contextForNextRound() { 1024 private Context nextContext() {
964 Context next = new Context(); 1025 Context next = new Context();
965 1026
966 Options options = Options.instance(context); 1027 Options options = Options.instance(context);
967 assert options != null; 1028 assert options != null;
968 next.put(Options.optionsKey, options); 1029 next.put(Options.optionsKey, options);
1023 List<JCCompilationUnit> roots, 1084 List<JCCompilationUnit> roots,
1024 List<ClassSymbol> classSymbols, 1085 List<ClassSymbol> classSymbols,
1025 Iterable<? extends PackageSymbol> pckSymbols) 1086 Iterable<? extends PackageSymbol> pckSymbols)
1026 throws IOException { 1087 throws IOException {
1027 1088
1089 TaskListener taskListener = context.get(TaskListener.class);
1028 log = Log.instance(context); 1090 log = Log.instance(context);
1029
1030 Round round = new Round(context, 1);
1031 round.compiler.todo.clear(); // free the compiler's resources
1032
1033 // The reverse() in the following line is to maintain behavioural
1034 // compatibility with the previous revision of the code. Strictly speaking,
1035 // it should not be necessary, but a javah golden file test fails without it.
1036 round.topLevelClasses =
1037 getTopLevelClasses(roots).prependList(classSymbols.reverse());
1038
1039 round.packageInfoFiles = getPackageInfoFiles(roots);
1040 1091
1041 Set<PackageSymbol> specifiedPackages = new LinkedHashSet<PackageSymbol>(); 1092 Set<PackageSymbol> specifiedPackages = new LinkedHashSet<PackageSymbol>();
1042 for (PackageSymbol psym : pckSymbols) 1093 for (PackageSymbol psym : pckSymbols)
1043 specifiedPackages.add(psym); 1094 specifiedPackages.add(psym);
1044 this.specifiedPackages = Collections.unmodifiableSet(specifiedPackages); 1095 this.specifiedPackages = Collections.unmodifiableSet(specifiedPackages);
1045 1096
1046 ComputeAnnotationSet annotationComputer = new ComputeAnnotationSet(elementUtils); 1097 Round round = new Round(context, roots, classSymbols);
1047 round.findAnnotationsPresent(annotationComputer); 1098
1048 1099 boolean errorStatus;
1049 boolean errorStatus = false; 1100 boolean moreToDo;
1050 1101 do {
1051 runAround: 1102 // Run processors for round n
1052 while (true) { 1103 round.run(false, false);
1053 if ((fatalErrors && round.errorCount() != 0) 1104
1054 || (werror && round.warningCount() != 0)) { 1105 // Processors for round n have run to completion.
1106 // Check for errors and whether there is more work to do.
1107 errorStatus = round.unrecoverableError();
1108 moreToDo = moreToDo();
1109
1110 // Set up next round.
1111 // Copy mutable collections returned from filer.
1112 round = round.next(
1113 new LinkedHashSet<JavaFileObject>(filer.getGeneratedSourceFileObjects()),
1114 new LinkedHashMap<String,JavaFileObject>(filer.getGeneratedClasses()));
1115
1116 // Check for errors during setup.
1117 if (round.unrecoverableError())
1055 errorStatus = true; 1118 errorStatus = true;
1056 break runAround; 1119
1057 } 1120 } while (moreToDo && !errorStatus);
1058
1059 round.run(false, false);
1060
1061 /*
1062 * Processors for round n have run to completion. Prepare
1063 * for round (n+1) by checked for errors raised by
1064 * annotation processors and then checking for syntax
1065 * errors on any generated source files.
1066 */
1067 if (messager.errorRaised()) {
1068 errorStatus = true;
1069 break runAround;
1070 }
1071
1072 if (!moreToDo())
1073 break runAround; // No new files
1074
1075 round = round.next();
1076
1077 List<JCCompilationUnit> parsedFiles = round.parseNewSourceFiles();
1078 roots = cleanTrees(roots).appendList(parsedFiles);
1079
1080 // Check for errors after parsing
1081 if (round.unrecoverableError()) {
1082 errorStatus = true;
1083 break runAround;
1084 }
1085
1086 List<ClassSymbol> newClasses = round.enterNewClassFiles();
1087 round.enterTrees(roots);
1088
1089 round.topLevelClasses = join(
1090 getTopLevelClasses(parsedFiles),
1091 getTopLevelClassesFromClasses(newClasses));
1092
1093 round.packageInfoFiles = join(
1094 getPackageInfoFiles(parsedFiles),
1095 getPackageInfoFilesFromClasses(newClasses));
1096
1097 round.findAnnotationsPresent(annotationComputer);
1098 round.updateProcessingState();
1099 }
1100 1121
1101 // run last round 1122 // run last round
1102 round.run(true, errorStatus); 1123 round.run(true, errorStatus);
1103 1124
1104 // Add any sources generated during the last round to the set
1105 // of files to be compiled.
1106 if (moreToDo()) {
1107 List<JCCompilationUnit> parsedFiles = round.parseNewSourceFiles();
1108 roots = cleanTrees(roots).appendList(parsedFiles);
1109 }
1110
1111 // Set error status for any files compiled and generated in
1112 // the last round
1113 if (round.unrecoverableError() || (werror && round.warningCount() != 0))
1114 errorStatus = true;
1115
1116 round = round.next();
1117
1118 filer.warnIfUnclosedFiles(); 1125 filer.warnIfUnclosedFiles();
1119 warnIfUnmatchedOptions(); 1126 warnIfUnmatchedOptions();
1120 1127
1121 /* 1128 /*
1122 * If an annotation processor raises an error in a round, 1129 * If an annotation processor raises an error in a round,
1123 * that round runs to completion and one last round occurs. 1130 * that round runs to completion and one last round occurs.
1124 * The last round may also occur because no more source or 1131 * The last round may also occur because no more source or
1125 * class files have been generated. Therefore, if an error 1132 * class files have been generated. Therefore, if an error
1126 * was raised on either of the last *two* rounds, the compile 1133 * was raised on either of the last *two* rounds, the compile
1127 * should exit with a nonzero exit code. The current value of 1134 * should exit with a nonzero exit code. The current value of
1128 * errorStatus holds whether or not an error was raised on the 1135 * errorStatus holds whether or not an error was raised on the
1129 * second to last round; errorRaised() gives the error status 1136 * second to last round; errorRaised() gives the error status
1130 * of the last round. 1137 * of the last round.
1131 */ 1138 */
1132 errorStatus = errorStatus || messager.errorRaised(); 1139 if (messager.errorRaised()
1140 || werror && round.warningCount() > 0 && round.errorCount() > 0)
1141 errorStatus = true;
1142
1143 Set<JavaFileObject> newSourceFiles =
1144 new LinkedHashSet<JavaFileObject>(filer.getGeneratedSourceFileObjects());
1145 roots = cleanTrees(round.roots);
1146
1147 JavaCompiler compiler = round.finalCompiler(errorStatus);
1148
1149 if (newSourceFiles.size() > 0)
1150 roots = roots.appendList(compiler.parseFiles(newSourceFiles));
1151
1152 errorStatus = errorStatus || (compiler.errorCount() > 0);
1133 1153
1134 // Free resources 1154 // Free resources
1135 this.close(); 1155 this.close();
1136 1156
1137 TaskListener taskListener = this.context.get(TaskListener.class);
1138 if (taskListener != null) 1157 if (taskListener != null)
1139 taskListener.finished(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING)); 1158 taskListener.finished(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING));
1140 1159
1141 JavaCompiler compiler;
1142
1143 if (errorStatus) { 1160 if (errorStatus) {
1144 compiler = round.compiler;
1145 compiler.log.nwarnings += messager.warningCount();
1146 compiler.log.nerrors += messager.errorCount();
1147 if (compiler.errorCount() == 0) 1161 if (compiler.errorCount() == 0)
1148 compiler.log.nerrors++; 1162 compiler.log.nerrors++;
1149 } else if (procOnly && !foundTypeProcessors) { 1163 return compiler;
1150 compiler = round.compiler; 1164 }
1165
1166 if (procOnly && !foundTypeProcessors) {
1151 compiler.todo.clear(); 1167 compiler.todo.clear();
1152 } else { // Final compilation 1168 } else {
1153 round = round.next();
1154 round.updateProcessingState();
1155 compiler = round.compiler;
1156 if (procOnly && foundTypeProcessors) 1169 if (procOnly && foundTypeProcessors)
1157 compiler.shouldStopPolicy = CompileState.FLOW; 1170 compiler.shouldStopPolicy = CompileState.FLOW;
1158 1171
1159 compiler.enterTrees(cleanTrees(roots)); 1172 compiler.enterTrees(roots);
1160 } 1173 }
1161 1174
1162 return compiler; 1175 return compiler;
1163 } 1176 }
1164 1177
1183 private List<ClassSymbol> getTopLevelClasses(List<? extends JCCompilationUnit> units) { 1196 private List<ClassSymbol> getTopLevelClasses(List<? extends JCCompilationUnit> units) {
1184 List<ClassSymbol> classes = List.nil(); 1197 List<ClassSymbol> classes = List.nil();
1185 for (JCCompilationUnit unit : units) { 1198 for (JCCompilationUnit unit : units) {
1186 for (JCTree node : unit.defs) { 1199 for (JCTree node : unit.defs) {
1187 if (node.getTag() == JCTree.CLASSDEF) { 1200 if (node.getTag() == JCTree.CLASSDEF) {
1188 classes = classes.prepend(((JCClassDecl) node).sym); 1201 ClassSymbol sym = ((JCClassDecl) node).sym;
1202 assert sym != null;
1203 classes = classes.prepend(sym);
1189 } 1204 }
1190 } 1205 }
1191 } 1206 }
1192 return classes.reverse(); 1207 return classes.reverse();
1193 } 1208 }

mercurial