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 |