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)); + } }