@@ -133,6 +133,48 @@ protected static class Messages {
133
133
134
134
// compiler.properties -> compiler.misc.verbose.*
135
135
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
+ };
136
178
}
137
179
138
180
private static final Object LOCK = new Object ();
@@ -630,10 +672,6 @@ private static CompilerResult compileInProcess0(Class<?> javacClass, String[] ar
630
672
private static final Pattern STACK_TRACE_OTHER_LINE =
631
673
Pattern .compile ("^(?:Caused by:\\ s.*|\\ s*at .*|\\ s*\\ .\\ .\\ .\\ s\\ d+\\ smore)$" );
632
674
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
-
637
675
/**
638
676
* Parse the compiler output into a list of compiler messages
639
677
*
@@ -692,73 +730,131 @@ static List<CompilerMessage> parseModernStream(int exitCode, BufferedReader inpu
692
730
}
693
731
}
694
732
733
+ String bufferContent = buffer .toString ();
734
+ if (bufferContent .isEmpty ()) {
735
+ return errors ;
736
+ }
737
+
695
738
// javac output not detected by other parsing
696
739
// 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 );
732
758
}
759
+ errors .add (new CompilerMessage (buffer .toString (), ERROR ));
733
760
}
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
+
734
767
return errors ;
735
768
}
736
769
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 );
739
784
}
740
785
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 );
743
788
}
744
789
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 );
747
792
}
748
793
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 );
751
796
}
752
797
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 ) {
754
807
for (String prefix : prefixes ) {
755
- if (line .startsWith (prefix )) {
808
+ if (text .startsWith (prefix )) {
756
809
return true ;
757
810
}
758
811
}
759
812
return false ;
760
813
}
761
814
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
+
762
858
/**
763
859
* Construct a compiler message object from a compiler output line
764
860
*
0 commit comments