diff options
author | cushon <cushon@google.com> | 2017-01-25 22:04:36 -0800 |
---|---|---|
committer | Liam Miller-Cushon <cushon@google.com> | 2017-01-27 15:03:01 -0800 |
commit | d1509927c68b994ecb9eb95a8ae8478da9f04ed4 (patch) | |
tree | c76e984a405aeab69641ae8edf82332401e09c8f | |
parent | a64301e54ccc8339879cbcc37f9f7e476a8b50ac (diff) | |
download | turbine-d1509927c68b994ecb9eb95a8ae8478da9f04ed4.tar.gz |
Repackage supertypes of compiled classes in the output jar
so header compilations can be performed without transitive dependencies
on the classpath.
MOE_MIGRATED_REVID=145636768
-rw-r--r-- | java/com/google/turbine/binder/ClassPathBinder.java | 54 | ||||
-rw-r--r-- | java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java | 5 | ||||
-rw-r--r-- | java/com/google/turbine/bytecode/ClassReader.java | 2 | ||||
-rw-r--r-- | java/com/google/turbine/deps/Transitive.java | 130 | ||||
-rw-r--r-- | java/com/google/turbine/main/Main.java | 32 | ||||
-rw-r--r-- | java/com/google/turbine/options/TurbineOptions.java | 5 | ||||
-rw-r--r-- | javatests/com/google/turbine/bytecode/JavapUtils.java | 5 | ||||
-rw-r--r-- | javatests/com/google/turbine/deps/TransitiveTest.java | 176 | ||||
-rw-r--r-- | javatests/com/google/turbine/lower/LowerTest.java | 2 |
9 files changed, 383 insertions, 28 deletions
diff --git a/java/com/google/turbine/binder/ClassPathBinder.java b/java/com/google/turbine/binder/ClassPathBinder.java index 66800a8..af94fed 100644 --- a/java/com/google/turbine/binder/ClassPathBinder.java +++ b/java/com/google/turbine/binder/ClassPathBinder.java @@ -16,6 +16,8 @@ package com.google.turbine.binder; +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; import com.google.common.collect.ImmutableMap; import com.google.common.io.ByteStreams; import com.google.turbine.binder.bytecode.BytecodeBoundClass; @@ -29,6 +31,7 @@ import java.io.IOException; import java.nio.file.Path; import java.util.Enumeration; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; import java.util.jar.JarEntry; import java.util.jar.JarFile; @@ -37,6 +40,12 @@ import java.util.jar.JarFile; public class ClassPathBinder { /** + * The prefix for repackaged transitive dependencies; see {@link + * com.google.turbine.deps.Transitive}. + */ + public static final String TRANSITIVE_PREFIX = "META-INF/TRANSITIVE/"; + + /** * Creates an environment containing symbols in the given classpath and bootclasspath, and adds * them to the top-level index. */ @@ -52,6 +61,7 @@ public class ClassPathBinder { private static Env<ClassSymbol, BytecodeBoundClass> bindClasspath( TopLevelIndex.Builder tli, Iterable<Path> paths) throws IOException { + Map<ClassSymbol, BytecodeBoundClass> transitive = new LinkedHashMap<>(); Map<ClassSymbol, BytecodeBoundClass> map = new HashMap<>(); Env<ClassSymbol, BytecodeBoundClass> benv = new Env<ClassSymbol, BytecodeBoundClass>() { @@ -62,11 +72,18 @@ public class ClassPathBinder { }; for (Path path : paths) { try { - bindJar(tli, path, map, benv); + bindJar(tli, path, map, benv, transitive); } catch (IOException e) { throw new IOException("error reading " + path, e); } } + for (Map.Entry<ClassSymbol, BytecodeBoundClass> entry : transitive.entrySet()) { + ClassSymbol symbol = entry.getKey(); + if (!map.containsKey(symbol)) { + map.put(symbol, entry.getValue()); + tli.insert(symbol); + } + } return new SimpleEnv<>(ImmutableMap.copyOf(map)); } @@ -74,7 +91,8 @@ public class ClassPathBinder { TopLevelIndex.Builder tli, Path path, Map<ClassSymbol, BytecodeBoundClass> env, - Env<ClassSymbol, BytecodeBoundClass> benv) + Env<ClassSymbol, BytecodeBoundClass> benv, + Map<ClassSymbol, BytecodeBoundClass> transitive) throws IOException { // TODO(cushon): consider creating a nio-friendly jar reading abstraction for testing, // that yields something like `Iterable<Pair<String, Supplier<byte[]>>>` @@ -87,21 +105,35 @@ public class ClassPathBinder { if (!name.endsWith(".class")) { continue; } + if (name.startsWith(TRANSITIVE_PREFIX)) { + ClassSymbol sym = + new ClassSymbol( + name.substring(TRANSITIVE_PREFIX.length(), name.length() - ".class".length())); + if (!transitive.containsKey(sym)) { + transitive.put( + sym, new BytecodeBoundClass(sym, toByteArrayOrDie(jf, je), benv, path.toString())); + } + continue; + } ClassSymbol sym = new ClassSymbol(name.substring(0, name.length() - ".class".length())); if (!env.containsKey(sym)) { - env.put( - sym, - new BytecodeBoundClass(sym, () -> toByteArrayOrDie(jf, je), benv, path.toString())); + env.put(sym, new BytecodeBoundClass(sym, toByteArrayOrDie(jf, je), benv, path.toString())); tli.insert(sym); } } } - private static byte[] toByteArrayOrDie(JarFile jf, JarEntry je) { - try { - return ByteStreams.toByteArray(jf.getInputStream(je)); - } catch (IOException e) { - throw new IOError(e); - } + private static Supplier<byte[]> toByteArrayOrDie(JarFile jf, JarEntry je) { + return Suppliers.memoize( + new Supplier<byte[]>() { + @Override + public byte[] get() { + try { + return ByteStreams.toByteArray(jf.getInputStream(je)); + } catch (IOException e) { + throw new IOError(e); + } + } + }); } } diff --git a/java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java b/java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java index 6b53c00..b2ca745 100644 --- a/java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java +++ b/java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java @@ -528,4 +528,9 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou public String jarFile() { return jarFile; } + + /** The class file the symbol was loaded from. */ + public ClassFile classFile() { + return classFile.get(); + } } diff --git a/java/com/google/turbine/bytecode/ClassReader.java b/java/com/google/turbine/bytecode/ClassReader.java index ff55a76..45da3a4 100644 --- a/java/com/google/turbine/bytecode/ClassReader.java +++ b/java/com/google/turbine/bytecode/ClassReader.java @@ -149,7 +149,7 @@ public class ClassReader { List<ClassFile.AnnotationInfo> annotations = new ArrayList<>(); if ((accessFlags & TurbineFlag.ACC_ANNOTATION) == 0) { reader.skip(reader.u4()); - return null; + return ImmutableList.of(); } reader.u4(); // length int numAnnotations = reader.u2(); diff --git a/java/com/google/turbine/deps/Transitive.java b/java/com/google/turbine/deps/Transitive.java new file mode 100644 index 0000000..81747e4 --- /dev/null +++ b/java/com/google/turbine/deps/Transitive.java @@ -0,0 +1,130 @@ +/* + * Copyright 2017 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.turbine.deps; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableList.Builder; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.turbine.binder.Binder.BindingResult; +import com.google.turbine.binder.bound.TypeBoundClass; +import com.google.turbine.binder.bytecode.BytecodeBoundClass; +import com.google.turbine.binder.env.CompoundEnv; +import com.google.turbine.binder.env.Env; +import com.google.turbine.binder.env.SimpleEnv; +import com.google.turbine.binder.sym.ClassSymbol; +import com.google.turbine.bytecode.ClassFile; +import com.google.turbine.bytecode.ClassFile.FieldInfo; +import com.google.turbine.bytecode.ClassFile.InnerClass; +import com.google.turbine.bytecode.ClassWriter; +import com.google.turbine.model.TurbineTyKind; +import java.util.LinkedHashSet; +import java.util.Set; + +/** + * Collects the minimal compile-time API for symbols in the supertype closure of compiled classes. + * This allows header compilations to be performed against a classpath containing only direct + * dependencies and no transitive dependencies. + */ +public class Transitive { + + public static ImmutableMap<String, byte[]> collectDeps( + ImmutableSet<String> bootClassPath, BindingResult bound) { + ImmutableMap.Builder<String, byte[]> transitive = ImmutableMap.builder(); + for (ClassSymbol sym : superClosure(bound)) { + BytecodeBoundClass info = bound.classPathEnv().get(sym); + if (info == null) { + // the symbol wasn't loaded from the classpath + continue; + } + String jarFile = info.jarFile(); + if (bootClassPath.contains(jarFile)) { + // don't export symbols loaded from the bootclasspath + continue; + } + transitive.put(sym.binaryName(), ClassWriter.writeClass(trimClass(sym, info))); + } + return transitive.build(); + } + + /** + * Removes information from repackaged classes that will not be needed by upstream compilations. + */ + private static ClassFile trimClass(ClassSymbol sym, BytecodeBoundClass info) { + ClassFile cf = info.classFile(); + // drop non-constant fields + Builder<FieldInfo> fields = ImmutableList.builder(); + for (FieldInfo f : cf.fields()) { + if (f.value() != null) { + fields.add(f); + } + } + // Remove InnerClass attributes that are unnecessary after pruning the types they refer to. + // To do this for javac, we would have to scan all remaining signatures and preserve attributes + // for reachable inner classes, but turbine only needs the attributes for the immediate + // children or parent of the current class. + Builder<InnerClass> innerClasses = ImmutableList.builder(); + for (InnerClass i : cf.innerClasses()) { + if (i.innerClass().equals(sym.binaryName()) || i.outerClass().equals(sym.binaryName())) { + innerClasses.add(i); + } + } + return new ClassFile( + cf.access(), + cf.name(), + cf.signature(), + cf.superName(), + cf.interfaces(), + // drop methods, except for annotations where we need to resolve key/value information + info.kind() == TurbineTyKind.ANNOTATION ? cf.methods() : ImmutableList.of(), + fields.build(), + // unnecessary annotations are dropped during class reading, the only remaining ones are + // well-known @interface meta-annotations (e.g. @Retention, etc.) + cf.annotations(), + innerClasses.build(), + cf.typeAnnotations()); + } + + private static Set<ClassSymbol> superClosure(BindingResult bound) { + Env<ClassSymbol, TypeBoundClass> env = + CompoundEnv.<ClassSymbol, TypeBoundClass>of(new SimpleEnv<>(bound.units())) + .append(bound.classPathEnv()); + Set<ClassSymbol> closure = new LinkedHashSet<>(); + for (ClassSymbol sym : bound.units().keySet()) { + addSuperTypes(closure, env, sym); + } + return closure; + } + + private static void addSuperTypes( + Set<ClassSymbol> closure, Env<ClassSymbol, TypeBoundClass> env, ClassSymbol sym) { + if (!closure.add(sym)) { + return; + } + TypeBoundClass info = env.get(sym); + if (info == null) { + return; + } + closure.addAll(info.children().values()); + if (info.superclass() != null) { + addSuperTypes(closure, env, info.superclass()); + } + for (ClassSymbol i : info.interfaces()) { + addSuperTypes(closure, env, i); + } + } +} diff --git a/java/com/google/turbine/main/Main.java b/java/com/google/turbine/main/Main.java index c82b38e..563bc73 100644 --- a/java/com/google/turbine/main/Main.java +++ b/java/com/google/turbine/main/Main.java @@ -25,7 +25,9 @@ import com.google.common.hash.Hashing; import com.google.common.io.ByteStreams; import com.google.turbine.binder.Binder; import com.google.turbine.binder.Binder.BindingResult; +import com.google.turbine.binder.ClassPathBinder; import com.google.turbine.deps.Dependencies; +import com.google.turbine.deps.Transitive; import com.google.turbine.diag.SourceFile; import com.google.turbine.lower.Lower; import com.google.turbine.lower.Lower.Lowered; @@ -83,6 +85,8 @@ public class Main { // TODO(cushon): parallelize Lowered lowered = Lower.lowerAll(bound.units(), bound.classPathEnv()); + Map<String, byte[]> transitive = Transitive.collectDeps(options.bootClassPath(), bound); + if (options.outputDeps().isPresent()) { DepsProto.Dependencies deps = Dependencies.collectDeps(options.targetLabel(), options.bootClassPath(), bound, lowered); @@ -92,7 +96,7 @@ public class Main { } } - writeOutput(Paths.get(options.outputFile()), lowered.bytes()); + writeOutput(Paths.get(options.outputFile()), lowered.bytes(), transitive); return true; } @@ -121,23 +125,31 @@ public class Main { } /** Write bytecode to the output jar. */ - private static void writeOutput(Path path, Map<String, byte[]> lowered) throws IOException { + private static void writeOutput( + Path path, Map<String, byte[]> lowered, Map<String, byte[]> transitive) throws IOException { try (OutputStream os = Files.newOutputStream(path); BufferedOutputStream bos = new BufferedOutputStream(os, BUFFER_SIZE); JarOutputStream jos = new JarOutputStream(bos)) { for (Map.Entry<String, byte[]> entry : lowered.entrySet()) { - JarEntry je = new JarEntry(entry.getKey() + ".class"); - je.setTime(0L); // normalize timestamps to the DOS epoch - je.setMethod(ZipEntry.STORED); - byte[] bytes = entry.getValue(); - je.setSize(bytes.length); - je.setCrc(Hashing.crc32().hashBytes(bytes).padToLong()); - jos.putNextEntry(je); - jos.write(bytes); + addEntry(jos, entry.getKey() + ".class", entry.getValue()); + } + for (Map.Entry<String, byte[]> entry : transitive.entrySet()) { + addEntry( + jos, ClassPathBinder.TRANSITIVE_PREFIX + entry.getKey() + ".class", entry.getValue()); } } } + private static void addEntry(JarOutputStream jos, String name, byte[] bytes) throws IOException { + JarEntry je = new JarEntry(name); + je.setTime(0L); // normalize timestamps to the DOS epoch + je.setMethod(ZipEntry.STORED); + je.setSize(bytes.length); + je.setCrc(Hashing.crc32().hashBytes(bytes).padToLong()); + jos.putNextEntry(je); + jos.write(bytes); + } + private static final Function<String, Path> TO_PATH = new Function<String, Path>() { @Override diff --git a/java/com/google/turbine/options/TurbineOptions.java b/java/com/google/turbine/options/TurbineOptions.java index 866318f..9814094 100644 --- a/java/com/google/turbine/options/TurbineOptions.java +++ b/java/com/google/turbine/options/TurbineOptions.java @@ -34,7 +34,7 @@ public class TurbineOptions { private final ImmutableList<String> processorPath; private final ImmutableSet<String> processors; private final ImmutableSet<String> blacklistedProcessors; - private final String tempDir; + private final @Nullable String tempDir; private final ImmutableList<String> sourceJars; private final Optional<String> outputDeps; private final ImmutableMap<String, String> directJarsToTargets; @@ -69,7 +69,7 @@ public class TurbineOptions { this.processors = checkNotNull(processors, "processors must not be null"); this.blacklistedProcessors = checkNotNull(blacklistedProcessors, "blacklistedProcessors must not be null"); - this.tempDir = checkNotNull(tempDir, "tempDir must not be null"); + this.tempDir = tempDir; this.sourceJars = checkNotNull(sourceJars, "sourceJars must not be null"); this.outputDeps = Optional.fromNullable(outputDeps); this.directJarsToTargets = @@ -103,6 +103,7 @@ public class TurbineOptions { } /** A temporary directory, e.g. for extracting sourcejar entries to before compilation. */ + @Nullable public String tempDir() { return tempDir; } diff --git a/javatests/com/google/turbine/bytecode/JavapUtils.java b/javatests/com/google/turbine/bytecode/JavapUtils.java index ccaed47..8c711c3 100644 --- a/javatests/com/google/turbine/bytecode/JavapUtils.java +++ b/javatests/com/google/turbine/bytecode/JavapUtils.java @@ -32,7 +32,6 @@ import java.nio.file.FileSystem; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.List; import javax.tools.DiagnosticCollector; import javax.tools.JavaFileObject; import javax.tools.StandardLocation; @@ -42,7 +41,8 @@ public class JavapUtils { static final ImmutableList<Path> BOOTCLASSPATH = ImmutableList.of(Paths.get(System.getProperty("java.home")).resolve("lib/rt.jar")); - public static String dump(String className, byte[] bytes) throws IOException { + public static String dump(String className, byte[] bytes, ImmutableList<String> options) + throws IOException { FileSystem fs = Jimfs.newFileSystem(Configuration.unix()); Path root = fs.getPath("classes"); Path path = root.resolve(className + ".class"); @@ -54,7 +54,6 @@ public class JavapUtils { fileManager.setLocationFromPaths(StandardLocation.CLASS_PATH, ImmutableList.of(root)); fileManager.setLocationFromPaths(StandardLocation.PLATFORM_CLASS_PATH, BOOTCLASSPATH); DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>(); - List<String> options = ImmutableList.of("-s", "-private"); Writer writer = new StringWriter(); JavapTask task = new JavapTask(writer, fileManager, diagnostics, options, ImmutableList.of(className)); diff --git a/javatests/com/google/turbine/deps/TransitiveTest.java b/javatests/com/google/turbine/deps/TransitiveTest.java new file mode 100644 index 0000000..c6a6eea --- /dev/null +++ b/javatests/com/google/turbine/deps/TransitiveTest.java @@ -0,0 +1,176 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.turbine.deps; + +import static com.google.common.collect.Iterables.getOnlyElement; +import static com.google.common.truth.Truth.assertThat; +import static java.nio.charset.StandardCharsets.UTF_8; +import static java.util.stream.Collectors.toList; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import com.google.common.io.ByteStreams; +import com.google.turbine.bytecode.ClassFile; +import com.google.turbine.bytecode.ClassFile.InnerClass; +import com.google.turbine.bytecode.ClassReader; +import com.google.turbine.main.Main; +import com.google.turbine.options.TurbineOptions; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class TransitiveTest { + + static final ImmutableList<Path> BOOTCLASSPATH = + ImmutableList.of(Paths.get(System.getProperty("java.home")).resolve("lib/rt.jar")); + + @Rule public final TemporaryFolder temporaryFolder = new TemporaryFolder(); + + class SourceBuilder { + private final Path lib; + private final ImmutableList.Builder<Path> sources = ImmutableList.builder(); + + SourceBuilder() throws IOException { + lib = temporaryFolder.newFolder().toPath(); + } + + SourceBuilder addSourceLines(String name, String... lines) throws IOException { + Path path = lib.resolve(name); + Files.createDirectories(path.getParent()); + Files.write(path, Arrays.asList(lines), UTF_8); + sources.add(path); + return this; + } + + ImmutableList<Path> build() { + return sources.build(); + } + } + + Path runTurbine(List<Path> sources, List<Path> classpath) throws IOException { + Path out = temporaryFolder.newFolder().toPath().resolve("out.jar"); + boolean ok = + Main.compile( + TurbineOptions.builder() + .addSources(sources.stream().map(Path::toString).collect(toList())) + .addClassPathEntries(classpath.stream().map(Path::toString).collect(toList())) + .addBootClassPathEntries(Iterables.transform(BOOTCLASSPATH, Path::toString)) + .setOutput(out.toString()) + .build()); + assertThat(ok).isTrue(); + return out; + } + + private Map<String, byte[]> readJar(Path libb) throws IOException { + Map<String, byte[]> jarEntries = new LinkedHashMap<>(); + try (JarFile jf = new JarFile(libb.toFile())) { + Enumeration<JarEntry> entries = jf.entries(); + while (entries.hasMoreElements()) { + JarEntry je = entries.nextElement(); + jarEntries.put(je.getName(), ByteStreams.toByteArray(jf.getInputStream(je))); + } + } + return jarEntries; + } + + @Test + public void transitive() throws Exception { + Path liba = + runTurbine( + new SourceBuilder() + .addSourceLines( + "a/A.java", + "package a;", + "import java.util.Map;", + "public class A {", + " public @interface Anno {", + " int x() default 42;", + " }", + " public static class Inner {}", + " public static final int CONST = 42;", + " public int mutable = 42;", + " public Map.Entry<String, String> f(Map<String, String> m) {", + " return m.entrySet().iterator().next();", + " }", + "}") + .build(), + ImmutableList.of()); + + Path libb = + runTurbine( + new SourceBuilder() + .addSourceLines("b/B.java", "package b;", "public class B extends a.A {}") + .build(), + ImmutableList.of(liba)); + + // libb repackages A, and any member types + assertThat(readJar(libb).keySet()) + .containsExactly( + "b/B.class", + "META-INF/TRANSITIVE/a/A.class", + "META-INF/TRANSITIVE/a/A$Anno.class", + "META-INF/TRANSITIVE/a/A$Inner.class"); + + ClassFile a = ClassReader.read(readJar(libb).get("META-INF/TRANSITIVE/a/A.class")); + // methods and non-constant fields are removed + assertThat(getOnlyElement(a.fields()).name()).isEqualTo("CONST"); + assertThat(a.methods()).isEmpty(); + assertThat(Iterables.transform(a.innerClasses(), InnerClass::innerClass)) + .containsExactly("a/A$Anno", "a/A$Inner"); + + // annotation interface methods are preserved + assertThat(ClassReader.read(readJar(libb).get("META-INF/TRANSITIVE/a/A$Anno.class")).methods()) + .hasSize(1); + + // a class that references members of the transitive supertype A by simple name + // compiles cleanly against the repackaged version of A + Path libc = + runTurbine( + new SourceBuilder() + .addSourceLines( + "c/C.java", + "package c;", + "public class C extends b.B {", + " @Anno(x = 2) static final Inner i; // a.A$Inner ", + " static final int X = CONST; // a.A#CONST", + "}") + .build(), + ImmutableList.of(libb)); + + assertThat(readJar(libc).keySet()) + .containsExactly( + "c/C.class", + "META-INF/TRANSITIVE/b/B.class", + "META-INF/TRANSITIVE/a/A.class", + "META-INF/TRANSITIVE/a/A$Anno.class", + "META-INF/TRANSITIVE/a/A$Inner.class"); + } +} diff --git a/javatests/com/google/turbine/lower/LowerTest.java b/javatests/com/google/turbine/lower/LowerTest.java index 735f69e..ff0391a 100644 --- a/javatests/com/google/turbine/lower/LowerTest.java +++ b/javatests/com/google/turbine/lower/LowerTest.java @@ -278,7 +278,7 @@ public class LowerTest { Map<String, byte[]> actual = IntegrationTestSupport.runTurbine(input.sources, ImmutableList.of(), BOOTCLASSPATH); - assertThat(JavapUtils.dump("Test", actual.get("Test"))) + assertThat(JavapUtils.dump("Test", actual.get("Test"), ImmutableList.of("-s", "-private"))) .isEqualTo( Joiner.on('\n') .join( |