Skip to content

Commit 81e3fff

Browse files
committed
Support JDK 27 end positions (JDK-8372948)
JDK-8372948 removed EndPosTable and JCCompilationUnit.endPositions and re-ordered the four-argument ParserFactory.newParser. Trees now resolves the end-position API once through a method handle and all three call sites go through it; Trees.newParser passes the parser flags in the order each JDK generation expects.
1 parent b426577 commit 81e3fff

4 files changed

Lines changed: 79 additions & 6 deletions

File tree

‎palantir-java-format/src/main/java/com/palantir/javaformat/java/Formatter.java‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,8 +184,8 @@ public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOExcept
184184
};
185185
Log.instance(context).useSource(source);
186186
ParserFactory parserFactory = ParserFactory.instance(context);
187-
JavacParser parser = parserFactory.newParser(
188-
sourceText, /*keepDocComments=*/ true, /*keepEndPos=*/ true, /*keepLineMap=*/ true);
187+
JavacParser parser =
188+
Trees.newParser(parserFactory, sourceText, /*keepDocComments=*/ true, /*keepLineMap=*/ true);
189189
unit = parser.parseCompilationUnit();
190190
unit.sourcefile = source;
191191

‎palantir-java-format/src/main/java/com/palantir/javaformat/java/RemoveUnusedImports.java‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ private static RangeMap<Integer, String> buildReplacements(
232232
continue;
233233
}
234234
// delete the import
235-
int endPosition = importTree.getEndPosition(unit.endPositions);
235+
int endPosition = Trees.getEndPosition(importTree, unit);
236236
endPosition = Math.max(CharMatcher.isNot(' ').indexIn(contents, endPosition), endPosition);
237237
String sep = Newlines.guessLineSeparator(contents);
238238
if (endPosition + sep.length() < contents.length()

‎palantir-java-format/src/main/java/com/palantir/javaformat/java/StringWrapper.java‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -576,7 +576,7 @@ private static boolean noComments(String input, JCTree.JCCompilationUnit unit, T
576576
CharMatcher.whitespace().or(CharMatcher.anyOf("\"+"));
577577

578578
private static int getEndPosition(JCTree.JCCompilationUnit unit, Tree tree) {
579-
return ((JCTree) tree).getEndPosition(unit.endPositions);
579+
return Trees.getEndPosition(tree, unit);
580580
}
581581

582582
private static int getStartPosition(Tree tree) {

‎palantir-java-format/src/main/java/com/palantir/javaformat/java/Trees.java‎

Lines changed: 75 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,19 @@
2323
import com.sun.source.tree.ParenthesizedTree;
2424
import com.sun.source.tree.Tree;
2525
import com.sun.source.util.TreePath;
26+
import com.sun.tools.javac.parser.JavacParser;
27+
import com.sun.tools.javac.parser.ParserFactory;
2628
import com.sun.tools.javac.tree.JCTree;
29+
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
2730
import com.sun.tools.javac.tree.Pretty;
2831
import com.sun.tools.javac.tree.TreeInfo;
2932
import java.io.IOException;
3033
import java.io.UncheckedIOException;
34+
import java.lang.invoke.MethodHandle;
35+
import java.lang.invoke.MethodHandles;
36+
import java.lang.invoke.MethodType;
37+
import java.lang.invoke.VarHandle;
38+
import javax.annotation.Nullable;
3139
import javax.lang.model.element.Name;
3240

3341
/** Utilities for working with {@link Tree}s. */
@@ -44,8 +52,73 @@ static int getStartPosition(Tree expression) {
4452

4553
/** Returns the source end position of the node. */
4654
static int getEndPosition(Tree expression, TreePath path) {
47-
return ((JCTree) expression)
48-
.getEndPosition(((JCTree.JCCompilationUnit) path.getCompilationUnit()).endPositions);
55+
return getEndPosition(expression, (JCCompilationUnit) path.getCompilationUnit());
56+
}
57+
58+
/** Returns the source end position of the node. */
59+
static int getEndPosition(Tree tree, JCCompilationUnit unit) {
60+
try {
61+
return (int) GET_END_POSITION.invokeExact((JCTree) tree, unit);
62+
} catch (RuntimeException | Error e) {
63+
throw e;
64+
} catch (Throwable e) {
65+
throw new IllegalStateException(e);
66+
}
67+
}
68+
69+
/**
70+
* Creates a parser that records end positions. JDK-8372948 (JDK 27) dropped the {@code keepEndPos} argument
71+
* from {@link ParserFactory#newParser}, so the remaining four-argument overload now reads
72+
* {@code (input, keepDocComments, keepLineMap, parseModuleInfo)}.
73+
*/
74+
static JavacParser newParser(
75+
ParserFactory parserFactory, CharSequence input, boolean keepDocComments, boolean keepLineMap) {
76+
if (END_POS_TABLE_CLASS != null) {
77+
return parserFactory.newParser(input, keepDocComments, /* keepEndPos= */ true, keepLineMap);
78+
}
79+
// The last argument is parseModuleInfo on these JDKs.
80+
return parserFactory.newParser(input, keepDocComments, keepLineMap, false);
81+
}
82+
83+
/**
84+
* {@code com.sun.tools.javac.tree.EndPosTable}, or null on JDKs that store end positions directly in the tree
85+
* (JDK-8372948, JDK 27 and later).
86+
*/
87+
@Nullable
88+
private static final Class<?> END_POS_TABLE_CLASS = endPosTableClass();
89+
90+
/** {@code (JCTree, JCCompilationUnit) -> int}, bound to whichever end position API this JDK has. */
91+
private static final MethodHandle GET_END_POSITION = getEndPositionHandle();
92+
93+
@Nullable
94+
private static Class<?> endPosTableClass() {
95+
try {
96+
return Class.forName("com.sun.tools.javac.tree.EndPosTable");
97+
} catch (ClassNotFoundException e) {
98+
return null;
99+
}
100+
}
101+
102+
private static MethodHandle getEndPositionHandle() {
103+
MethodHandles.Lookup lookup = MethodHandles.lookup();
104+
try {
105+
if (END_POS_TABLE_CLASS == null) {
106+
// (tree, unit) -> tree.getEndPosition()
107+
return MethodHandles.dropArguments(
108+
lookup.findVirtual(JCTree.class, "getEndPosition", MethodType.methodType(int.class)),
109+
1,
110+
JCCompilationUnit.class);
111+
}
112+
// (tree, unit) -> tree.getEndPosition(unit.endPositions)
113+
return MethodHandles.filterArguments(
114+
lookup.findVirtual(
115+
JCTree.class, "getEndPosition", MethodType.methodType(int.class, END_POS_TABLE_CLASS)),
116+
1,
117+
lookup.findVarHandle(JCCompilationUnit.class, "endPositions", END_POS_TABLE_CLASS)
118+
.toMethodHandle(VarHandle.AccessMode.GET));
119+
} catch (ReflectiveOperationException e) {
120+
throw new IllegalStateException("Unsupported javac end position API", e);
121+
}
49122
}
50123

51124
/** Returns the source text for the node. */

0 commit comments

Comments
 (0)