Put turbine bridge pruning behind a flag

--
MOS_MIGRATED_REVID=135171940
diff --git a/src/java_tools/buildjar/java/com/google/devtools/build/java/turbine/javac/JavacTurbine.java b/src/java_tools/buildjar/java/com/google/devtools/build/java/turbine/javac/JavacTurbine.java
index ef2ec5a..cc09d5a 100644
--- a/src/java_tools/buildjar/java/com/google/devtools/build/java/turbine/javac/JavacTurbine.java
+++ b/src/java_tools/buildjar/java/com/google/devtools/build/java/turbine/javac/JavacTurbine.java
@@ -162,7 +162,9 @@
     if (sources.isEmpty()) {
       // accept compilations with an empty source list for compatibility with JavaBuilder
       emitClassJar(
-          Paths.get(turbineOptions.outputFile()), ImmutableMap.<String, OutputFileObject>of());
+          Paths.get(turbineOptions.outputFile()),
+          ImmutableMap.<String, OutputFileObject>of(),
+          turbineOptions);
       dependencyModule.emitDependencyInformation(/*classpath=*/ "", /*successful=*/ true);
       return Result.OK_WITH_REDUCED_CLASSPATH;
     }
@@ -203,7 +205,7 @@
     }
 
     if (result.ok()) {
-      emitClassJar(Paths.get(turbineOptions.outputFile()), compileResult.files());
+      emitClassJar(Paths.get(turbineOptions.outputFile()), compileResult.files(), turbineOptions);
       dependencyModule.emitDependencyInformation(
           CLASSPATH_JOINER.join(actualClasspath), compileResult.success());
     }
@@ -231,7 +233,8 @@
   }
 
   /** Write the class output from a successful compilation to the output jar. */
-  private static void emitClassJar(Path outputJar, ImmutableMap<String, OutputFileObject> files)
+  private static void emitClassJar(
+      Path outputJar, ImmutableMap<String, OutputFileObject> files, TurbineOptions turbineOptions)
       throws IOException {
     try (OutputStream fos = Files.newOutputStream(outputJar);
         ZipOutputStream zipOut =
@@ -247,7 +250,7 @@
           continue;
         }
         if (name.endsWith(".class")) {
-          bytes = processBytecode(bytes);
+          bytes = processBytecode(bytes, turbineOptions);
         }
         ZipUtil.storeEntry(name, bytes, zipOut);
         hasEntries = true;
@@ -265,11 +268,11 @@
    * <p>Most code will already have been removed after parsing, but the bytecode will still contain
    * e.g. lowered class and instance initializers.
    */
-  private static byte[] processBytecode(byte[] bytes) {
+  private static byte[] processBytecode(byte[] bytes, TurbineOptions turbineOptions) {
     ClassWriter cw = new ClassWriter(0);
     new ClassReader(bytes)
         .accept(
-            new PrivateMemberPruner(cw),
+            new PrivateMemberPruner(cw, turbineOptions),
             ClassReader.SKIP_CODE | ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG);
     return cw.toByteArray();
   }
@@ -289,8 +292,12 @@
    * detect private members at that point (e.g. with implicit modifiers).
    */
   static class PrivateMemberPruner extends ClassVisitor {
-    public PrivateMemberPruner(ClassVisitor cv) {
+
+    private final boolean dropBridges;
+
+    public PrivateMemberPruner(ClassVisitor cv, TurbineOptions turbineOptions) {
       super(Opcodes.ASM5, cv);
+      this.dropBridges = turbineOptions.javacOpts().contains("-XDdropBridgesInTurbine=true");
     }
 
     @Override
@@ -312,9 +319,11 @@
         // drop class initializers, which are going to be empty after tree pruning
         return null;
       }
-      if ((access & (Opcodes.ACC_SYNTHETIC | Opcodes.ACC_BRIDGE)) != 0) {
-        // drop bridges (see b/31653210)
-        return null;
+      // drop synthetic methods, possibly including bridges (see b/31653210)
+      if ((access & Opcodes.ACC_SYNTHETIC) == Opcodes.ACC_SYNTHETIC) {
+        if (((access & Opcodes.ACC_BRIDGE) == 0) || dropBridges) {
+          return null;
+        }
       }
       return super.visitMethod(access, name, desc, signature, exceptions);
     }
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 76454ae..5cbd08c 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
@@ -706,8 +706,7 @@
   @Test
   public void constantsEnum() throws Exception {
     addSourceLines(
-        "TheEnum.java",
-        // TODO(cushon): fix google-java-format's handling of lists of string literals
+        "TheEnum.java", //
         "public enum TheEnum {",
         "  ONE, TWO, THREE;",
         "}");
@@ -1273,6 +1272,7 @@
         "  public String call() { return \"\"; }",
         "}");
 
+    optionsBuilder.addAllJavacOpts(ImmutableList.of("-XDdropBridgesInTurbine=true"));
     compile();
 
     Map<String, byte[]> outputs = collectOutputs();
@@ -1298,4 +1298,42 @@
     };
     assertThat(text).isEqualTo(Joiner.on('\n').join(expected));
   }
+
+  @Test
+  public void preserveBridge() throws Exception {
+    addSourceLines(
+        "Bridge.java",
+        "import java.util.concurrent.Callable;",
+        "class Bridge implements Callable<String> {",
+        "  public String call() { return \"\"; }",
+        "}");
+
+    compile();
+
+    Map<String, byte[]> outputs = collectOutputs();
+
+    assertThat(outputs.keySet()).containsExactly("Bridge.class");
+
+    String text = textify(outputs.get("Bridge.class"));
+    String[] expected = {
+      "// class version 52.0 (52)",
+      "// access flags 0x20",
+      "// signature Ljava/lang/Object;Ljava/util/concurrent/Callable<Ljava/lang/String;>;",
+      "// declaration: Bridge implements java.util.concurrent.Callable<java.lang.String>",
+      "class Bridge implements java/util/concurrent/Callable  {",
+      "",
+      "",
+      "  // access flags 0x0",
+      "  <init>()V",
+      "",
+      "  // access flags 0x1",
+      "  public call()Ljava/lang/String;",
+      "",
+      "  // access flags 0x1041",
+      "  public synthetic bridge call()Ljava/lang/Object; throws java/lang/Exception ",
+      "}",
+      ""
+    };
+    assertThat(text).isEqualTo(Joiner.on('\n').join(expected));
+  }
 }