Java tutorial
/* * Copyright 2014-present Facebook, Inc. * * 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.facebook.buck.java.abi; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import com.facebook.buck.io.ProjectFilesystem; import com.facebook.buck.zip.Unzip; import com.google.common.base.Joiner; import com.google.common.collect.FluentIterable; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSortedSet; import com.google.common.collect.Lists; import com.google.common.io.ByteStreams; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.AnnotationNode; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.FieldNode; import org.objectweb.asm.tree.MethodNode; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; import java.util.List; import java.util.SortedSet; import java.util.jar.JarOutputStream; import java.util.zip.ZipEntry; import javax.tools.JavaCompiler; import javax.tools.JavaFileObject; import javax.tools.StandardJavaFileManager; import javax.tools.ToolProvider; public class MirrorTest { private static final ImmutableSortedSet<Path> EMPTY_CLASSPATH = ImmutableSortedSet.of(); @Rule public TemporaryFolder temp = new TemporaryFolder(); private ProjectFilesystem filesystem; private Path stubJar; @Before public void createStubJar() throws IOException { File out = temp.newFolder(); filesystem = new ProjectFilesystem(out.toPath()); stubJar = Paths.get("stub.jar"); } @Test public void emptyClass() throws IOException { Path jar = compileToJar(EMPTY_CLASSPATH, "A.java", "package com.example.buck; public class A {}"); new StubJar(jar).writeTo(filesystem, stubJar); // Verify that the stub jar works by compiling some code that depends on A. compileToJar(ImmutableSortedSet.of(stubJar), "B.java", "package com.example.buck; public class B extends A {}"); } @Test public void emptyClassWithAnnotation() throws IOException { Path jar = compileToJar(EMPTY_CLASSPATH, "A.java", "package com.example.buck; @Deprecated public class A {}"); new StubJar(jar).writeTo(filesystem, stubJar); // Examine the jar to see if the "A" class is deprecated. ClassNode classNode = readClass(stubJar, "com/example/buck/A.class").getClassNode(); assertNotEquals(0, classNode.access & Opcodes.ACC_DEPRECATED); } @Test public void classWithTwoMethods() throws IOException { Path jar = compileToJar(EMPTY_CLASSPATH, "A.java", Joiner.on("\n").join(ImmutableList.of("package com.example.buck;", "public class A {", " public String toString() { return null; }", " public void eatCake() {}", "}"))); new StubJar(jar).writeTo(filesystem, stubJar); // Verify that both methods are present and given in alphabetical order. ClassNode classNode = readClass(stubJar, "com/example/buck/A.class").getClassNode(); List<MethodNode> methods = classNode.methods; // Index 0 is the <init> method. Skip that. assertEquals("eatCake", methods.get(1).name); assertEquals("toString", methods.get(2).name); } @Test public void genericClassSignaturesShouldBePreserved() throws IOException { Path jar = compileToJar(EMPTY_CLASSPATH, "A.java", Joiner.on("\n").join(ImmutableList.of("package com.example.buck;", "public class A<T> {", " public T get(String key) { return null; }", "}"))); // With generic classes, there are typically two interesting things we want to keep an eye on. // First is the "descriptor", which is the signature of the method with type erasure complete. // Optionally, compilers (and the OpenJDK, Oracle and Eclipse compilers all do this) can also // include a "signature", which is the signature of the method before type erasure. See // http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.3 for more. AbiClass original = readClass(jar, "com/example/buck/A.class"); String classSig = original.getClassNode().signature; MethodNode originalGet = original.findMethod("get"); new StubJar(jar).writeTo(filesystem, stubJar); AbiClass stubbed = readClass(stubJar, "com/example/buck/A.class"); assertEquals(classSig, stubbed.getClassNode().signature); MethodNode stubbedGet = stubbed.findMethod("get"); assertMethodEquals(originalGet, stubbedGet); } @Test public void shouldIgnorePrivateMethods() throws IOException { Path jar = compileToJar(EMPTY_CLASSPATH, "A.java", Joiner.on("\n") .join(ImmutableList.of("package com.example.buck;", "public class A {", " private void privateMethod() {}", " void packageMethod() {}", " protected void protectedMethod() {}", " public void publicMethod() {}", "}"))); new StubJar(jar).writeTo(filesystem, stubJar); AbiClass stubbed = readClass(stubJar, "com/example/buck/A.class"); for (MethodNode method : stubbed.getClassNode().methods) { assertFalse(method.name.contains("private")); } } @Test public void shouldPreserveAField() throws IOException { Path jar = compileToJar(EMPTY_CLASSPATH, "A.java", Joiner.on("\n").join(ImmutableList .of("package com.example.buck;", "public class A {", " protected String protectedField;", "}"))); new StubJar(jar).writeTo(filesystem, stubJar); AbiClass stubbed = readClass(stubJar, "com/example/buck/A.class"); FieldNode field = stubbed.findField("protectedField"); assertEquals("protectedField", field.name); assertTrue((field.access & Opcodes.ACC_PROTECTED) > 0); } @Test public void shouldIgnorePrivateFields() throws IOException { Path jar = compileToJar(EMPTY_CLASSPATH, "A.java", Joiner.on("\n").join(ImmutableList .of("package com.example.buck;", "public class A {", " private String privateField;", "}"))); new StubJar(jar).writeTo(filesystem, stubJar); AbiClass stubbed = readClass(stubJar, "com/example/buck/A.class"); assertEquals(0, stubbed.getClassNode().fields.size()); } @Test public void shouldPreserveGenericTypesOnFields() throws IOException { Path jar = compileToJar(EMPTY_CLASSPATH, "A.java", Joiner.on("\n").join( ImmutableList.of("package com.example.buck;", "public class A<T> {", " public T theField;", "}"))); new StubJar(jar).writeTo(filesystem, stubJar); AbiClass original = readClass(jar, "com/example/buck/A.class"); AbiClass stubbed = readClass(stubJar, "com/example/buck/A.class"); FieldNode originalField = original.findField("theField"); FieldNode stubbedField = stubbed.findField("theField"); assertFieldEquals(originalField, stubbedField); } @Test public void shouldPreserveGenericTypesOnMethods() throws IOException { Path jar = compileToJar(EMPTY_CLASSPATH, "A.java", Joiner.on("\n").join(ImmutableList.of("package com.example.buck;", "public class A<T> {", " public T get(String key) { return null; }", " public <X extends Comparable<T>> X compareWith(T other) { return null; }", "}"))); new StubJar(jar).writeTo(filesystem, stubJar); AbiClass original = readClass(jar, "com/example/buck/A.class"); AbiClass stubbed = readClass(stubJar, "com/example/buck/A.class"); MethodNode originalGet = original.findMethod("get"); MethodNode stubbedGet = stubbed.findMethod("get"); assertEquals(originalGet.signature, stubbedGet.signature); assertEquals(originalGet.desc, stubbedGet.desc); MethodNode originalCompare = original.findMethod("compareWith"); MethodNode stubbedCompare = stubbed.findMethod("compareWith"); assertEquals(originalCompare.signature, stubbedCompare.signature); assertEquals(originalCompare.desc, stubbedCompare.desc); } @Test public void preservesAnnotationsOnMethods() throws IOException { Path annotations = buildAnnotationJar(); Path jar = compileToJar(ImmutableSortedSet.of(annotations), "A.java", Joiner.on("\n").join(ImmutableList.of("package com.example.buck;", "public class A {", " @Foo", " public void cheese(String key) {}", "}"))); new StubJar(jar).writeTo(filesystem, stubJar); AbiClass stubbed = readClass(stubJar, "com/example/buck/A.class"); MethodNode method = stubbed.findMethod("cheese"); List<AnnotationNode> seen = method.visibleAnnotations; assertEquals(1, seen.size()); assertEquals("Lcom/example/buck/Foo;", seen.get(0).desc); } @Test public void preservesAnnotationsOnFields() throws IOException { Path annotations = buildAnnotationJar(); Path jar = compileToJar(ImmutableSortedSet.of(annotations), "A.java", Joiner.on("\n").join(ImmutableList .of("package com.example.buck;", "public class A {", " @Foo", " public String name;", "}"))); new StubJar(jar).writeTo(filesystem, stubJar); AbiClass stubbed = readClass(stubJar, "com/example/buck/A.class"); FieldNode field = stubbed.findField("name"); List<AnnotationNode> seen = field.visibleAnnotations; assertEquals(1, seen.size()); assertEquals("Lcom/example/buck/Foo;", seen.get(0).desc); } @Test public void preservesAnnotationsOnParameters() throws IOException { Path annotations = buildAnnotationJar(); Path jar = compileToJar(ImmutableSortedSet.of(annotations), "A.java", Joiner.on("\n").join(ImmutableList.of("package com.example.buck;", "public class A {", " public void peynir(@Foo String very, int tasty) {}", "}"))); new StubJar(jar).writeTo(filesystem, stubJar); AbiClass stubbed = readClass(stubJar, "com/example/buck/A.class"); MethodNode method = stubbed.findMethod("peynir"); List<AnnotationNode>[] parameterAnnotations = method.visibleParameterAnnotations; assertEquals(2, parameterAnnotations.length); } @Test public void preservesAnnotationsWithPrimitiveValues() throws IOException { Path annotations = buildAnnotationJar(); Path jar = compileToJar(ImmutableSortedSet.of(annotations), "A.java", Joiner.on("\n") .join("package com.example.buck;", "@Foo(primitiveValue=1)", "public @interface A {}")); new StubJar(jar).writeTo(filesystem, stubJar); // Examine the jar to see if the "A" class is deprecated. ClassNode classNode = readClass(stubJar, "com/example/buck/A.class").getClassNode(); List<AnnotationNode> classAnnotations = classNode.visibleAnnotations; assertEquals(1, classAnnotations.size()); AnnotationNode annotation = classAnnotations.get(0); assertNotNull(annotation.values); assertEquals(2, annotation.values.size()); assertEquals("primitiveValue", annotation.values.get(0)); assertEquals(1, annotation.values.get(1)); } @Test public void preservesAnnotationsWithStringArrayValues() throws IOException { Path annotations = buildAnnotationJar(); Path jar = compileToJar(ImmutableSortedSet.of(annotations), "A.java", Joiner.on("\n").join( "package com.example.buck;", "@Foo(stringArrayValue={\"1\", \"2\"})", "public @interface A {}")); new StubJar(jar).writeTo(filesystem, stubJar); // Examine the jar to see if the "A" class is deprecated. ClassNode classNode = readClass(stubJar, "com/example/buck/A.class").getClassNode(); List<AnnotationNode> classAnnotations = classNode.visibleAnnotations; assertEquals(1, classAnnotations.size()); AnnotationNode annotation = classAnnotations.get(0); assertNotNull(annotation.values); assertEquals(2, annotation.values.size()); assertEquals("stringArrayValue", annotation.values.get(0)); assertEquals(ImmutableList.of("1", "2"), annotation.values.get(1)); } @Test public void preservesAnnotationsWithEnumValues() throws IOException { Path jar = compileToJar(EMPTY_CLASSPATH, "A.java", Joiner.on("\n").join("package com.example.buck;", "import java.lang.annotation.*;", "@Retention(RetentionPolicy.RUNTIME)", "public @interface A {}")); new StubJar(jar).writeTo(filesystem, stubJar); // Examine the jar to see if the "A" class is deprecated. ClassNode classNode = readClass(stubJar, "com/example/buck/A.class").getClassNode(); List<AnnotationNode> classAnnotations = classNode.visibleAnnotations; assertEquals(1, classAnnotations.size()); AnnotationNode annotation = classAnnotations.get(0); assertNotNull(annotation.values); assertEquals(2, annotation.values.size()); assertEquals("value", annotation.values.get(0)); assertEnumAnnotationValue(annotation.values, 1, "Ljava/lang/annotation/RetentionPolicy;", "RUNTIME"); } @Test public void preservesAnnotationsWithEnumArrayValues() throws IOException { Path jar = compileToJar(EMPTY_CLASSPATH, "A.java", Joiner.on("\n").join("package com.example.buck;", "import java.lang.annotation.*;", "@Target({ElementType.CONSTRUCTOR, ElementType.FIELD})", "public @interface A {}")); new StubJar(jar).writeTo(filesystem, stubJar); // Examine the jar to see if the "A" class is deprecated. ClassNode classNode = readClass(stubJar, "com/example/buck/A.class").getClassNode(); List<AnnotationNode> classAnnotations = classNode.visibleAnnotations; assertEquals(1, classAnnotations.size()); AnnotationNode annotation = classAnnotations.get(0); assertNotNull(annotation.values); assertEquals(2, annotation.values.size()); assertEquals("value", annotation.values.get(0)); @SuppressWarnings("unchecked") List<Object> enumArray = (List<Object>) annotation.values.get(1); assertEquals(2, enumArray.size()); assertEnumAnnotationValue(enumArray, 0, "Ljava/lang/annotation/ElementType;", "CONSTRUCTOR"); assertEnumAnnotationValue(enumArray, 1, "Ljava/lang/annotation/ElementType;", "FIELD"); } @Test public void preservesAnnotationsWithAnnotationValues() throws IOException { Path annotations = buildAnnotationJar(); Path jar = compileToJar(ImmutableSortedSet.of(annotations), "A.java", Joiner.on("\n").join("package com.example.buck;", "import java.lang.annotation.*;", "@Foo(annotationValue=@Retention(RetentionPolicy.RUNTIME))", "public @interface A {}")); new StubJar(jar).writeTo(filesystem, stubJar); // Examine the jar to see if the "A" class is deprecated. ClassNode classNode = readClass(stubJar, "com/example/buck/A.class").getClassNode(); List<AnnotationNode> classAnnotations = classNode.visibleAnnotations; assertEquals(1, classAnnotations.size()); AnnotationNode annotation = classAnnotations.get(0); assertNotNull(annotation.values); assertEquals(2, annotation.values.size()); assertEquals("annotationValue", annotation.values.get(0)); AnnotationNode nestedAnnotation = (AnnotationNode) annotation.values.get(1); assertEquals("Ljava/lang/annotation/Retention;", nestedAnnotation.desc); assertNotNull(nestedAnnotation.values); assertEquals(2, nestedAnnotation.values.size()); assertEquals("value", nestedAnnotation.values.get(0)); assertEnumAnnotationValue(nestedAnnotation.values, 1, "Ljava/lang/annotation/RetentionPolicy;", "RUNTIME"); } @Test public void preservesAnnotationsWithAnnotationArrayValues() throws IOException { Path annotations = buildAnnotationJar(); Path jar = compileToJar(ImmutableSortedSet.of(annotations), "A.java", Joiner.on("\n").join("package com.example.buck;", "import java.lang.annotation.*;", "@Foo(annotationArrayValue=@Retention(RetentionPolicy.RUNTIME))", "public @interface A {}")); new StubJar(jar).writeTo(filesystem, stubJar); // Examine the jar to see if the "A" class is deprecated. ClassNode classNode = readClass(stubJar, "com/example/buck/A.class").getClassNode(); List<AnnotationNode> classAnnotations = classNode.visibleAnnotations; assertEquals(1, classAnnotations.size()); AnnotationNode annotation = classAnnotations.get(0); assertNotNull(annotation.values); assertEquals(2, annotation.values.size()); assertEquals("annotationArrayValue", annotation.values.get(0)); @SuppressWarnings("unchecked") List<Object> annotationArray = (List<Object>) annotation.values.get(1); assertEquals(1, annotationArray.size()); AnnotationNode nestedAnnotation = (AnnotationNode) annotationArray.get(0); assertEquals("Ljava/lang/annotation/Retention;", nestedAnnotation.desc); assertNotNull(nestedAnnotation.values); assertEquals(2, nestedAnnotation.values.size()); assertEquals("value", nestedAnnotation.values.get(0)); assertEnumAnnotationValue(nestedAnnotation.values, 1, "Ljava/lang/annotation/RetentionPolicy;", "RUNTIME"); } private void assertEnumAnnotationValue(List<Object> annotationValueList, int index, String enumType, String enumValue) { String[] enumArray = (String[]) annotationValueList.get(index); assertEquals(enumType, enumArray[0]); assertEquals(enumValue, enumArray[1]); } @Test public void stubsInnerClasses() throws IOException { Path jar = compileToJar(EMPTY_CLASSPATH, "A.java", Joiner.on("\n").join(ImmutableList.of("package com.example.buck;", "public class A {", " public class B {", " public int count;", " public void foo() {}", " }", "}"))); new StubJar(jar).writeTo(filesystem, stubJar); AbiClass original = readClass(jar, "com/example/buck/A$B.class"); AbiClass stubbed = readClass(stubJar, "com/example/buck/A$B.class"); MethodNode originalFoo = original.findMethod("foo"); MethodNode stubbedFoo = stubbed.findMethod("foo"); assertMethodEquals(originalFoo, stubbedFoo); FieldNode originalCount = original.findField("count"); FieldNode stubbedCount = stubbed.findField("count"); assertFieldEquals(originalCount, stubbedCount); } @Test public void abiSafeChangesResultInTheSameOutputJar() throws IOException { Path jar = compileToJar(EMPTY_CLASSPATH, "A.java", Joiner.on("\n") .join(ImmutableList.of("package com.example.buck;", "public class A {", " protected final static int count = 42;", " public String getGreeting() { return \"hello\"; }", " Class<?> clazz;", " public int other;", "}"))); new StubJar(jar).writeTo(filesystem, stubJar); String originalHash = filesystem.computeSha1(stubJar); Path jar2 = compileToJar(EMPTY_CLASSPATH, "A.java", Joiner.on("\n").join(ImmutableList.of("package com.example.buck;", "public class A {", " Class<?> clazz = String.class;", " public String getGreeting() { return \"merhaba\"; }", " protected final static int count = 42;", " public int other = 32;", "}"))); filesystem.deleteFileAtPath(stubJar); new StubJar(jar2).writeTo(filesystem, stubJar); String secondHash = filesystem.computeSha1(stubJar); assertEquals(originalHash, secondHash); } @Test public void shouldIncludeStaticFields() throws IOException { Path jar = compileToJar(EMPTY_CLASSPATH, "A.java", Joiner.on("\n") .join(ImmutableList.of("package com.example.buck;", "public class A {", " public static String foo;", " public final static int count = 42;", " protected static void method() {}", "}"))); new StubJar(jar).writeTo(filesystem, stubJar); AbiClass stubbed = readClass(stubJar, "com/example/buck/A.class"); stubbed.findMethod("method"); // Presence is enough stubbed.findField("foo"); // Presence is enough FieldNode count = stubbed.findField("count"); assertEquals(42, count.value); } @Test public void innerClassesInStubsCanBeCompiledAgainst() throws IOException { Path original = compileToJar(EMPTY_CLASSPATH, "Outer.java", Joiner.on("\n") .join(ImmutableList.of("package com.example.buck;", "public class Outer {", " public class Inner {", " public String getGreeting() { return \"hola\"; }", " }", "}"))); new StubJar(original).writeTo(filesystem, stubJar); compileToJar(ImmutableSortedSet.of(stubJar), "A.java", Joiner.on("\n").join(ImmutableList.of("package com.example.buck2;", // Note: different package "import com.example.buck.Outer;", // Inner class becomes available "public class A {", " private Outer.Inner field;", // Reference the inner class "}"))); } @Test public void shouldPreserveSynchronizedKeywordOnMethods() throws IOException { Path original = compileToJar(EMPTY_CLASSPATH, "A.java", Joiner.on("\n").join(ImmutableList.of("package com.example.buck;", "public class A {", " public synchronized void doMagic() {}", "}"))); new StubJar(original).writeTo(filesystem, stubJar); AbiClass stub = readClass(stubJar, "com/example/buck/A.class"); MethodNode magic = stub.findMethod("doMagic"); assertTrue((magic.access & Opcodes.ACC_SYNCHRONIZED) > 0); } @Test public void shouldKeepMultipleFieldsWithSameDescValue() throws IOException { Path original = compileToJar(EMPTY_CLASSPATH, "A.java", Joiner.on("\n") .join(ImmutableList.of("package com.example.buck;", "public class A {", " public static final A SEVERE = new A();", " public static final A NOT_SEVERE = new A();", " public static final A QUITE_MILD = new A();", "}"))); new StubJar(original).writeTo(filesystem, stubJar); AbiClass stubbed = readClass(stubJar, "com/example/buck/A.class"); stubbed.findField("SEVERE"); stubbed.findField("NOT_SEVERE"); stubbed.findField("QUITE_MILD"); } @Test public void stubJarIsEquallyAtHomeWalkingADirectoryOfClassFiles() throws IOException { Path jar = compileToJar(EMPTY_CLASSPATH, "A.java", Joiner.on("\n").join(ImmutableList.of("package com.example.buck;", "public class A {", " public String toString() { return null; }", " public void eatCake() {}", "}"))); Path classDir = temp.newFolder().toPath(); Unzip.extractZipFile(jar, classDir, Unzip.ExistingFileMode.OVERWRITE); new StubJar(classDir).writeTo(filesystem, stubJar); // Verify that both methods are present and given in alphabetical order. AbiClass classNode = readClass(stubJar, "com/example/buck/A.class"); List<MethodNode> methods = classNode.getClassNode().methods; // Index 0 is the <init> method. Skip that. assertEquals("eatCake", methods.get(1).name); assertEquals("toString", methods.get(2).name); } @Test public void shouldIncludeBridgeMethods() throws IOException { Path original = compileToJar(EMPTY_CLASSPATH, "A.java", Joiner.on('\n') .join(ImmutableList.of("package com.example.buck;", "public class A implements Comparable<A> {", " public int compareTo(A other) {", " return 0;", " }", "}"))); new StubJar(original).writeTo(filesystem, stubJar); AbiClass stubbed = readClass(stubJar, "com/example/buck/A.class"); int count = 0; for (MethodNode method : stubbed.getClassNode().methods) { if ("compareTo".equals(method.name)) { count++; } } // One for the generics method, one for the bridge method from Comparable assertEquals(2, count); } private Path compileToJar(SortedSet<Path> classpath, String fileName, String source) throws IOException { File inputs = temp.newFolder(); File file = new File(inputs, fileName); Files.write(file.toPath(), source.getBytes(StandardCharsets.UTF_8)); JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null); Iterable<? extends JavaFileObject> sourceObjects = fileManager .getJavaFileObjectsFromFiles(ImmutableSet.of(file)); final File outputDir = temp.newFolder(); List<String> args = Lists.newArrayList("-g", "-d", outputDir.getAbsolutePath()); if (!classpath.isEmpty()) { args.add("-classpath"); args.add(Joiner.on(File.pathSeparator) .join(FluentIterable.from(classpath).transform(filesystem.getAbsolutifier()))); } JavaCompiler.CompilationTask compilation = compiler.getTask(null, fileManager, null, args, null, sourceObjects); Boolean result = compilation.call(); fileManager.close(); assertNotNull(result); assertTrue(result); File jar = new File(outputDir, "output.jar"); try (FileOutputStream fos = new FileOutputStream(jar); final JarOutputStream os = new JarOutputStream(fos)) { SimpleFileVisitor<Path> visitor = new SimpleFileVisitor<Path>() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { if (file.getFileName().toString().endsWith(".class")) { ZipEntry entry = new ZipEntry(outputDir.toPath().relativize(file).toString()); os.putNextEntry(entry); ByteStreams.copy(Files.newInputStream(file), os); os.closeEntry(); } return FileVisitResult.CONTINUE; } }; Files.walkFileTree(outputDir.toPath(), visitor); } return jar.toPath().toAbsolutePath(); } private AbiClass readClass(Path pathToJar, String className) throws IOException { return AbiClass.extract(filesystem.getPathForRelativePath(pathToJar), className); } private Path buildAnnotationJar() throws IOException { return compileToJar(EMPTY_CLASSPATH, "Foo.java", Joiner.on("\n").join(ImmutableList.of("package com.example.buck;", "import java.lang.annotation.*;", "import static java.lang.annotation.ElementType.*;", "@Retention(RetentionPolicy.RUNTIME)", "@Target(value={CONSTRUCTOR, FIELD, METHOD, PARAMETER, TYPE})", "public @interface Foo {", " int primitiveValue() default 0;", " String[] stringArrayValue() default {};", " Retention annotationValue() default @Retention(RetentionPolicy.SOURCE);", " Retention[] annotationArrayValue() default {};", "}"))); } private void assertMethodEquals(MethodNode expected, MethodNode seen) { assertEquals(expected.access, seen.access); assertEquals(expected.desc, seen.desc); assertEquals(expected.signature, seen.signature); } private void assertFieldEquals(FieldNode expected, FieldNode seen) { assertEquals(expected.name, seen.name); assertEquals(expected.desc, seen.desc); assertEquals(expected.signature, seen.signature); } }