• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2016 Google Inc. All Rights Reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.turbine.lower;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 import static com.google.turbine.testing.TestClassPaths.TURBINE_BOOTCLASSPATH;
21 import static java.nio.charset.StandardCharsets.UTF_8;
22 import static org.junit.Assert.fail;
23 
24 import com.google.common.base.Joiner;
25 import com.google.common.collect.ImmutableList;
26 import com.google.common.collect.ImmutableMap;
27 import com.google.common.io.ByteStreams;
28 import com.google.turbine.binder.Binder;
29 import com.google.turbine.binder.Binder.BindingResult;
30 import com.google.turbine.binder.ClassPathBinder;
31 import com.google.turbine.binder.bound.SourceTypeBoundClass;
32 import com.google.turbine.binder.env.SimpleEnv;
33 import com.google.turbine.binder.sym.ClassSymbol;
34 import com.google.turbine.binder.sym.FieldSymbol;
35 import com.google.turbine.binder.sym.MethodSymbol;
36 import com.google.turbine.binder.sym.TyVarSymbol;
37 import com.google.turbine.bytecode.ByteReader;
38 import com.google.turbine.bytecode.ConstantPoolReader;
39 import com.google.turbine.diag.TurbineError;
40 import com.google.turbine.model.TurbineConstantTypeKind;
41 import com.google.turbine.model.TurbineFlag;
42 import com.google.turbine.model.TurbineTyKind;
43 import com.google.turbine.parse.Parser;
44 import com.google.turbine.testing.AsmUtils;
45 import com.google.turbine.type.Type;
46 import com.google.turbine.type.Type.ClassTy;
47 import com.google.turbine.type.Type.ClassTy.SimpleClassTy;
48 import com.google.turbine.type.Type.IntersectionTy;
49 import com.google.turbine.type.Type.PrimTy;
50 import com.google.turbine.type.Type.TyVar;
51 import java.io.IOException;
52 import java.io.OutputStream;
53 import java.nio.file.Files;
54 import java.nio.file.Path;
55 import java.util.ArrayList;
56 import java.util.LinkedHashMap;
57 import java.util.List;
58 import java.util.Map;
59 import java.util.Optional;
60 import java.util.jar.JarEntry;
61 import java.util.jar.JarOutputStream;
62 import org.junit.Rule;
63 import org.junit.Test;
64 import org.junit.rules.TemporaryFolder;
65 import org.junit.runner.RunWith;
66 import org.junit.runners.JUnit4;
67 import org.objectweb.asm.AnnotationVisitor;
68 import org.objectweb.asm.ClassReader;
69 import org.objectweb.asm.ClassVisitor;
70 import org.objectweb.asm.ClassWriter;
71 import org.objectweb.asm.FieldVisitor;
72 import org.objectweb.asm.Opcodes;
73 import org.objectweb.asm.TypePath;
74 
75 @RunWith(JUnit4.class)
76 public class LowerTest {
77 
78   @Rule public final TemporaryFolder temporaryFolder = new TemporaryFolder();
79 
80   @Test
hello()81   public void hello() throws Exception {
82 
83     ImmutableList<Type> interfaceTypes =
84         ImmutableList.of(
85             ClassTy.create(
86                 ImmutableList.of(
87                     SimpleClassTy.create(
88                         new ClassSymbol("java/util/List"),
89                         ImmutableList.of(
90                             TyVar.create(
91                                 new TyVarSymbol(new ClassSymbol("test/Test"), "V"),
92                                 ImmutableList.of())),
93                         ImmutableList.of()))));
94     Type.ClassTy xtnds = Type.ClassTy.OBJECT;
95     ImmutableMap<TyVarSymbol, SourceTypeBoundClass.TyVarInfo> tps =
96         ImmutableMap.of(
97             new TyVarSymbol(new ClassSymbol("test/Test"), "V"),
98             new SourceTypeBoundClass.TyVarInfo(
99                 IntersectionTy.create(
100                     ImmutableList.of(
101                         ClassTy.create(
102                             ImmutableList.of(
103                                 SimpleClassTy.create(
104                                     new ClassSymbol("test/Test$Inner"),
105                                     ImmutableList.of(),
106                                     ImmutableList.of()))))),
107                 ImmutableList.of()));
108     int access = TurbineFlag.ACC_SUPER | TurbineFlag.ACC_PUBLIC;
109     ImmutableList<SourceTypeBoundClass.MethodInfo> methods =
110         ImmutableList.of(
111             new SourceTypeBoundClass.MethodInfo(
112                 new MethodSymbol(new ClassSymbol("test/Test"), "f"),
113                 ImmutableMap.of(),
114                 PrimTy.create(TurbineConstantTypeKind.INT, ImmutableList.of()),
115                 ImmutableList.of(),
116                 ImmutableList.of(),
117                 TurbineFlag.ACC_STATIC | TurbineFlag.ACC_PUBLIC,
118                 null,
119                 null,
120                 ImmutableList.of(),
121                 null),
122             new SourceTypeBoundClass.MethodInfo(
123                 new MethodSymbol(new ClassSymbol("test/Test"), "g"),
124                 ImmutableMap.of(
125                     new TyVarSymbol(new MethodSymbol(new ClassSymbol("test/Test"), "g"), "V"),
126                     new SourceTypeBoundClass.TyVarInfo(
127                         IntersectionTy.create(
128                             ImmutableList.of(
129                                 ClassTy.create(
130                                     ImmutableList.of(
131                                         SimpleClassTy.create(
132                                             new ClassSymbol("java/lang/Runnable"),
133                                             ImmutableList.of(),
134                                             ImmutableList.of()))))),
135                         ImmutableList.of()),
136                     new TyVarSymbol(new MethodSymbol(new ClassSymbol("test/Test"), "g"), "E"),
137                     new SourceTypeBoundClass.TyVarInfo(
138                         IntersectionTy.create(
139                             ImmutableList.of(
140                                 ClassTy.create(
141                                     ImmutableList.of(
142                                         SimpleClassTy.create(
143                                             new ClassSymbol("java/lang/Error"),
144                                             ImmutableList.of(),
145                                             ImmutableList.of()))))),
146                         ImmutableList.of())),
147                 Type.VOID,
148                 ImmutableList.of(
149                     new SourceTypeBoundClass.ParamInfo(
150                         PrimTy.create(TurbineConstantTypeKind.INT, ImmutableList.of()),
151                         "foo",
152                         ImmutableList.of(),
153                         0)),
154                 ImmutableList.of(
155                     TyVar.create(
156                         new TyVarSymbol(new MethodSymbol(new ClassSymbol("test/Test"), "g"), "E"),
157                         ImmutableList.of())),
158                 TurbineFlag.ACC_PUBLIC,
159                 null,
160                 null,
161                 ImmutableList.of(),
162                 null));
163     ImmutableList<SourceTypeBoundClass.FieldInfo> fields =
164         ImmutableList.of(
165             new SourceTypeBoundClass.FieldInfo(
166                 new FieldSymbol(new ClassSymbol("test/Test"), "theField"),
167                 Type.ClassTy.asNonParametricClassTy(new ClassSymbol("test/Test$Inner")),
168                 TurbineFlag.ACC_STATIC | TurbineFlag.ACC_FINAL | TurbineFlag.ACC_PUBLIC,
169                 ImmutableList.of(),
170                 null,
171                 null));
172     ClassSymbol owner = null;
173     TurbineTyKind kind = TurbineTyKind.CLASS;
174     ImmutableMap<String, ClassSymbol> children = ImmutableMap.of();
175     ImmutableMap<String, TyVarSymbol> tyParams =
176         ImmutableMap.of("V", new TyVarSymbol(new ClassSymbol("test/Test"), "V"));
177 
178     SourceTypeBoundClass c =
179         new SourceTypeBoundClass(
180             interfaceTypes,
181             xtnds,
182             tps,
183             access,
184             methods,
185             fields,
186             owner,
187             kind,
188             children,
189             tyParams,
190             null,
191             null,
192             null,
193             null,
194             ImmutableList.of(),
195             null,
196             null);
197 
198     SourceTypeBoundClass i =
199         new SourceTypeBoundClass(
200             ImmutableList.of(),
201             Type.ClassTy.OBJECT,
202             ImmutableMap.of(),
203             TurbineFlag.ACC_STATIC | TurbineFlag.ACC_PROTECTED,
204             ImmutableList.of(),
205             ImmutableList.of(),
206             new ClassSymbol("test/Test"),
207             TurbineTyKind.CLASS,
208             ImmutableMap.of("Inner", new ClassSymbol("test/Test$Inner")),
209             ImmutableMap.of(),
210             null,
211             null,
212             null,
213             null,
214             ImmutableList.of(),
215             null,
216             null);
217 
218     SimpleEnv.Builder<ClassSymbol, SourceTypeBoundClass> b = SimpleEnv.builder();
219     b.put(new ClassSymbol("test/Test"), c);
220     b.put(new ClassSymbol("test/Test$Inner"), i);
221 
222     Map<String, byte[]> bytes =
223         Lower.lowerAll(
224                 ImmutableMap.of(
225                     new ClassSymbol("test/Test"), c, new ClassSymbol("test/Test$Inner"), i),
226                 ImmutableList.of(),
227                 TURBINE_BOOTCLASSPATH.env())
228             .bytes();
229 
230     assertThat(AsmUtils.textify(bytes.get("test/Test")))
231         .isEqualTo(
232             new String(
233                 ByteStreams.toByteArray(
234                     LowerTest.class.getResourceAsStream("testdata/golden/outer.txt")),
235                 UTF_8));
236     assertThat(AsmUtils.textify(bytes.get("test/Test$Inner")))
237         .isEqualTo(
238             new String(
239                 ByteStreams.toByteArray(
240                     LowerTest.class.getResourceAsStream("testdata/golden/inner.txt")),
241                 UTF_8));
242   }
243 
244   @Test
innerClassAttributeOrder()245   public void innerClassAttributeOrder() throws IOException {
246     BindingResult bound =
247         Binder.bind(
248             ImmutableList.of(
249                 Parser.parse(
250                     Joiner.on('\n')
251                         .join(
252                             "class Test {", //
253                             "  class Inner {",
254                             "    class InnerMost {}",
255                             "  }",
256                             "}"))),
257             ClassPathBinder.bindClasspath(ImmutableList.of()),
258             TURBINE_BOOTCLASSPATH,
259             /* moduleVersion=*/ Optional.empty());
260     Map<String, byte[]> lowered =
261         Lower.lowerAll(bound.units(), bound.modules(), bound.classPathEnv()).bytes();
262     List<String> attributes = new ArrayList<>();
263     new ClassReader(lowered.get("Test$Inner$InnerMost"))
264         .accept(
265             new ClassVisitor(Opcodes.ASM7) {
266               @Override
267               public void visitInnerClass(
268                   String name, String outerName, String innerName, int access) {
269                 attributes.add(String.format("%s %s %s", name, outerName, innerName));
270               }
271             },
272             0);
273     assertThat(attributes)
274         .containsExactly("Test$Inner Test Inner", "Test$Inner$InnerMost Test$Inner InnerMost")
275         .inOrder();
276   }
277 
278   @Test
wildArrayElement()279   public void wildArrayElement() throws Exception {
280     IntegrationTestSupport.TestInput input =
281         IntegrationTestSupport.TestInput.parse(
282             new String(
283                 ByteStreams.toByteArray(
284                     getClass().getResourceAsStream("testdata/canon_array.test")),
285                 UTF_8));
286 
287     Map<String, byte[]> actual =
288         IntegrationTestSupport.runTurbine(input.sources, ImmutableList.of());
289 
290     ByteReader reader = new ByteReader(actual.get("Test"), 0);
291     assertThat(reader.u4()).isEqualTo(0xcafebabe); // magic
292     assertThat(reader.u2()).isEqualTo(0); // minor
293     assertThat(reader.u2()).isEqualTo(52); // major
294     ConstantPoolReader pool = ConstantPoolReader.readConstantPool(reader);
295     assertThat(reader.u2()).isEqualTo(TurbineFlag.ACC_SUPER); // access
296     assertThat(pool.classInfo(reader.u2())).isEqualTo("Test"); // this
297     assertThat(pool.classInfo(reader.u2())).isEqualTo("java/lang/Object"); // super
298     assertThat(reader.u2()).isEqualTo(0); // interfaces
299     assertThat(reader.u2()).isEqualTo(1); // field count
300     assertThat(reader.u2()).isEqualTo(0); // access
301     assertThat(pool.utf8(reader.u2())).isEqualTo("i"); // name
302     assertThat(pool.utf8(reader.u2())).isEqualTo("LA$I;"); // descriptor
303     int attributesCount = reader.u2();
304     String signature = null;
305     for (int j = 0; j < attributesCount; j++) {
306       String attributeName = pool.utf8(reader.u2());
307       switch (attributeName) {
308         case "Signature":
309           reader.u4(); // length
310           signature = pool.utf8(reader.u2());
311           break;
312         default:
313           reader.skip(reader.u4());
314           break;
315       }
316     }
317     assertThat(signature).isEqualTo("LA<[*>.I;");
318   }
319 
320   @Test
typePath()321   public void typePath() throws Exception {
322     BindingResult bound =
323         Binder.bind(
324             ImmutableList.of(
325                 Parser.parse(
326                     Joiner.on('\n')
327                         .join(
328                             "import java.lang.annotation.ElementType;",
329                             "import java.lang.annotation.Target;",
330                             "import java.util.List;",
331                             "@Target({ElementType.TYPE_USE}) @interface Anno {}",
332                             "class Test {",
333                             "  public @Anno int[][] xs;",
334                             "}"))),
335             ClassPathBinder.bindClasspath(ImmutableList.of()),
336             TURBINE_BOOTCLASSPATH,
337             /* moduleVersion=*/ Optional.empty());
338     Map<String, byte[]> lowered =
339         Lower.lowerAll(bound.units(), bound.modules(), bound.classPathEnv()).bytes();
340     TypePath[] path = new TypePath[1];
341     new ClassReader(lowered.get("Test"))
342         .accept(
343             new ClassVisitor(Opcodes.ASM7) {
344               @Override
345               public FieldVisitor visitField(
346                   int access, String name, String desc, String signature, Object value) {
347                 return new FieldVisitor(Opcodes.ASM7) {
348                   @Override
349                   public AnnotationVisitor visitTypeAnnotation(
350                       int typeRef, TypePath typePath, String desc, boolean visible) {
351                     path[0] = typePath;
352                     return null;
353                   };
354                 };
355               }
356             },
357             0);
358     assertThat(path[0].getLength()).isEqualTo(2);
359     assertThat(path[0].getStep(0)).isEqualTo(TypePath.ARRAY_ELEMENT);
360     assertThat(path[0].getStepArgument(0)).isEqualTo(0);
361     assertThat(path[0].getStep(1)).isEqualTo(TypePath.ARRAY_ELEMENT);
362     assertThat(path[0].getStepArgument(1)).isEqualTo(0);
363   }
364 
365   @Test
invalidConstants()366   public void invalidConstants() throws Exception {
367     Path lib = temporaryFolder.newFile("lib.jar").toPath();
368     try (OutputStream os = Files.newOutputStream(lib);
369         JarOutputStream jos = new JarOutputStream(os)) {
370       jos.putNextEntry(new JarEntry("Lib.class"));
371 
372       ClassWriter cw = new ClassWriter(0);
373       cw.visit(52, Opcodes.ACC_SUPER, "Lib", null, "java/lang/Object", null);
374       cw.visitField(Opcodes.ACC_FINAL | Opcodes.ACC_STATIC, "ZCONST", "Z", null, Integer.MAX_VALUE);
375       cw.visitField(Opcodes.ACC_FINAL | Opcodes.ACC_STATIC, "SCONST", "S", null, Integer.MAX_VALUE);
376       jos.write(cw.toByteArray());
377     }
378 
379     ImmutableMap<String, String> input =
380         ImmutableMap.of(
381             "Test.java",
382             Joiner.on('\n')
383                 .join(
384                     "class Test {",
385                     "  static final short SCONST = Lib.SCONST + 0;",
386                     "  static final boolean ZCONST = Lib.ZCONST || false;",
387                     "}"));
388 
389     Map<String, byte[]> actual = IntegrationTestSupport.runTurbine(input, ImmutableList.of(lib));
390 
391     Map<String, Object> values = new LinkedHashMap<>();
392     new ClassReader(actual.get("Test"))
393         .accept(
394             new ClassVisitor(Opcodes.ASM7) {
395               @Override
396               public FieldVisitor visitField(
397                   int access, String name, String desc, String signature, Object value) {
398                 values.put(name, value);
399                 return super.visitField(access, name, desc, signature, value);
400               }
401             },
402             0);
403 
404     assertThat(values).containsEntry("SCONST", -1);
405     assertThat(values).containsEntry("ZCONST", 1);
406   }
407 
408   @Test
deprecated()409   public void deprecated() throws Exception {
410     BindingResult bound =
411         Binder.bind(
412             ImmutableList.of(Parser.parse("@Deprecated class Test {}")),
413             ClassPathBinder.bindClasspath(ImmutableList.of()),
414             TURBINE_BOOTCLASSPATH,
415             /* moduleVersion=*/ Optional.empty());
416     Map<String, byte[]> lowered =
417         Lower.lowerAll(bound.units(), bound.modules(), bound.classPathEnv()).bytes();
418     int[] acc = {0};
419     new ClassReader(lowered.get("Test"))
420         .accept(
421             new ClassVisitor(Opcodes.ASM7) {
422               @Override
423               public void visit(
424                   int version,
425                   int access,
426                   String name,
427                   String signature,
428                   String superName,
429                   String[] interfaces) {
430                 acc[0] = access;
431               }
432             },
433             0);
434     assertThat((acc[0] & Opcodes.ACC_DEPRECATED)).isEqualTo(Opcodes.ACC_DEPRECATED);
435   }
436 
437   @Test
lazyImports()438   public void lazyImports() throws Exception {
439     ImmutableMap<String, String> sources =
440         ImmutableMap.<String, String>builder()
441             .put(
442                 "b/B.java",
443                 lines(
444                     "package b;", //
445                     "public class B {",
446                     "  public static class A {",
447                     "    public static final int X = 0;",
448                     "  }",
449                     "  public static class C {}",
450                     "}"))
451             .put(
452                 "anno/Anno.java",
453                 lines(
454                     "package anno;", //
455                     "public @interface Anno {",
456                     "  int value() default 0;",
457                     "}"))
458             .put(
459                 "a/A.java",
460                 lines(
461                     "package a;", //
462                     "import b.B;",
463                     "import anno.Anno;",
464                     "import static b.B.nosuch.A;",
465                     "@Anno(A.X)",
466                     "public class A extends B {",
467                     "  public A a;",
468                     "  public static final int X = 1;",
469                     "}"))
470             .put(
471                 "a/C.java",
472                 lines(
473                     "package c;", //
474                     "import static b.B.nosuch.C;",
475                     "class C {",
476                     "  C c;",
477                     "}"))
478             .build();
479 
480     ImmutableMap<String, String> noImports;
481     {
482       ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();
483       sources.forEach(
484           (k, v) -> builder.put(k, v.replaceAll("import static b\\.B\\.nosuch\\..*;", "")));
485       noImports = builder.build();
486     }
487 
488     Map<String, byte[]> expected = IntegrationTestSupport.runJavac(noImports, ImmutableList.of());
489     Map<String, byte[]> actual = IntegrationTestSupport.runTurbine(sources, ImmutableList.of());
490     assertThat(IntegrationTestSupport.dump(IntegrationTestSupport.sortMembers(actual)))
491         .isEqualTo(IntegrationTestSupport.dump(IntegrationTestSupport.canonicalize(expected)));
492   }
493 
494   @Test
missingOuter()495   public void missingOuter() throws Exception {
496 
497     Map<String, byte[]> lib =
498         IntegrationTestSupport.runJavac(
499             ImmutableMap.of(
500                 "A.java",
501                     lines(
502                         "interface A {", //
503                         "  interface M {",
504                         "    interface I {}",
505                         "  } ",
506                         "}"),
507                 "B.java",
508                     lines(
509                         "interface B extends A {",
510                         "  interface BM extends M {",
511                         "    interface BI extends I {}",
512                         "  }",
513                         "}")),
514             ImmutableList.of());
515 
516     Path libJar = temporaryFolder.newFile("lib.jar").toPath();
517     try (OutputStream os = Files.newOutputStream(libJar);
518         JarOutputStream jos = new JarOutputStream(os)) {
519       jos.putNextEntry(new JarEntry("A$M.class"));
520       jos.write(lib.get("A$M"));
521       jos.putNextEntry(new JarEntry("A$M$I.class"));
522       jos.write(lib.get("A$M$I"));
523       jos.putNextEntry(new JarEntry("B.class"));
524       jos.write(lib.get("B"));
525       jos.putNextEntry(new JarEntry("B$BM.class"));
526       jos.write(lib.get("B$BM"));
527       jos.putNextEntry(new JarEntry("B$BM$BI.class"));
528       jos.write(lib.get("B$BM$BI"));
529     }
530 
531     ImmutableMap<String, String> sources =
532         ImmutableMap.<String, String>builder()
533             .put(
534                 "Test.java",
535                 lines(
536                     "public class Test extends B.BM {", //
537                     "  I i;",
538                     "}"))
539             .build();
540 
541     try {
542       IntegrationTestSupport.runTurbine(sources, ImmutableList.of(libJar));
543       fail();
544     } catch (TurbineError error) {
545       assertThat(error)
546           .hasMessageThat()
547           .contains("Test.java: error: could not locate class file for A");
548     }
549   }
550 
551   @Test
missingOuter2()552   public void missingOuter2() throws Exception {
553 
554     Map<String, byte[]> lib =
555         IntegrationTestSupport.runJavac(
556             ImmutableMap.of(
557                 "A.java",
558                 lines(
559                     "class A {", //
560                     "  class M { ",
561                     "    class I {} ",
562                     "  } ",
563                     "}"),
564                 "B.java",
565                 lines(
566                     "class B extends A { ",
567                     "  class BM extends M { ",
568                     "    class BI extends I {} ",
569                     "  } ",
570                     "}")),
571             ImmutableList.of());
572 
573     Path libJar = temporaryFolder.newFile("lib.jar").toPath();
574     try (OutputStream os = Files.newOutputStream(libJar);
575         JarOutputStream jos = new JarOutputStream(os)) {
576       jos.putNextEntry(new JarEntry("A$M.class"));
577       jos.write(lib.get("A$M"));
578       jos.putNextEntry(new JarEntry("A$M$I.class"));
579       jos.write(lib.get("A$M$I"));
580       jos.putNextEntry(new JarEntry("B.class"));
581       jos.write(lib.get("B"));
582       jos.putNextEntry(new JarEntry("B$BM.class"));
583       jos.write(lib.get("B$BM"));
584       jos.putNextEntry(new JarEntry("B$BM$BI.class"));
585       jos.write(lib.get("B$BM$BI"));
586     }
587 
588     ImmutableMap<String, String> sources =
589         ImmutableMap.<String, String>builder()
590             .put(
591                 "Test.java",
592                 lines(
593                     "public class Test extends B {", //
594                     "  class M extends BM {",
595                     "     I i;",
596                     "  }",
597                     "}"))
598             .build();
599 
600     try {
601       IntegrationTestSupport.runTurbine(sources, ImmutableList.of(libJar));
602       fail();
603     } catch (TurbineError error) {
604       assertThat(error)
605           .hasMessageThat()
606           .contains(
607               "Test.java:3: error: could not locate class file for A\n"
608                   + "     I i;\n"
609                   + "       ^");
610     }
611   }
612 
613   // If an element incorrectly has multiple visibility modifiers, pick one, and rely on javac to
614   // report a diagnostic.
615   @Test
multipleVisibilities()616   public void multipleVisibilities() throws Exception {
617     ImmutableMap<String, String> sources =
618         ImmutableMap.of("Test.java", "public protected class Test {}");
619 
620     Map<String, byte[]> lowered =
621         IntegrationTestSupport.runTurbine(sources, /* classpath= */ ImmutableList.of());
622     int[] testAccess = {0};
623     new ClassReader(lowered.get("Test"))
624         .accept(
625             new ClassVisitor(Opcodes.ASM7) {
626               @Override
627               public void visit(
628                   int version,
629                   int access,
630                   String name,
631                   String signature,
632                   String superName,
633                   String[] interfaces) {
634                 testAccess[0] = access;
635               }
636             },
637             0);
638     assertThat((testAccess[0] & TurbineFlag.ACC_PUBLIC)).isEqualTo(TurbineFlag.ACC_PUBLIC);
639     assertThat((testAccess[0] & TurbineFlag.ACC_PROTECTED)).isNotEqualTo(TurbineFlag.ACC_PROTECTED);
640   }
641 
lines(String... lines)642   static String lines(String... lines) {
643     return Joiner.on("\n").join(lines);
644   }
645 }
646