Fix tree pruning bug with constructor delegation
-relax disables checks in Resolve to catch constructors that do not explicitly
delegate to a superclass constructor when no nullary superclass constructor
exists. Now that we're not using -relax, those constructor calls have to be
preserved.
--
MOS_MIGRATED_REVID=114875860
diff --git a/src/java_tools/buildjar/java/com/google/devtools/build/java/turbine/javac/TreePruner.java b/src/java_tools/buildjar/java/com/google/devtools/build/java/turbine/javac/TreePruner.java
index da74844..092b82b 100644
--- a/src/java_tools/buildjar/java/com/google/devtools/build/java/turbine/javac/TreePruner.java
+++ b/src/java_tools/buildjar/java/com/google/devtools/build/java/turbine/javac/TreePruner.java
@@ -22,16 +22,23 @@
import com.sun.source.tree.LiteralTree;
import com.sun.source.tree.MemberSelectTree;
import com.sun.source.tree.ParenthesizedTree;
+import com.sun.source.tree.Tree.Kind;
import com.sun.source.tree.TypeCastTree;
import com.sun.source.tree.UnaryTree;
import com.sun.source.util.SimpleTreeVisitor;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCBlock;
+import com.sun.tools.javac.tree.JCTree.JCExpression;
+import com.sun.tools.javac.tree.JCTree.JCExpressionStatement;
+import com.sun.tools.javac.tree.JCTree.JCIdent;
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
+import com.sun.tools.javac.tree.JCTree.JCMethodInvocation;
+import com.sun.tools.javac.tree.JCTree.JCStatement;
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
import com.sun.tools.javac.tree.TreeScanner;
import com.sun.tools.javac.util.List;
+import com.sun.tools.javac.util.Name;
/**
* Prunes AST nodes that are not required for header compilation.
@@ -65,6 +72,12 @@
if (tree.body == null) {
return;
}
+ if (tree.getReturnType() == null && delegatingConstructor(tree.body.stats)) {
+ // if the first statement of a constructor declaration delegates to another
+ // constructor, it needs to be preserved to satisfy checks in Resolve
+ tree.body.stats = com.sun.tools.javac.util.List.of(tree.body.stats.get(0));
+ return;
+ }
tree.body.stats = com.sun.tools.javac.util.List.nil();
}
@@ -87,6 +100,26 @@
}
};
+ private static boolean delegatingConstructor(List<JCStatement> stats) {
+ if (stats.isEmpty()) {
+ return false;
+ }
+ JCStatement stat = stats.get(0);
+ if (stat.getKind() != Kind.EXPRESSION_STATEMENT) {
+ return false;
+ }
+ JCExpression expr = ((JCExpressionStatement) stat).getExpression();
+ if (expr.getKind() != Kind.METHOD_INVOCATION) {
+ return false;
+ }
+ JCExpression select = ((JCMethodInvocation) expr).getMethodSelect();
+ if (select.getKind() != Kind.IDENTIFIER) {
+ return false;
+ }
+ Name name = ((JCIdent) select).getName();
+ return name.contentEquals("this") || name.contentEquals("super");
+ }
+
private static boolean isConstantVariable(JCVariableDecl tree) {
if ((tree.mods.flags & Flags.FINAL) != Flags.FINAL) {
return false;
diff --git a/src/java_tools/buildjar/javatests/com/google/devtools/build/java/turbine/javac/JavacTurbineTest.java b/src/java_tools/buildjar/javatests/com/google/devtools/build/java/turbine/javac/JavacTurbineTest.java
index f80dec4..c01f5d0 100644
--- a/src/java_tools/buildjar/javatests/com/google/devtools/build/java/turbine/javac/JavacTurbineTest.java
+++ b/src/java_tools/buildjar/javatests/com/google/devtools/build/java/turbine/javac/JavacTurbineTest.java
@@ -829,4 +829,36 @@
assertThat(sw.toString()).contains("error reading");
}
}
+
+ @Test
+ public void requiredConstructor() throws Exception {
+ addSourceLines("Super.java", "class Super {", " public Super(int x) {}", "}");
+ addSourceLines(
+ "Hello.java",
+ "class Hello extends Super {",
+ " public Hello() {",
+ " super(42);",
+ " }",
+ "}");
+
+ compile();
+
+ Map<String, byte[]> outputs = collectOutputs();
+
+ assertThat(outputs.keySet()).containsExactly("Super.class", "Hello.class");
+
+ String text = textify(outputs.get("Hello.class"));
+ String[] expected = {
+ "// class version 51.0 (51)",
+ "// access flags 0x20",
+ "class Hello extends Super {",
+ "",
+ "",
+ " // access flags 0x1",
+ " public <init>()V",
+ "}",
+ ""
+ };
+ assertThat(text).isEqualTo(Joiner.on('\n').join(expected));
+ }
}
diff --git a/src/java_tools/buildjar/javatests/com/google/devtools/build/java/turbine/javac/TreePrunerTest.java b/src/java_tools/buildjar/javatests/com/google/devtools/build/java/turbine/javac/TreePrunerTest.java
index 4455437..711b5524 100644
--- a/src/java_tools/buildjar/javatests/com/google/devtools/build/java/turbine/javac/TreePrunerTest.java
+++ b/src/java_tools/buildjar/javatests/com/google/devtools/build/java/turbine/javac/TreePrunerTest.java
@@ -220,6 +220,47 @@
assertThat(printPruned("final int x = new X().y;")).isEqualTo("final int x");
}
+ @Test
+ public void constructorChaining() {
+ String[] lines = {
+ "class Test {",
+ " Test() {",
+ " this(42);",
+ " process();",
+ " }",
+ " Test() {",
+ " this(42);",
+ " }",
+ " Test() {",
+ " super(42);",
+ " }",
+ " Test() {}",
+ "}",
+ };
+ JCCompilationUnit tree = parseLines(lines);
+ TreePruner.prune(tree);
+ String[] expected = {
+ "class Test {",
+ " ",
+ " Test() {",
+ " this(42);",
+ " }",
+ " ",
+ " Test() {",
+ " this(42);",
+ " }",
+ " ",
+ " Test() {",
+ " super(42);",
+ " }",
+ " ",
+ " Test() {",
+ " }",
+ "}",
+ };
+ assertThat(prettyPrint(tree)).isEqualTo(Joiner.on('\n').join(expected));
+ }
+
private String prettyPrint(JCCompilationUnit tree) {
return tree.toString().trim();
}