mirror of
https://github.com/tiennm99/lombok.git
synced 2026-10-03 07:13:15 +00:00
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
This commit is contained in:
1 parent
af01b7a27b
commit
10ab92b5d9
8 files changed
+188
-35
No files matched your search
@@ -167,6 +167,11 @@ This buildfile is part of projectlombok.org. It takes care of compiling and runn
|
||||
<test.javacX version="26" />
|
||||
</target>
|
||||
|
||||
<!-- 27 is non-LTS, not yet released -->
|
||||
<target name="test.javac27" depends="test.compile, test.formatter.compile" description="runs the tests on your default VM, using javac27 as underlying compiler">
|
||||
<test.javacX version="27" />
|
||||
</target>
|
||||
|
||||
<target name="test.javacCurrent" depends="test.compile, test.formatter.compile" description="runs the tests on your default VM, using its javac as underlying compiler">
|
||||
<echo>Running TestJavac on JVM${ant.java.version}, with the javac built into your VM distributon.</echo>
|
||||
<junit haltonfailure="yes" fork="true" forkmode="once">
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
}
|
||||
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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<ParserFactory> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user