From d64da606369a2e0c780c6639be8f5a851ada0ec9 Mon Sep 17 00:00:00 2001 From: Werner Dietl Date: Wed, 8 Mar 2023 16:22:37 -0500 Subject: [PATCH] Prepare for a signature change in `TreeMaker.Select` (#428) Co-authored-by: Chris Povirk --- docs/CHANGELOG.md | 2 +- .../reflection/DefaultReflectionResolver.java | 2 +- .../checkerframework/javacutil/TreeUtils.java | 70 +++++++++++++++++++ .../javacutil/trees/TreeBuilder.java | 23 +++--- .../javacutil/trees/TreeParser.java | 3 +- 5 files changed, 82 insertions(+), 18 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 31350474412..3505d59070b 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -21,7 +21,7 @@ Remove the `fastAssemble` task which is subsumed by `assembleForJavac`. **Closed issues:** -eisop#282, eisop#310, eisop#312. +eisop#282, eisop#310, eisop#312, typetools#5672. Version 3.32.0 (March 2, 2023) diff --git a/framework/src/main/java/org/checkerframework/common/reflection/DefaultReflectionResolver.java b/framework/src/main/java/org/checkerframework/common/reflection/DefaultReflectionResolver.java index 0c85da1fcc8..8c0161dbdf1 100644 --- a/framework/src/main/java/org/checkerframework/common/reflection/DefaultReflectionResolver.java +++ b/framework/src/main/java/org/checkerframework/common/reflection/DefaultReflectionResolver.java @@ -395,7 +395,7 @@ private List resolveReflectiveMethod( debugReflection("Resolved non-public method: " + symbol.owner + "." + symbol); } - JCExpression method = make.Select(receiver, symbol); + JCExpression method = TreeUtils.Select(make, receiver, symbol); args = getCorrectedArgs(symbol, args); // Build method invocation tree depending on the number of // parameters diff --git a/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtils.java b/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtils.java index 06b954fe86a..6459b2107ed 100644 --- a/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtils.java +++ b/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtils.java @@ -43,6 +43,7 @@ import com.sun.tools.javac.tree.JCTree.JCBinary; import com.sun.tools.javac.tree.JCTree.JCExpression; import com.sun.tools.javac.tree.JCTree.JCExpressionStatement; +import com.sun.tools.javac.tree.JCTree.JCFieldAccess; import com.sun.tools.javac.tree.JCTree.JCLambda; import com.sun.tools.javac.tree.JCTree.JCLambda.ParameterKind; import com.sun.tools.javac.tree.JCTree.JCLiteral; @@ -115,6 +116,9 @@ private TreeUtils() { /** Whether we are running on at least Java 16. */ private static final boolean atLeastJava16; + /** Whether we are running on at least Java 21. */ + private static final boolean atLeastJava21; + /** The CaseTree.getExpression method for Java up to 11; null otherwise. */ private static final @Nullable Method CASETREE_GETEXPRESSION; @@ -144,6 +148,12 @@ private TreeUtils() { /** The BindingPatternTree.getVariable method for Java 16 and higher; null otherwise. */ private static final @Nullable Method BINDINGPATTERNTREE_GETVARIABLE; + /** + * The {@code TreeMaker.Select(JCExpression, Symbol)} method. Return type changes for JDK21+. + * Only needs to be used while the code is compiled with JDK below 21. + */ + private static final @Nullable Method TREEMAKER_SELECT; + /** The value of Flags.RECORD which does not exist in Java 9 or 11. */ private static final long Flags_RECORD = 2305843009213693952L; @@ -183,6 +193,14 @@ private TreeUtils() { } atLeastJava16 = java16 != null && latestSource.ordinal() >= java16.ordinal(); + SourceVersion java21; + try { + java21 = SourceVersion.valueOf("RELEASE_21"); + } catch (IllegalArgumentException e) { + java21 = null; + } + atLeastJava21 = java21 != null && latestSource.ordinal() >= java21.ordinal(); + try { // TODO: profile and see whether doing all these here has a performance impact. // If so, move to lazily setting the fields. @@ -242,6 +260,12 @@ private TreeUtils() { INSTANCEOFTREE_GETPATTERN = null; BINDINGPATTERNTREE_GETVARIABLE = null; } + if (atLeastJava21) { + TREEMAKER_SELECT = + TreeMaker.class.getMethod("Select", JCExpression.class, Symbol.class); + } else { + TREEMAKER_SELECT = null; + } } catch (ClassNotFoundException | NoSuchMethodException e) { Error err = new AssertionError("Unexpected error in TreeUtils static initializer"); err.initCause(e); @@ -2545,4 +2569,50 @@ public static Tree.Kind getKindRecordAsClass(Tree tree) { public static boolean isBinaryComparison(BinaryTree tree) { return BINARY_COMPARISON_TREE_KINDS.contains(tree.getKind()); } + + /** + * Returns the result of {@code treeMaker.Select(base, sym)}. + * + * @param treeMaker the TreeMaker to use + * @param base the expression for the select + * @param sym the symbol to select + * @return the JCFieldAccess tree to select sym in base + */ + public static JCFieldAccess Select(TreeMaker treeMaker, Tree base, Symbol sym) { + if (atLeastJava21) { + try { + assert TREEMAKER_SELECT != null : "@AssumeAssertion(nullness): initialization"; + JCFieldAccess jfa = (JCFieldAccess) TREEMAKER_SELECT.invoke(treeMaker, base, sym); + if (jfa != null) { + return jfa; + } else { + throw new BugInCF( + "TreeUtils.Select: TreeMaker.Select returned null for tree: %s", base); + } + } catch (InvocationTargetException | IllegalAccessException e) { + throw new BugInCF("TreeUtils.Select: reflection failed for tree: %s", base, e); + } + } else { + @SuppressWarnings("cast") // Redundant on JDK 21+ + JCFieldAccess jfa = (JCFieldAccess) treeMaker.Select((JCExpression) base, sym); + return jfa; + } + } + + /** + * Returns the result of {@code treeMaker.Select(base, name)}. + * + * @param treeMaker the TreeMaker to use + * @param base the expression for the select + * @param name the name to select + * @return the JCFieldAccess tree to select sym in base + */ + public static JCFieldAccess Select( + TreeMaker treeMaker, Tree base, com.sun.tools.javac.util.Name name) { + /* + * There's no need for reflection here. The only reason we even declare this method is so that + * callers don't have to remember which overload we provide a wrapper around. + */ + return treeMaker.Select((JCExpression) base, name); + } } diff --git a/javacutil/src/main/java/org/checkerframework/javacutil/trees/TreeBuilder.java b/javacutil/src/main/java/org/checkerframework/javacutil/trees/TreeBuilder.java index 7aa031183cb..3cb7d50d7d1 100644 --- a/javacutil/src/main/java/org/checkerframework/javacutil/trees/TreeBuilder.java +++ b/javacutil/src/main/java/org/checkerframework/javacutil/trees/TreeBuilder.java @@ -125,9 +125,7 @@ public MemberSelectTree buildIteratorMethodAccess(ExpressionTree iterableExpr) { com.sun.tools.javac.util.List.nil(), methodClass); - JCTree.JCFieldAccess iteratorAccess = - (JCTree.JCFieldAccess) - maker.Select((JCTree.JCExpression) iterableExpr, iteratorMethod); + JCTree.JCFieldAccess iteratorAccess = TreeUtils.Select(maker, iterableExpr, iteratorMethod); iteratorAccess.setType(updatedMethodType); return iteratorAccess; @@ -156,11 +154,10 @@ public MemberSelectTree buildHasNextMethodAccess(ExpressionTree iteratorExpr) { } } - assert hasNextMethod != null : "no hasNext method declared for expression type"; + assert hasNextMethod != null + : "@AssumeAssertion(nullness): no hasNext method declared for expression type"; - JCTree.JCFieldAccess hasNextAccess = - (JCTree.JCFieldAccess) - maker.Select((JCTree.JCExpression) iteratorExpr, hasNextMethod); + JCTree.JCFieldAccess hasNextAccess = TreeUtils.Select(maker, iteratorExpr, hasNextMethod); hasNextAccess.setType(hasNextMethod.asType()); return hasNextAccess; @@ -210,8 +207,7 @@ public MemberSelectTree buildNextMethodAccess(ExpressionTree iteratorExpr) { com.sun.tools.javac.util.List.nil(), methodClass); - JCTree.JCFieldAccess nextAccess = - (JCTree.JCFieldAccess) maker.Select((JCTree.JCExpression) iteratorExpr, nextMethod); + JCTree.JCFieldAccess nextAccess = TreeUtils.Select(maker, iteratorExpr, nextMethod); nextAccess.setType(updatedMethodType); return nextAccess; @@ -225,8 +221,7 @@ public MemberSelectTree buildNextMethodAccess(ExpressionTree iteratorExpr) { */ public MemberSelectTree buildArrayLengthAccess(ExpressionTree expression) { - return (JCTree.JCFieldAccess) - maker.Select((JCTree.JCExpression) expression, symtab.lengthVar); + return TreeUtils.Select(maker, expression, symtab.lengthVar); } /** @@ -406,8 +401,7 @@ public MemberSelectTree buildValueOfMethodAccess(Tree expr) { Type.MethodType methodType = (Type.MethodType) valueOfMethod.asType(); - JCTree.JCFieldAccess valueOfAccess = - (JCTree.JCFieldAccess) maker.Select((JCTree.JCExpression) expr, valueOfMethod); + JCTree.JCFieldAccess valueOfAccess = TreeUtils.Select(maker, expr, valueOfMethod); valueOfAccess.setType(methodType); return valueOfAccess; @@ -467,8 +461,7 @@ public MemberSelectTree buildPrimValueMethodAccess(Tree expr) { Type.MethodType methodType = (Type.MethodType) primValueMethod.asType(); - JCTree.JCFieldAccess primValueAccess = - (JCTree.JCFieldAccess) maker.Select((JCTree.JCExpression) expr, primValueMethod); + JCTree.JCFieldAccess primValueAccess = TreeUtils.Select(maker, expr, primValueMethod); primValueAccess.setType(methodType); return primValueAccess; diff --git a/javacutil/src/main/java/org/checkerframework/javacutil/trees/TreeParser.java b/javacutil/src/main/java/org/checkerframework/javacutil/trees/TreeParser.java index d89614d63fb..9817052a536 100644 --- a/javacutil/src/main/java/org/checkerframework/javacutil/trees/TreeParser.java +++ b/javacutil/src/main/java/org/checkerframework/javacutil/trees/TreeParser.java @@ -10,6 +10,7 @@ import com.sun.tools.javac.util.Names; import org.checkerframework.javacutil.Pair; +import org.checkerframework.javacutil.TreeUtils; import java.util.StringTokenizer; @@ -119,7 +120,7 @@ private Pair parseExpression(StringTokenizer tokenizer, St token = delim; if (".".equals(delim)) { token = nextToken(tokenizer); - tree = maker.Select(tree, names.fromString(token)); + tree = TreeUtils.Select(maker, tree, names.fromString(token)); } else if ("(".equals(delim)) { token = nextToken(tokenizer); ListBuffer args = new ListBuffer<>();