From 10ab92b5d9eb852ccac7295d8017203e7efd7449 Mon Sep 17 00:00:00 2001 From: Danish Nawab Date: Mon, 24 Aug 2026 11:06:20 +0200 Subject: [PATCH 1/4] Support JDK27: end positions moved from EndPosTable to JCTree.endpos JDK27 removed com.sun.tools.javac.tree.EndPosTable and stores end positions directly on tree nodes (JDK-8372948). Lombok resolved that class in a static initializer and rethrew on failure, so the processor never initialized and every compilation on JDK27 failed. - Javac: probe for EndPosTable; when absent, use the no-arg DiagnosticPosition.getEndPosition() and write JCTree.endpos directly - CommentCatcher: skip setting genEndPos where the field no longer exists - new java27 parser factory: JDK27 dropped the keepEndPositions parameter from JavacParser and ParserFactory.newParser - DocCommentIntegrator: implement the new no-arg getEndPosition() - PrettyPrinter: drop an unused reflective getEndPosition lookup that would fail class init on JDK27 - add an opt-in test.javac27 target Fixes #4072 --- buildScripts/tests.ant.xml | 5 ++ .../lombok/delombok/DocCommentIntegrator.java | 7 ++ .../lombok/delombok/PrettyPrinter.java | 22 +------ .../sun/tools/javac/parser/JavacParser.java | 1 + src/utils/lombok/javac/CommentCatcher.java | 6 +- src/utils/lombok/javac/Javac.java | 64 ++++++++++++++---- .../javac/java27/CommentCollectingParser.java | 52 +++++++++++++++ .../CommentCollectingParserFactory.java | 66 +++++++++++++++++++ 8 files changed, 188 insertions(+), 35 deletions(-) create mode 100644 src/utils/lombok/javac/java27/CommentCollectingParser.java create mode 100644 src/utils/lombok/javac/java27/CommentCollectingParserFactory.java diff --git a/buildScripts/tests.ant.xml b/buildScripts/tests.ant.xml index 82292dc0..139ef0da 100644 --- a/buildScripts/tests.ant.xml +++ b/buildScripts/tests.ant.xml @@ -167,6 +167,11 @@ This buildfile is part of projectlombok.org. It takes care of compiling and runn + + + + + Running TestJavac on JVM${ant.java.version}, with the javac built into your VM distributon. diff --git a/src/delombok/lombok/delombok/DocCommentIntegrator.java b/src/delombok/lombok/delombok/DocCommentIntegrator.java index dc71fcb4..8c2b4f6a 100644 --- a/src/delombok/lombok/delombok/DocCommentIntegrator.java +++ b/src/delombok/lombok/delombok/DocCommentIntegrator.java @@ -152,6 +152,13 @@ public class DocCommentIntegrator { return pos; } + @SuppressWarnings("unused") // As of JDK27 this is the only getEndPosition; EndPosTable is gone. + public int getEndPosition() { + int end = Javac.getTreeEndPos(node); + if (end > pos) return end; + return pos + docCommentContent_.length(); + } + @SuppressWarnings("unused") // We compile against very old versions of javac intentionally (to support old stuff), but this is the method for newer impls. public int getEndPosition(EndPosTable endPosTable) { int end = endPosTable == null ? 0 : endPosTable.getEndPos(node); diff --git a/src/delombok/lombok/delombok/PrettyPrinter.java b/src/delombok/lombok/delombok/PrettyPrinter.java index f379094a..be99c401 100644 --- a/src/delombok/lombok/delombok/PrettyPrinter.java +++ b/src/delombok/lombok/delombok/PrettyPrinter.java @@ -86,7 +86,6 @@ import com.sun.tools.javac.tree.JCTree.JCVariableDecl; import com.sun.tools.javac.tree.JCTree.JCWhileLoop; import com.sun.tools.javac.tree.JCTree.JCWildcard; import com.sun.tools.javac.tree.JCTree.TypeBoundKind; -import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.Name; import com.sun.tools.javac.util.Position; @@ -1615,17 +1614,10 @@ public class PrettyPrinter extends JCTree.Visitor { } } - private static final Method getExtendsClause, getEndPosition; + private static final Method getExtendsClause; static { getExtendsClause = getMethod(JCClassDecl.class, "getExtendsClause", new Class[0]); - - if (getJavaCompilerVersion() < 8) { - getEndPosition = getMethod(DiagnosticPosition.class, "getEndPosition", java.util.Map.class); - } else { - getEndPosition = getMethod(DiagnosticPosition.class, "getEndPosition", "com.sun.tools.javac.tree.EndPosTable"); - } - Permit.setAccessible(getEndPosition); } private static Method getMethod(Class clazz, String name, Class... paramTypes) { @@ -1636,18 +1628,6 @@ public class PrettyPrinter extends JCTree.Visitor { } } - private static Method getMethod(Class clazz, String name, String... paramTypes) { - try { - Class[] c = new Class[paramTypes.length]; - for (int i = 0; i < paramTypes.length; i++) c[i] = Class.forName(paramTypes[i]); - return Permit.getMethod(clazz, name, c); - } catch (NoSuchMethodException e) { - throw sneakyThrow(e); - } catch (ClassNotFoundException e) { - throw sneakyThrow(e); - } - } - public static JCTree getExtendsClause(JCClassDecl decl) { return (JCTree) Permit.invokeSneaky(getExtendsClause, decl); } diff --git a/src/stubs/com/sun/tools/javac/parser/JavacParser.java b/src/stubs/com/sun/tools/javac/parser/JavacParser.java index 4f1f3380..f328556e 100644 --- a/src/stubs/com/sun/tools/javac/parser/JavacParser.java +++ b/src/stubs/com/sun/tools/javac/parser/JavacParser.java @@ -6,6 +6,7 @@ package com.sun.tools.javac.parser; import com.sun.tools.javac.tree.JCTree; public class JavacParser { + // Up to javac26 the last boolean is keepEndPositions; as of javac27 (JDK-8372948) it is parseModuleInfo. protected JavacParser(ParserFactory fac, Lexer S, boolean keepDocComments, boolean keepLineMap, boolean keepEndPositions) { } diff --git a/src/utils/lombok/javac/CommentCatcher.java b/src/utils/lombok/javac/CommentCatcher.java index 90266c26..c6cab588 100644 --- a/src/utils/lombok/javac/CommentCatcher.java +++ b/src/utils/lombok/javac/CommentCatcher.java @@ -44,7 +44,8 @@ public class CommentCatcher { setInCompiler(compiler, context); compiler.keepComments = true; - compiler.genEndPos = true; + // genEndPos no longer exists as of javac27. + if (!Javac.endPosStoredOnTree()) compiler.genEndPos = true; return new CommentCatcher(compiler); } @@ -105,6 +106,9 @@ public class CommentCatcher { parserFactory = Class.forName("lombok.javac.java7.CommentCollectingParserFactory"); } else if (javaCompilerVersion == 8) { parserFactory = Class.forName("lombok.javac.java8.CommentCollectingParserFactory"); + } else if (Javac.endPosStoredOnTree()) { + // JDK-8372948 also reshaped JavacParser's constructors. + parserFactory = Class.forName("lombok.javac.java27.CommentCollectingParserFactory"); } else { parserFactory = Class.forName("lombok.javac.java9.CommentCollectingParserFactory"); } diff --git a/src/utils/lombok/javac/Javac.java b/src/utils/lombok/javac/Javac.java index 840c32ee..fc4724ce 100644 --- a/src/utils/lombok/javac/Javac.java +++ b/src/utils/lombok/javac/Javac.java @@ -213,37 +213,51 @@ public class Javac { private static final Method getExtendsClause, getEndPosition, storeEnd; + /** As of javac27 (JDK-8372948), end positions live in {@code JCTree.endpos} and {@code EndPosTable} no longer exists. */ + private static final boolean ENDPOS_ON_TREE; + static { getExtendsClause = getMethod(JCClassDecl.class, "getExtendsClause", new Class[0]); if (getJavaCompilerVersion() < 8) { + ENDPOS_ON_TREE = false; getEndPosition = getMethod(DiagnosticPosition.class, "getEndPosition", java.util.Map.class); storeEnd = getMethod(java.util.Map.class, "put", Object.class, Object.class); } else { - getEndPosition = getMethod(DiagnosticPosition.class, "getEndPosition", "com.sun.tools.javac.tree.EndPosTable"); - Method storeEndMethodTemp; Class endPosTable; try { endPosTable = Class.forName("com.sun.tools.javac.tree.EndPosTable"); } catch (ClassNotFoundException ex) { - throw sneakyThrow(ex); + endPosTable = null; } - try { - storeEndMethodTemp = Permit.getMethod(endPosTable, "storeEnd", JCTree.class, int.class); - } catch (NoSuchMethodException e) { + ENDPOS_ON_TREE = endPosTable == null; + if (ENDPOS_ON_TREE) { + getEndPosition = getMethod(DiagnosticPosition.class, "getEndPosition", new Class[0]); + storeEnd = null; + } else { + getEndPosition = getMethod(DiagnosticPosition.class, "getEndPosition", endPosTable); + Method storeEndMethodTemp; try { - endPosTable = Class.forName("com.sun.tools.javac.parser.JavacParser$AbstractEndPosTable"); storeEndMethodTemp = Permit.getMethod(endPosTable, "storeEnd", JCTree.class, int.class); - } catch (NoSuchMethodException ex) { - throw sneakyThrow(ex); - } catch (ClassNotFoundException ex) { - throw sneakyThrow(ex); + } catch (NoSuchMethodException e) { + try { + endPosTable = Class.forName("com.sun.tools.javac.parser.JavacParser$AbstractEndPosTable"); + storeEndMethodTemp = Permit.getMethod(endPosTable, "storeEnd", JCTree.class, int.class); + } catch (NoSuchMethodException ex) { + throw sneakyThrow(ex); + } catch (ClassNotFoundException ex) { + throw sneakyThrow(ex); + } } + storeEnd = storeEndMethodTemp; } - storeEnd = storeEndMethodTemp; } Permit.setAccessible(getEndPosition); - Permit.setAccessible(storeEnd); + if (storeEnd != null) Permit.setAccessible(storeEnd); + } + + static boolean endPosStoredOnTree() { + return ENDPOS_ON_TREE; } private static Method getMethod(Class clazz, String name, Class... paramTypes) { @@ -423,6 +437,7 @@ public class Javac { public static int getEndPosition(DiagnosticPosition pos, JCCompilationUnit top) { try { + if (ENDPOS_ON_TREE) return (Integer) getEndPosition.invoke(pos); Object endPositions = JCCOMPILATIONUNIT_ENDPOSITIONS.get(top); return (Integer) getEndPosition.invoke(pos, endPositions); } catch (IllegalAccessException e) { @@ -432,8 +447,22 @@ public class Javac { } } + /** Returns -1 if this javac has no {@code JCTree.endpos} field (pre-27). */ + public static int getTreeEndPos(JCTree tree) { + if (JCTREE_ENDPOS == null) return -1; + try { + return JCTREE_ENDPOS.getInt(tree); + } catch (IllegalAccessException e) { + throw sneakyThrow(e); + } + } + public static void storeEnd(JCTree tree, int pos, JCCompilationUnit top) { try { + if (ENDPOS_ON_TREE) { + if (JCTREE_ENDPOS != null) JCTREE_ENDPOS.setInt(tree, pos); + return; + } Object endPositions = JCCOMPILATIONUNIT_ENDPOSITIONS.get(top); if (endPositions == null) return; storeEnd.invoke(endPositions, tree, pos); @@ -513,6 +542,15 @@ public class Javac { } } + private static final Field JCTREE_ENDPOS; + static { + Field f = null; + try { + f = Permit.getField(JCTree.class, "endpos"); + } catch (NoSuchFieldException e) {} + JCTREE_ENDPOS = f; + } + private static final Field JCCOMPILATIONUNIT_ENDPOSITIONS, JCCOMPILATIONUNIT_DOCCOMMENTS; static { Field f = null; diff --git a/src/utils/lombok/javac/java27/CommentCollectingParser.java b/src/utils/lombok/javac/java27/CommentCollectingParser.java new file mode 100644 index 00000000..01c37aae --- /dev/null +++ b/src/utils/lombok/javac/java27/CommentCollectingParser.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2026 The Project Lombok Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package lombok.javac.java27; + +import static lombok.javac.CommentCatcher.JCCompilationUnit_comments; +import static lombok.javac.CommentCatcher.JCCompilationUnit_textBlockStarts; + +import lombok.javac.java8.CommentCollectingScanner; + +import com.sun.tools.javac.parser.JavacParser; +import com.sun.tools.javac.parser.Lexer; +import com.sun.tools.javac.parser.ParserFactory; +import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; + +class CommentCollectingParser extends JavacParser { + private final Lexer lexer; + + // The stub constructor's last param is named keepEndPositions, but on javac27 this descriptor resolves to the constructor ending in parseModuleInfo. + protected CommentCollectingParser(ParserFactory fac, Lexer S, + boolean keepDocComments, boolean keepLineMap, boolean parseModuleInfo) { + super(fac, S, keepDocComments, keepLineMap, parseModuleInfo); + lexer = S; + } + + public JCCompilationUnit parseCompilationUnit() { + JCCompilationUnit result = super.parseCompilationUnit(); + if (lexer instanceof CommentCollectingScanner) { + JCCompilationUnit_comments.set(result, ((CommentCollectingScanner) lexer).getComments()); + JCCompilationUnit_textBlockStarts.set(result, ((CommentCollectingScanner) lexer).getTextBlockStarts()); + } + return result; + } +} diff --git a/src/utils/lombok/javac/java27/CommentCollectingParserFactory.java b/src/utils/lombok/javac/java27/CommentCollectingParserFactory.java new file mode 100644 index 00000000..7e0c1acf --- /dev/null +++ b/src/utils/lombok/javac/java27/CommentCollectingParserFactory.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2026 The Project Lombok Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package lombok.javac.java27; + +import java.lang.reflect.Field; + +import com.sun.tools.javac.main.JavaCompiler; +import com.sun.tools.javac.parser.JavacParser; +import com.sun.tools.javac.parser.Lexer; +import com.sun.tools.javac.parser.ParserFactory; +import com.sun.tools.javac.parser.ScannerFactory; +import com.sun.tools.javac.util.Context; + +import lombok.permit.Permit; + +public class CommentCollectingParserFactory extends ParserFactory { + private final Context context; + + static Context.Key key() { + return parserFactoryKey; + } + + protected CommentCollectingParserFactory(Context context) { + super(context); + this.context = context; + } + + public JavacParser newParser(CharSequence input, boolean keepDocComments, boolean keepLineMap, boolean parseModuleInfo) { + ScannerFactory scannerFactory = ScannerFactory.instance(context); + Lexer lexer = scannerFactory.newScanner(input, true); + Object x = new CommentCollectingParser(this, lexer, true, keepLineMap, parseModuleInfo); + return (JavacParser) x; + // CCP is based on a stub which extends nothing, but at runtime the stub is replaced with + //javac's JavacParser. Either way this will work out. + } + + public static void setInCompiler(JavaCompiler compiler, Context context) { + context.put(CommentCollectingParserFactory.key(), (ParserFactory) null); + Field field; + try { + field = Permit.getField(JavaCompiler.class, "parserFactory"); + field.set(compiler, new CommentCollectingParserFactory(context)); + } catch (Exception e) { + throw new IllegalStateException("Could not set comment sensitive parser in the compiler", e); + } + } +} From 25eae29093410cba53e3f91e2da4ba1fbf1953f4 Mon Sep 17 00:00:00 2001 From: Danish Nawab Date: Wed, 26 Aug 2026 16:15:38 +0200 Subject: [PATCH 2/4] Add Danish Nawab to AUTHORS --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index a0130bc5..38e032c6 100755 --- a/AUTHORS +++ b/AUTHORS @@ -11,6 +11,7 @@ Christian Nüssgens Christian Schlichtherle Christian Sterzl Christoph Dreis +Danish Nawab DaveLaw Dave Brosius Dawid Rusin From 40cdde815038a4bddc2bc31afce80c4c62e75e67 Mon Sep 17 00:00:00 2001 From: Danish Nawab Date: Wed, 26 Aug 2026 16:37:07 +0200 Subject: [PATCH 3/4] [changelog] JDK27 support --- doc/changelog.markdown | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/changelog.markdown b/doc/changelog.markdown index af8894f1..52e30b90 100644 --- a/doc/changelog.markdown +++ b/doc/changelog.markdown @@ -2,6 +2,7 @@ Lombok Changelog ---------------- ### v1.18.47 "Edgy Guinea Pig" +* PLATFORM: JDK27 support added [#4072](https://github.com/projectlombok/lombok/issues/4072). * BUGFIX: `@SneakyThrows` usage on JDK26 no longer results in class files that require `lombok.jar` to be on the runtime classpath (which should not be neccessary). [#4040](https://github.com/projectlombok/lombok/issues/4022). * FEATURE: New [config key](https://projectlombok.org/features/configuration) `lombok.checkReturnValueAnnotation` (values: `none`, `lombok`; default: `none`) lets lombok generate `@lombok.CheckReturnValue` on generated methods where the return value should not be ignored, such as `@With` methods and `@Builder.build()`. A future lombok release may flip the default to `lombok`. [#4013](https://github.com/projectlombok/lombok/pull/4013). * PROMOTION: `@SuperBuilder` has been promoted to the main package. Otherwise, no changes have been made to the annotation. The old experimental annotation will remain for a few versions, at which point it will be marked as a deprecated annotation. Eventually it'll be removed. If you had `lombok.config` configuration for this annotation, the configuration keys for this feature have been renamed. From 1a23dad52c01e7b18892573549841e36d6e4abde Mon Sep 17 00:00:00 2001 From: Reinier Zwitserloot Date: Fri, 28 Aug 2026 01:02:56 +0200 Subject: [PATCH 4/4] [review][refactor] No meaningful changes: Improved comments, javadoc, and cleaned up some code. --- .../lombok/delombok/DocCommentIntegrator.java | 6 +- .../sun/tools/javac/main/JavaCompiler.java | 32 +++--- .../sun/tools/javac/parser/JavacParser.java | 9 +- src/utils/lombok/javac/CommentCatcher.java | 9 +- src/utils/lombok/javac/Javac.java | 105 ++++-------------- src/utils/lombok/permit/Permit.java | 8 ++ 6 files changed, 62 insertions(+), 107 deletions(-) diff --git a/src/delombok/lombok/delombok/DocCommentIntegrator.java b/src/delombok/lombok/delombok/DocCommentIntegrator.java index 8c2b4f6a..a792f8f9 100644 --- a/src/delombok/lombok/delombok/DocCommentIntegrator.java +++ b/src/delombok/lombok/delombok/DocCommentIntegrator.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2025 The Project Lombok Authors. + * Copyright (C) 2009-2026 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -152,14 +152,14 @@ public class DocCommentIntegrator { return pos; } - @SuppressWarnings("unused") // As of JDK27 this is the only getEndPosition; EndPosTable is gone. + @SuppressWarnings("unused") // We compile against very old versions of javac intentionally (to support old stuff), but this is the method for javac 27+ public int getEndPosition() { int end = Javac.getTreeEndPos(node); if (end > pos) return end; return pos + docCommentContent_.length(); } - @SuppressWarnings("unused") // We compile against very old versions of javac intentionally (to support old stuff), but this is the method for newer impls. + @SuppressWarnings("unused") // We compile against very old versions of javac intentionally (to support old stuff), but this is the method for javac 9(?)-26. public int getEndPosition(EndPosTable endPosTable) { int end = endPosTable == null ? 0 : endPosTable.getEndPos(node); if (end > pos) return end; diff --git a/src/stubs/com/sun/tools/javac/main/JavaCompiler.java b/src/stubs/com/sun/tools/javac/main/JavaCompiler.java index d0e7b38f..3a96e59c 100644 --- a/src/stubs/com/sun/tools/javac/main/JavaCompiler.java +++ b/src/stubs/com/sun/tools/javac/main/JavaCompiler.java @@ -14,24 +14,20 @@ import com.sun.tools.javac.util.List; import com.sun.tools.javac.comp.Todo; public class JavaCompiler { - // Shared by JDK6-9 - public boolean keepComments; - public boolean genEndPos; - public Todo todo; + /* JDK [6,) */ public boolean keepComments; + /* JDK [6,26] */ public boolean genEndPos; // JDK27+: Removed; end pos are stored on nodes themselves + /* JDK [6,) */ public Todo todo; - public JavaCompiler(Context context) {} - public int errorCount() { return 0; } - public static String version() { return ""; } - public JCCompilationUnit parse(String fileName) throws IOException { return null; } - public List enterTrees(List roots) {return null;} + /* JDK [6,) */ public JavaCompiler(Context context) {} + /* JDK [6,) */ public int errorCount() { return 0; } + /* JDK [6,) */ public static String version() { return ""; } + /* JDK [6,) */ public JCCompilationUnit parse(String fileName) throws IOException { return null; } + /* JDK [6,) */ public List enterTrees(List roots) {return null;} - //JDK up to 8 - public void initProcessAnnotations(Iterable processors) throws IOException {} - public JavaCompiler processAnnotations(List roots, List classnames) {return this;} - - // JDK 9 - public void initProcessAnnotations(Iterable processors, Collection initialFiles, Collection initialClassNames) {} - public void processAnnotations(List roots, Collection classnames) {} - public void close() {} - public List initModules(List roots) { return null; } + /* JDK [6,8] */ public void initProcessAnnotations(Iterable processors) throws IOException {} + /* JDK [9,) */ public void initProcessAnnotations(Iterable processors, Collection initialFiles, Collection initialClassNames) {} + /* JDK [6,8] */ public JavaCompiler processAnnotations(List roots, List classnames) {return this;} + /* JDK [9,) */ public void processAnnotations(List roots, Collection classnames) {} + /* JDK [9,) */ public void close() {} + /* JDK [9,) */ public List initModules(List roots) { return null; } } diff --git a/src/stubs/com/sun/tools/javac/parser/JavacParser.java b/src/stubs/com/sun/tools/javac/parser/JavacParser.java index f328556e..d3faa3db 100644 --- a/src/stubs/com/sun/tools/javac/parser/JavacParser.java +++ b/src/stubs/com/sun/tools/javac/parser/JavacParser.java @@ -7,9 +7,16 @@ import com.sun.tools.javac.tree.JCTree; public class JavacParser { // Up to javac26 the last boolean is keepEndPositions; as of javac27 (JDK-8372948) it is parseModuleInfo. - protected JavacParser(ParserFactory fac, Lexer S, boolean keepDocComments, boolean keepLineMap, boolean keepEndPositions) { + // Javac -9(?) has: +// protected JavacParser(ParserFactory fac, Lexer S, boolean keepDocComments, boolean keepLineMap, boolean keepEndPositions) + // Javac 27+ has: +// protected JavacParser(ParserFactory fac, Lexer S, boolean keepDocComments, boolean keepLineMap, boolean parseModuleInfo) + // These signatures are identical. + + protected JavacParser(ParserFactory fac, Lexer S, boolean keepDocComments, boolean keepLineMap, boolean varied) { } + // Javac 10(?)-26. protected JavacParser(ParserFactory fac, Lexer S, boolean keepDocComments, boolean keepLineMap, boolean keepEndPositions, boolean parseModuleInfo) { } diff --git a/src/utils/lombok/javac/CommentCatcher.java b/src/utils/lombok/javac/CommentCatcher.java index c6cab588..11d1c93f 100644 --- a/src/utils/lombok/javac/CommentCatcher.java +++ b/src/utils/lombok/javac/CommentCatcher.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2019 The Project Lombok Authors. + * Copyright (C) 2011-2026 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -106,11 +106,12 @@ public class CommentCatcher { parserFactory = Class.forName("lombok.javac.java7.CommentCollectingParserFactory"); } else if (javaCompilerVersion == 8) { parserFactory = Class.forName("lombok.javac.java8.CommentCollectingParserFactory"); - } else if (Javac.endPosStoredOnTree()) { + } else if (!Javac.endPosStoredOnTree()) { + parserFactory = Class.forName("lombok.javac.java9.CommentCollectingParserFactory"); + } else { + assert javaCompilerVersion > 26; // JDK-8372948 also reshaped JavacParser's constructors. parserFactory = Class.forName("lombok.javac.java27.CommentCollectingParserFactory"); - } else { - parserFactory = Class.forName("lombok.javac.java9.CommentCollectingParserFactory"); } Permit.getMethod(parserFactory, "setInCompiler", JavaCompiler.class, Context.class).invoke(null, compiler, context); } catch (InvocationTargetException e) { diff --git a/src/utils/lombok/javac/Javac.java b/src/utils/lombok/javac/Javac.java index fc4724ce..134d86dd 100644 --- a/src/utils/lombok/javac/Javac.java +++ b/src/utils/lombok/javac/Javac.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2025 The Project Lombok Authors. + * Copyright (C) 2009-2026 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -125,15 +125,7 @@ public class Javac { return version; } - private static final Class DOCCOMMENTTABLE_CLASS; - - static { - Class c = null; - try { - c = Class.forName("com.sun.tools.javac.tree.DocCommentTable"); - } catch (Throwable ignore) {} - DOCCOMMENTTABLE_CLASS = c; - } + private static final Class DOCCOMMENTTABLE_CLASS = Permit.permissiveGetClass("com.sun.tools.javac.tree.DocCommentTable"); public static boolean instanceOfDocCommentTable(Object o) { return DOCCOMMENTTABLE_CLASS != null && DOCCOMMENTTABLE_CLASS.isInstance(o); @@ -213,7 +205,10 @@ public class Javac { private static final Method getExtendsClause, getEndPosition, storeEnd; - /** As of javac27 (JDK-8372948), end positions live in {@code JCTree.endpos} and {@code EndPosTable} no longer exists. */ + /** + * Do JCTree objects track their own endpos (true) or are they stored in a separate EndPos table (false)? + * As of javac27 (JDK-8372948), end positions live in {@code JCTree.endpos} and {@code EndPosTable} no longer exists. + */ private static final boolean ENDPOS_ON_TREE; static { @@ -224,25 +219,26 @@ public class Javac { getEndPosition = getMethod(DiagnosticPosition.class, "getEndPosition", java.util.Map.class); storeEnd = getMethod(java.util.Map.class, "put", Object.class, Object.class); } else { - Class endPosTable; + Class endPosTableType; try { - endPosTable = Class.forName("com.sun.tools.javac.tree.EndPosTable"); + endPosTableType = Class.forName("com.sun.tools.javac.tree.EndPosTable"); } catch (ClassNotFoundException ex) { - endPosTable = null; + endPosTableType = null; } - ENDPOS_ON_TREE = endPosTable == null; - if (ENDPOS_ON_TREE) { + if (endPosTableType == null) { + ENDPOS_ON_TREE = true; getEndPosition = getMethod(DiagnosticPosition.class, "getEndPosition", new Class[0]); storeEnd = null; } else { - getEndPosition = getMethod(DiagnosticPosition.class, "getEndPosition", endPosTable); + ENDPOS_ON_TREE = false; + getEndPosition = getMethod(DiagnosticPosition.class, "getEndPosition", endPosTableType); Method storeEndMethodTemp; try { - storeEndMethodTemp = Permit.getMethod(endPosTable, "storeEnd", JCTree.class, int.class); + storeEndMethodTemp = Permit.getMethod(endPosTableType, "storeEnd", JCTree.class, int.class); } catch (NoSuchMethodException e) { try { - endPosTable = Class.forName("com.sun.tools.javac.parser.JavacParser$AbstractEndPosTable"); - storeEndMethodTemp = Permit.getMethod(endPosTable, "storeEnd", JCTree.class, int.class); + endPosTableType = Class.forName("com.sun.tools.javac.parser.JavacParser$AbstractEndPosTable"); + storeEndMethodTemp = Permit.getMethod(endPosTableType, "storeEnd", JCTree.class, int.class); } catch (NoSuchMethodException ex) { throw sneakyThrow(ex); } catch (ClassNotFoundException ex) { @@ -268,18 +264,6 @@ public class Javac { } } - private static Method getMethod(Class clazz, String name, String... paramTypes) { - try { - Class[] c = new Class[paramTypes.length]; - for (int i = 0; i < paramTypes.length; i++) c[i] = Class.forName(paramTypes[i]); - return Permit.getMethod(clazz, name, c); - } catch (NoSuchMethodException e) { - throw sneakyThrow(e); - } catch (ClassNotFoundException e) { - throw sneakyThrow(e); - } - } - /** * In some versions, the field's type is {@code JCTree}, in others it is {@code JCExpression}, which at the JVM level are not the same. */ @@ -447,7 +431,7 @@ public class Javac { } } - /** Returns -1 if this javac has no {@code JCTree.endpos} field (pre-27). */ + /** Returns -1 if this javac has no {@code JCTree.endpos} field (JDK[6,26]). */ public static int getTreeEndPos(JCTree tree) { if (JCTREE_ENDPOS == null) return -1; try { @@ -473,30 +457,9 @@ public class Javac { } } - private static final Class JC_VOID_TYPE, JC_NO_TYPE; - - static { - Class c = null; - try { - c = Class.forName("com.sun.tools.javac.code.Type$JCVoidType"); - } catch (Throwable ignore) {} - JC_VOID_TYPE = c; - c = null; - try { - c = Class.forName("com.sun.tools.javac.code.Type$JCNoType"); - } catch (Throwable ignore) {} - JC_NO_TYPE = c; - } - - private static final Field symtabVoidType = getFieldIfExists(Symtab.class, "voidType"); - - private static Field getFieldIfExists(Class c, String fieldName) { - try { - return Permit.getField(c, "voidType"); - } catch (Exception e) { - return null; - } - } + private static final Class JC_VOID_TYPE = Permit.permissiveGetClass("com.sun.tools.javac.code.Type$JCVoidType"); + private static final Class JC_NO_TYPE = Permit.permissiveGetClass("com.sun.tools.javac.code.Type$JCNoType"); + private static final Field symtabVoidType = Permit.permissiveGetField(Symtab.class, "voidType"); public static Type createVoidType(Symtab symbolTable, TypeTag tag) { if (symtabVoidType != null) try { @@ -542,29 +505,9 @@ public class Javac { } } - private static final Field JCTREE_ENDPOS; - static { - Field f = null; - try { - f = Permit.getField(JCTree.class, "endpos"); - } catch (NoSuchFieldException e) {} - JCTREE_ENDPOS = f; - } - - private static final Field JCCOMPILATIONUNIT_ENDPOSITIONS, JCCOMPILATIONUNIT_DOCCOMMENTS; - static { - Field f = null; - try { - f = Permit.getField(JCCompilationUnit.class, "endPositions"); - } catch (NoSuchFieldException e) {} - JCCOMPILATIONUNIT_ENDPOSITIONS = f; - - f = null; - try { - f = Permit.getField(JCCompilationUnit.class, "docComments"); - } catch (NoSuchFieldException e) {} - JCCOMPILATIONUNIT_DOCCOMMENTS = f; - } + private static final Field JCTREE_ENDPOS = Permit.permissiveGetField(JCTree.class, "endpos"); + private static final Field JCCOMPILATIONUNIT_ENDPOSITIONS = Permit.permissiveGetField(JCCompilationUnit.class, "endPositions"); + private static final Field JCCOMPILATIONUNIT_DOCCOMMENTS = Permit.permissiveGetField(JCCompilationUnit.class, "docComments"); static RuntimeException sneakyThrow(Throwable t) { if (t == null) throw new NullPointerException("t"); @@ -574,6 +517,6 @@ public class Javac { @SuppressWarnings("unchecked") private static void sneakyThrow0(Throwable t) throws T { - throw (T)t; + throw (T) t; } } diff --git a/src/utils/lombok/permit/Permit.java b/src/utils/lombok/permit/Permit.java index 2f1699bd..7184ad38 100644 --- a/src/utils/lombok/permit/Permit.java +++ b/src/utils/lombok/permit/Permit.java @@ -370,4 +370,12 @@ public class Permit { return null; } } + + public static Class permissiveGetClass(String fullyQualifiedTypeName) { + try { + return Class.forName(fullyQualifiedTypeName); + } catch (Exception e) { + return null; + } + } }