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