@@ -133,6 +133,48 @@ protected static class Messages {
133133
134134 // compiler.properties -> compiler.misc.verbose.*
135135 protected static final String [] MISC_PREFIXES = {"[" };
136+
137+ // Generic javac error prefix
138+ // TODO: In JDK 8, this generic prefix no longer seems to be in use for javac error messages, at least not in
139+ // the Java part of javac. Maybe in C sources? Does javac even use any native classes?
140+ protected static final String [] JAVAC_GENERIC_ERROR_PREFIXES = {"javac:" };
141+
142+ // Hard-coded, English-only error header in JVM native code, *not* followed by stack trace, but rather
143+ // by another text message
144+ protected static final String [] VM_INIT_ERROR_HEADERS = {"Error occurred during initialization of VM" };
145+
146+ // Hard-coded, English-only error header in class System, followed by stack trace
147+ protected static final String [] BOOT_LAYER_INIT_ERROR_HEADERS = {
148+ "Error occurred during initialization of boot layer"
149+ };
150+
151+ // javac.properties-> javac.msg.proc.annotation.uncaught.exception
152+ // (en JDK-8, ja JDK-8, zh_CN JDK-8, en JDK-21, ja JDK-21, zh_CN JDK-21, de JDK-21)
153+ protected static final String [] ANNOTATION_PROCESSING_ERROR_HEADERS = {
154+ "\n \n An annotation processor threw an uncaught exception.\n Consult the following stack trace for details.\n \n " ,
155+ "\n \n 注釈処理で捕捉されない例外がスローされました。\n 詳細は次のスタック・トレースで調査してください。\n \n " ,
156+ "\n \n 批注处理程序抛出未捕获的异常错误。\n 有关详细信息, 请参阅以下堆栈跟踪。\n \n " ,
157+ "\n \n An annotation processor threw an uncaught exception.\n Consult the following stack trace for details.\n \n " ,
158+ "\n \n 注釈処理で捕捉されない例外がスローされました。\n 詳細は次のスタックトレースで調査してください。\n \n " ,
159+ "\n \n 批注处理程序抛出未捕获的异常错误。\n 有关详细信息, 请参阅以下堆栈跟踪。\n \n " ,
160+ "\n \n Ein Annotationsprozessor hat eine nicht abgefangene Ausnahme ausgelöst.\n Details finden Sie im folgenden Stacktrace.\n \n "
161+ };
162+
163+ // javac.properties-> javac.msg.bug
164+ // (en JDK-8, ja JDK-8, zh_CN JDK-8, en JDK-9, ja JDK-9, zh_CN JDK-9, en JDK-21, ja JDK-21, zh_CN JDK-21, de
165+ // JDK-21)
166+ protected static final String [] FILE_A_BUG_ERROR_HEADERS = {
167+ "An exception has occurred in the compiler ({0}). Please file a bug at the Java Developer Connection (http://java.sun.com/webapps/bugreport) after checking the Bug Parade for duplicates. Include your program and the following diagnostic in your report. Thank you.\n " ,
168+ "コンパイラで例外が発生しました({0})。Bug Paradeで重複がないかをご確認のうえ、Java Developer Connection (http://java.sun.com/webapps/bugreport)でbugの登録をお願いいたします。レポートには、そのプログラムと下記の診断内容を含めてください。ご協力ありがとうございます。\n " ,
169+ "编译器 ({0}) 中出现异常错误。 如果在 Bug Parade 中没有找到该错误, 请在 Java Developer Connection (http://java.sun.com/webapps/bugreport) 中建立 Bug。请在报告中附上您的程序和以下诊断信息。谢谢。\n " ,
170+ "An exception has occurred in the compiler ({0}). Please file a bug against the Java compiler via the Java bug reporting page (http://bugreport.java.com) after checking the Bug Database (http://bugs.java.com) for duplicates. Include your program and the following diagnostic in your report. Thank you." ,
171+ "コンパイラで例外が発生しました({0})。Bug Database (http://bugs.java.com)で重複がないかをご確認のうえ、Java bugレポート・ページ(http://bugreport.java.com)でJavaコンパイラに対するbugの登録をお願いいたします。レポートには、そのプログラムと下記の診断内容を含めてください。ご協力ありがとうございます。" ,
172+ "编译器 ({0}) 中出现异常错误。如果在 Bug Database (http://bugs.java.com) 中没有找到该错误, 请通过 Java Bug 报告页 (http://bugreport.java.com) 建立该 Java 编译器 Bug。请在报告中附上您的程序和以下诊断信息。谢谢。" ,
173+ "An exception has occurred in the compiler ({0}). Please file a bug against the Java compiler via the Java bug reporting page (https://bugreport.java.com) after checking the Bug Database (https://bugs.java.com) for duplicates. Include your program, the following diagnostic, and the parameters passed to the Java compiler in your report. Thank you.\n " ,
174+ "コンパイラで例外が発生しました({0})。バグ・データベース(https://bugs.java.com)で重複がないかをご確認のうえ、Javaのバグ・レポート・ページ(https://bugreport.java.com)から、Javaコンパイラに対するバグの登録をお願いいたします。レポートには、該当のプログラム、次の診断内容、およびJavaコンパイラに渡されたパラメータをご入力ください。ご協力ありがとうございます。\n " ,
175+ "编译器 ({0}) 中出现异常错误。如果在 Bug Database (https://bugs.java.com) 中没有找到有关该错误的 Java 编译器 Bug,请通过 Java Bug 报告页 (https://bugreport.java.com) 提交 Java 编译器 Bug。请在报告中附上您的程序、以下诊断信息以及传递到 Java 编译器的参数。谢谢。\n " ,
176+ "Im Compiler ({0}) ist eine Ausnahme aufgetreten. Erstellen Sie auf der Java-Seite zum Melden von Bugs (https://bugreport.java.com) einen Bugbericht, nachdem Sie die Bugdatenbank (https://bugs.java.com) auf Duplikate geprüft haben. Geben Sie in Ihrem Bericht Ihr Programm, die folgende Diagnose und die Parameter an, die Sie dem Java-Compiler übergeben haben. Vielen Dank.\n "
177+ };
136178 }
137179
138180 private static final Object LOCK = new Object ();
@@ -630,10 +672,6 @@ private static CompilerResult compileInProcess0(Class<?> javacClass, String[] ar
630672 private static final Pattern STACK_TRACE_OTHER_LINE =
631673 Pattern .compile ("^(?:Caused by:\\ s.*|\\ s*at .*|\\ s*\\ .\\ .\\ .\\ s\\ d+\\ smore)$" );
632674
633- // Match generic javac errors with 'javac:' prefix, JMV init and boot layer init errors
634- private static final Pattern JAVAC_OR_JVM_ERROR =
635- Pattern .compile ("^(?:javac:|Error occurred during initialization of (?:boot layer|VM)).*" , Pattern .DOTALL );
636-
637675 /**
638676 * Parse the compiler output into a list of compiler messages
639677 *
@@ -692,73 +730,131 @@ static List<CompilerMessage> parseModernStream(int exitCode, BufferedReader inpu
692730 }
693731 }
694732
733+ String bufferContent = buffer .toString ();
734+ if (bufferContent .isEmpty ()) {
735+ return errors ;
736+ }
737+
695738 // javac output not detected by other parsing
696739 // maybe better to ignore only the summary and mark the rest as error
697- String bufferAsString = buffer .toString ();
698- if (!bufferAsString .isEmpty ()) {
699- if (JAVAC_OR_JVM_ERROR .matcher (bufferAsString ).matches ()) {
700- errors .add (new CompilerMessage (bufferAsString , ERROR ));
701- } else if (hasPointer ) {
702- // A compiler message remains in buffer at end of parse stream
703- errors .add (parseModernError (exitCode , bufferAsString ));
704- } else if (stackTraceLineCount > 0 ) {
705- // Extract stack trace from end of buffer
706- String [] lines = bufferAsString .split ("\\ R" );
707- int linesTotal = lines .length ;
708- buffer = new StringBuilder ();
709- int firstLine = linesTotal - stackTraceLineCount ;
710-
711- // Salvage Javac localized message 'javac.msg.bug' ("An exception has occurred in the
712- // compiler ... Please file a bug")
713- if (firstLine > 0 ) {
714- final String lineBeforeStackTrace = lines [firstLine - 1 ];
715- // One of those two URL substrings should always appear, without regard to JVM locale.
716- // TODO: Update, if the URL changes, last checked for JDK 21.
717- if (lineBeforeStackTrace .contains ("java.sun.com/webapps/bugreport" )
718- || lineBeforeStackTrace .contains ("bugreport.java.com" )) {
719- firstLine --;
720- }
721- }
722-
723- // Note: For message 'javac.msg.proc.annotation.uncaught.exception' ("An annotation processor
724- // threw an uncaught exception"), there is no locale-independent substring, and the header is
725- // also multi-line. It was discarded in the removed method 'parseAnnotationProcessorStream',
726- // and we continue to do so.
727-
728- for (int i = firstLine ; i < linesTotal ; i ++) {
729- buffer .append (lines [i ]).append (EOL );
730- }
731- errors .add (new CompilerMessage (buffer .toString (), ERROR ));
740+ String cleanedUpMessage ;
741+ if ((cleanedUpMessage = getJavacGenericError (bufferContent )) != null
742+ || (cleanedUpMessage = getBootLayerInitError (bufferContent )) != null
743+ || (cleanedUpMessage = getVMInitError (bufferContent )) != null
744+ || (cleanedUpMessage = getFileABugError (bufferContent )) != null
745+ || (cleanedUpMessage = getAnnotationProcessingError (bufferContent )) != null ) {
746+ errors .add (new CompilerMessage (cleanedUpMessage , ERROR ));
747+ } else if (hasPointer ) {
748+ // A compiler message remains in buffer at end of parse stream
749+ errors .add (parseModernError (exitCode , bufferContent ));
750+ } else if (stackTraceLineCount > 0 ) {
751+ // Extract stack trace from end of buffer
752+ String [] lines = bufferContent .split ("\\ R" );
753+ int linesTotal = lines .length ;
754+ buffer = new StringBuilder ();
755+ int firstLine = linesTotal - stackTraceLineCount ;
756+ for (int i = firstLine ; i < linesTotal ; i ++) {
757+ buffer .append (lines [i ]).append (EOL );
732758 }
759+ errors .add (new CompilerMessage (buffer .toString (), ERROR ));
733760 }
761+ // TODO: Add something like this? Check if it creates more value or more unnecessary log output in general.
762+ // else {
763+ // // Fall-back, if still no error or stack trace was recognised
764+ // errors.add(new CompilerMessage(bufferContent, exitCode == 0 ? OTHER : ERROR));
765+ // }
766+
734767 return errors ;
735768 }
736769
737- private static boolean isMisc (String line ) {
738- return startsWithPrefix (line , MISC_PREFIXES );
770+ private static boolean isMisc (String message ) {
771+ return startsWithPrefix (message , MISC_PREFIXES );
772+ }
773+
774+ private static boolean isNote (String message ) {
775+ return startsWithPrefix (message , NOTE_PREFIXES );
776+ }
777+
778+ private static boolean isWarning (String message ) {
779+ return startsWithPrefix (message , WARNING_PREFIXES );
780+ }
781+
782+ private static boolean isError (String message ) {
783+ return startsWithPrefix (message , ERROR_PREFIXES );
739784 }
740785
741- private static boolean isNote (String line ) {
742- return startsWithPrefix ( line , NOTE_PREFIXES );
786+ private static String getJavacGenericError (String message ) {
787+ return getTextStartingWithPrefix ( message , JAVAC_GENERIC_ERROR_PREFIXES );
743788 }
744789
745- private static boolean isWarning (String line ) {
746- return startsWithPrefix ( line , WARNING_PREFIXES );
790+ private static String getVMInitError (String message ) {
791+ return getTextStartingWithPrefix ( message , VM_INIT_ERROR_HEADERS );
747792 }
748793
749- private static boolean isError (String line ) {
750- return startsWithPrefix ( line , ERROR_PREFIXES );
794+ private static String getBootLayerInitError (String message ) {
795+ return getTextStartingWithPrefix ( message , BOOT_LAYER_INIT_ERROR_HEADERS );
751796 }
752797
753- private static boolean startsWithPrefix (String line , String [] prefixes ) {
798+ private static String getFileABugError (String message ) {
799+ return getTextStartingWithPrefix (message , FILE_A_BUG_ERROR_HEADERS );
800+ }
801+
802+ private static String getAnnotationProcessingError (String message ) {
803+ return getTextStartingWithPrefix (message , ANNOTATION_PROCESSING_ERROR_HEADERS );
804+ }
805+
806+ private static boolean startsWithPrefix (String text , String [] prefixes ) {
754807 for (String prefix : prefixes ) {
755- if (line .startsWith (prefix )) {
808+ if (text .startsWith (prefix )) {
756809 return true ;
757810 }
758811 }
759812 return false ;
760813 }
761814
815+ /**
816+ * Identify and return a known javac error message prefix and all subsequent text - usually a stack trace - from a
817+ * javac log output buffer.
818+ *
819+ * @param text log buffer to search for a javac error message stack trace
820+ * @param prefixes array of strings in Java properties format, e.g. {@code "some error with line feed\nand parameter
821+ * placeholders {0} and {1}"} in multiple locales (hence the array). For the search, the
822+ * placeholders may be represented by any text in the log buffer.
823+ * @return if found, the error message + all subsequent text, otherwise {@code null}
824+ */
825+ static String getTextStartingWithPrefix (String text , String [] prefixes ) {
826+ // Implementation note: The properties format with placeholders makes it easy to just copy & paste values from
827+ // the JDK compared to having to convert them to regular expressions with ".*" instead of "{0}" and quote
828+ // special regex characters. This makes the implementation of this method more complex and potentially a bit
829+ // slower, but hopefully is worth the effort for the convenience of future developers maintaining this class.
830+
831+ // Normalise line feeds to the UNIX format found in JDK multi-line messages in properties files
832+ text = text .replaceAll ("\\ R" , "\n " );
833+
834+ // Search text for given error message prefixes/headers, until the first match is found
835+ for (String prefix : prefixes ) {
836+ // Split properties message along placeholders like "{0}", "{1}" etc.
837+ String [] prefixParts = prefix .split ("\\ {\\ d+\\ }" );
838+ for (int i = 0 ; i < prefixParts .length ; i ++) {
839+ // Make sure to treat split sections as literal text in search regex by enclosing them in "\Q" and "\E".
840+ // See https://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html, search for "Quotation".
841+ prefixParts [i ] = "\\ Q" + prefixParts [i ] + "\\ E" ;
842+ }
843+ // Join message parts, replacing properties placeholders by ".*" regex ones
844+ prefix = String .join (".*?" , prefixParts );
845+ // Find prefix + subsequent text in Pattern.DOTALL mode, represented in regex as "(?s)".
846+ // This matches across line break boundaries.
847+ Matcher matcher = Pattern .compile ("(?s).*(" + prefix + ".*)" ).matcher (text );
848+ if (matcher .matches ()) {
849+ // Match -> cut off text before header and replace UNIX line breaks by platform ones again
850+ return matcher .replaceFirst ("$1" ).replaceAll ("\n " , EOL );
851+ }
852+ }
853+
854+ // No match
855+ return null ;
856+ }
857+
762858 /**
763859 * Construct a compiler message object from a compiler output line
764860 *
0 commit comments