Merge remote-tracking branch 'aosp/upstream-main' into aosp/master am: 1317f29ad0 am: 8742e9d19f am: 210c7d0174

Original change: https://android-review.googlesource.com/c/platform/external/turbine/+/2327873

Change-Id: I6dec4d2861f797e01e4f2fa71e0b001740c03849
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index c5cea7a..54db52c 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -29,19 +29,19 @@
       fail-fast: false
       matrix:
         os: [ ubuntu-latest ]
-        java: [ 18, 17, 11 ]
+        java: [ 19, 17, 11.0.16 ]
         experimental: [ false ]
         include:
           # Only test on macos and windows with a single recent JDK to avoid a
           # combinatorial explosion of test configurations.
           - os: macos-latest
-            java: 18
+            java: 19
             experimental: false
           - os: windows-latest
-            java: 18
+            java: 19
             experimental: false
           - os: ubuntu-latest
-            java: 19-ea
+            java: 20-ea
             experimental: true
     runs-on: ${{ matrix.os }}
     continue-on-error: ${{ matrix.experimental }}
diff --git a/java/com/google/turbine/binder/FileManagerClassBinder.java b/java/com/google/turbine/binder/FileManagerClassBinder.java
index d36d2d8..a807dd7 100644
--- a/java/com/google/turbine/binder/FileManagerClassBinder.java
+++ b/java/com/google/turbine/binder/FileManagerClassBinder.java
@@ -19,7 +19,6 @@
 import com.google.common.base.Joiner;
 import com.google.common.base.Supplier;
 import com.google.common.collect.ImmutableMap;
-import com.google.common.io.ByteStreams;
 import com.google.turbine.binder.bound.ModuleInfo;
 import com.google.turbine.binder.bytecode.BytecodeBoundClass;
 import com.google.turbine.binder.env.Env;
@@ -113,7 +112,7 @@
                   @Override
                   public byte[] get() {
                     try {
-                      return ByteStreams.toByteArray(jfo.openInputStream());
+                      return jfo.openInputStream().readAllBytes();
                     } catch (IOException e) {
                       throw new UncheckedIOException(e);
                     }
@@ -162,7 +161,7 @@
         @Override
         public byte[] get() {
           try {
-            return ByteStreams.toByteArray(fileObject.openInputStream());
+            return fileObject.openInputStream().readAllBytes();
           } catch (IOException e) {
             throw new UncheckedIOException(e);
           }
diff --git a/java/com/google/turbine/bytecode/ByteReader.java b/java/com/google/turbine/bytecode/ByteReader.java
index a9deff2..5458b49 100644
--- a/java/com/google/turbine/bytecode/ByteReader.java
+++ b/java/com/google/turbine/bytecode/ByteReader.java
@@ -20,11 +20,9 @@
 
 import com.google.common.io.ByteArrayDataInput;
 import com.google.common.io.ByteStreams;
-import com.google.errorprone.annotations.CanIgnoreReturnValue;
 import java.io.ByteArrayInputStream;
 
 /** A {@link ByteArrayDataInput} wrapper that tracks the current byte array index. */
-@CanIgnoreReturnValue
 public class ByteReader {
 
   private final byte[] bytes;
diff --git a/java/com/google/turbine/bytecode/ClassReader.java b/java/com/google/turbine/bytecode/ClassReader.java
index 740026a..da35196 100644
--- a/java/com/google/turbine/bytecode/ClassReader.java
+++ b/java/com/google/turbine/bytecode/ClassReader.java
@@ -159,7 +159,7 @@
   /** Reads a JVMS 4.7.9 Signature attribute. */
   private String readSignature(ConstantPoolReader constantPool) {
     String signature;
-    reader.u4(); // length
+    int unusedLength = reader.u4();
     signature = constantPool.utf8(reader.u2());
     return signature;
   }
@@ -167,7 +167,7 @@
   /** Reads JVMS 4.7.6 InnerClasses attributes. */
   private List<ClassFile.InnerClass> readInnerClasses(
       ConstantPoolReader constantPool, String thisClass) {
-    reader.u4(); // length
+    int unusedLength = reader.u4();
     int numberOfClasses = reader.u2();
     List<ClassFile.InnerClass> innerclasses = new ArrayList<>();
     for (int i = 0; i < numberOfClasses; i++) {
@@ -197,7 +197,7 @@
   private void readAnnotations(
       ImmutableList.Builder<ClassFile.AnnotationInfo> annotations,
       ConstantPoolReader constantPool) {
-    reader.u4(); // length
+    int unusedLength = reader.u4();
     int numAnnotations = reader.u2();
     for (int n = 0; n < numAnnotations; n++) {
       annotations.add(readAnnotation(constantPool));
@@ -207,7 +207,7 @@
   /** Processes a JVMS 4.7.18 RuntimeVisibleParameterAnnotations attribute */
   public void readParameterAnnotations(
       List<ImmutableList.Builder<AnnotationInfo>> annotations, ConstantPoolReader constantPool) {
-    reader.u4(); // length
+    int unusedLength = reader.u4();
     int numParameters = reader.u1();
     while (annotations.size() < numParameters) {
       annotations.add(ImmutableList.builder());
@@ -223,7 +223,7 @@
   /** Processes a JVMS 4.7.24 MethodParameters attribute. */
   private void readMethodParameters(
       ImmutableList.Builder<ParameterInfo> parameters, ConstantPoolReader constantPool) {
-    reader.u4(); // length
+    int unusedLength = reader.u4();
     int numParameters = reader.u1();
     for (int i = 0; i < numParameters; i++) {
       String name = constantPool.utf8(reader.u2());
@@ -239,7 +239,7 @@
 
   /** Processes a JVMS 4.7.25 Module attribute. */
   private ModuleInfo readModule(ConstantPoolReader constantPool) {
-    reader.u4(); // length
+    int unusedLength = reader.u4();
     String name = constantPool.moduleInfo(reader.u2());
     int flags = reader.u2();
     int versionIndex = reader.u2();
@@ -423,7 +423,7 @@
             signature = readSignature(constantPool);
             break;
           case "AnnotationDefault":
-            reader.u4(); // length
+            int unusedLength = reader.u4();
             defaultValue = readElementValue(constantPool);
             break;
           case "RuntimeInvisibleAnnotations":
@@ -470,7 +470,7 @@
   /** Reads an Exceptions attribute. */
   private ImmutableList<String> readExceptions(ConstantPoolReader constantPool) {
     ImmutableList.Builder<String> exceptions = ImmutableList.builder();
-    reader.u4(); // length
+    int unusedLength = reader.u4();
     int numberOfExceptions = reader.u2();
     for (int exceptionIndex = 0; exceptionIndex < numberOfExceptions; exceptionIndex++) {
       exceptions.add(constantPool.classInfo(reader.u2()));
@@ -496,7 +496,7 @@
         String attributeName = constantPool.utf8(reader.u2());
         switch (attributeName) {
           case "ConstantValue":
-            reader.u4(); // length
+            int unusedLength = reader.u4();
             value = constantPool.constant(reader.u2());
             break;
           case "RuntimeInvisibleAnnotations":
@@ -525,7 +525,7 @@
   }
 
   private String readTurbineTransitiveJar(ConstantPoolReader constantPool) {
-    reader.u4(); // length
+    int unusedLength = reader.u4();
     return constantPool.utf8(reader.u2());
   }
 }
diff --git a/java/com/google/turbine/main/Main.java b/java/com/google/turbine/main/Main.java
index da97bcd..34984a8 100644
--- a/java/com/google/turbine/main/Main.java
+++ b/java/com/google/turbine/main/Main.java
@@ -58,7 +58,6 @@
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.time.LocalDateTime;
-import java.time.ZoneId;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Map;
@@ -433,16 +432,11 @@
   }
 
   /** Normalize timestamps. */
-  static final long DEFAULT_TIMESTAMP =
-      LocalDateTime.of(2010, 1, 1, 0, 0, 0)
-          .atZone(ZoneId.systemDefault())
-          .toInstant()
-          .toEpochMilli();
+  static final LocalDateTime DEFAULT_TIMESTAMP = LocalDateTime.of(2010, 1, 1, 0, 0, 0);
 
   private static void addEntry(JarOutputStream jos, String name, byte[] bytes) throws IOException {
     JarEntry je = new JarEntry(name);
-    // TODO(cushon): switch to setLocalTime after we migrate to JDK 9
-    je.setTime(DEFAULT_TIMESTAMP);
+    je.setTimeLocal(DEFAULT_TIMESTAMP);
     je.setMethod(ZipEntry.STORED);
     je.setSize(bytes.length);
     je.setCrc(Hashing.crc32().hashBytes(bytes).padToLong());
diff --git a/java/com/google/turbine/options/LanguageVersion.java b/java/com/google/turbine/options/LanguageVersion.java
index e2b0ea7..d8641b4 100644
--- a/java/com/google/turbine/options/LanguageVersion.java
+++ b/java/com/google/turbine/options/LanguageVersion.java
@@ -53,7 +53,7 @@
     try {
       return SourceVersion.valueOf("RELEASE_" + source());
     } catch (IllegalArgumentException unused) {
-      throw new IllegalArgumentException("invalid -source version: " + source());
+      return SourceVersion.latestSupported();
     }
   }
 
diff --git a/java/com/google/turbine/parse/ConstExpressionParser.java b/java/com/google/turbine/parse/ConstExpressionParser.java
index 8b7466f..e4aad6b 100644
--- a/java/com/google/turbine/parse/ConstExpressionParser.java
+++ b/java/com/google/turbine/parse/ConstExpressionParser.java
@@ -588,11 +588,11 @@
     }
     eat();
     int pos = position;
-    Tree.ConstVarName constVarName = (Tree.ConstVarName) qualIdent();
-    if (constVarName == null) {
+    Expression constVarName = qualIdent();
+    if (!(constVarName instanceof Tree.ConstVarName)) {
       return null;
     }
-    ImmutableList<Ident> name = constVarName.name();
+    ImmutableList<Ident> name = ((Tree.ConstVarName) constVarName).name();
     ImmutableList.Builder<Tree.Expression> args = ImmutableList.builder();
     if (token == Token.LPAREN) {
       eat();
diff --git a/java/com/google/turbine/parse/Parser.java b/java/com/google/turbine/parse/Parser.java
index 9417801..acf84d7 100644
--- a/java/com/google/turbine/parse/Parser.java
+++ b/java/com/google/turbine/parse/Parser.java
@@ -547,6 +547,7 @@
       switch (token) {
         case IDENT:
           {
+            String javadoc = lexer.javadoc();
             Ident name = eatIdent();
             if (token == Token.LPAREN) {
               dropParens();
@@ -569,7 +570,7 @@
                         ImmutableList.of()),
                     name,
                     Optional.<Expression>empty(),
-                    null));
+                    javadoc));
             annos = ImmutableList.builder();
             break;
           }
diff --git a/java/com/google/turbine/processing/TurbineElements.java b/java/com/google/turbine/processing/TurbineElements.java
index b5fd7f4..9b3ea26 100644
--- a/java/com/google/turbine/processing/TurbineElements.java
+++ b/java/com/google/turbine/processing/TurbineElements.java
@@ -384,7 +384,7 @@
       return false;
     }
     TypeMirror a = overrider.asType();
-    TypeMirror b = types.asMemberOf((DeclaredType) type.asType(), overridden);
+    TypeMirror b = types.asMemberOfInternal((DeclaredType) type.asType(), overridden);
     if (b == null) {
       return false;
     }
diff --git a/java/com/google/turbine/processing/TurbineFiler.java b/java/com/google/turbine/processing/TurbineFiler.java
index f440ada..8c522ba 100644
--- a/java/com/google/turbine/processing/TurbineFiler.java
+++ b/java/com/google/turbine/processing/TurbineFiler.java
@@ -24,7 +24,6 @@
 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.diag.SourceFile;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
@@ -380,7 +379,7 @@
 
     @Override
     public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
-      return new String(ByteStreams.toByteArray(openInputStream()), UTF_8);
+      return new String(openInputStream().readAllBytes(), UTF_8);
     }
   }
 
diff --git a/java/com/google/turbine/processing/TurbineTypes.java b/java/com/google/turbine/processing/TurbineTypes.java
index d2068dd..467059c 100644
--- a/java/com/google/turbine/processing/TurbineTypes.java
+++ b/java/com/google/turbine/processing/TurbineTypes.java
@@ -26,18 +26,11 @@
 import com.google.turbine.binder.bound.TypeBoundClass;
 import com.google.turbine.binder.bound.TypeBoundClass.TyVarInfo;
 import com.google.turbine.binder.sym.ClassSymbol;
-import com.google.turbine.binder.sym.FieldSymbol;
-import com.google.turbine.binder.sym.MethodSymbol;
-import com.google.turbine.binder.sym.ParamSymbol;
-import com.google.turbine.binder.sym.RecordComponentSymbol;
 import com.google.turbine.binder.sym.Symbol;
 import com.google.turbine.binder.sym.TyVarSymbol;
 import com.google.turbine.model.TurbineConstantTypeKind;
 import com.google.turbine.model.TurbineTyKind;
-import com.google.turbine.processing.TurbineElement.TurbineExecutableElement;
-import com.google.turbine.processing.TurbineElement.TurbineFieldElement;
 import com.google.turbine.processing.TurbineElement.TurbineTypeElement;
-import com.google.turbine.processing.TurbineElement.TurbineTypeParameterElement;
 import com.google.turbine.processing.TurbineTypeMirror.TurbineDeclaredType;
 import com.google.turbine.processing.TurbineTypeMirror.TurbineErrorType;
 import com.google.turbine.processing.TurbineTypeMirror.TurbineTypeVariable;
@@ -1127,41 +1120,6 @@
                     .build()));
   }
 
-  private static ClassSymbol enclosingClass(Symbol symbol) {
-    switch (symbol.symKind()) {
-      case CLASS:
-        return (ClassSymbol) symbol;
-      case TY_PARAM:
-        return enclosingClass(((TyVarSymbol) symbol).owner());
-      case METHOD:
-        return ((MethodSymbol) symbol).owner();
-      case FIELD:
-        return ((FieldSymbol) symbol).owner();
-      case PARAMETER:
-        return ((ParamSymbol) symbol).owner().owner();
-      case RECORD_COMPONENT:
-        return ((RecordComponentSymbol) symbol).owner();
-      case MODULE:
-      case PACKAGE:
-        throw new IllegalArgumentException(symbol.symKind().toString());
-    }
-    throw new AssertionError(symbol.symKind());
-  }
-
-  private static Type type(Element element) {
-    switch (element.getKind()) {
-      case TYPE_PARAMETER:
-        return TyVar.create(((TurbineTypeParameterElement) element).sym(), ImmutableList.of());
-      case FIELD:
-        return ((TurbineFieldElement) element).info().type();
-      case METHOD:
-      case CONSTRUCTOR:
-        return ((TurbineExecutableElement) element).info().asType();
-      default:
-        throw new UnsupportedOperationException(element.toString());
-    }
-  }
-
   /**
    * Returns the {@link TypeMirror} of the given {@code element} as a member of {@code containing},
    * or else {@code null} if it is not a member.
@@ -1171,13 +1129,24 @@
    */
   @Override
   public TypeMirror asMemberOf(DeclaredType containing, Element element) {
+    TypeMirror result = asMemberOfInternal(containing, element);
+    if (result == null) {
+      throw new IllegalArgumentException(String.format("asMemberOf(%s, %s)", containing, element));
+    }
+    return result;
+  }
+
+  public @Nullable TypeMirror asMemberOfInternal(DeclaredType containing, Element element) {
     ClassTy c = ((TurbineDeclaredType) containing).asTurbineType();
-    ClassSymbol symbol = enclosingClass(((TurbineElement) element).sym());
-    ImmutableList<ClassTy> path = factory.cha().search(c, enclosingClass(symbol));
+    Symbol enclosing = ((TurbineElement) element.getEnclosingElement()).sym();
+    if (!enclosing.symKind().equals(Symbol.Kind.CLASS)) {
+      return null;
+    }
+    ImmutableList<ClassTy> path = factory.cha().search(c, (ClassSymbol) enclosing);
     if (path.isEmpty()) {
       return null;
     }
-    Type type = type(element);
+    Type type = asTurbineType(element.asType());
     for (ClassTy ty : path) {
       ImmutableMap<TyVarSymbol, Type> mapping = getMapping(ty);
       if (mapping == null) {
diff --git a/java/com/google/turbine/type/Type.java b/java/com/google/turbine/type/Type.java
index 085346a..5fbf1b1 100644
--- a/java/com/google/turbine/type/Type.java
+++ b/java/com/google/turbine/type/Type.java
@@ -17,6 +17,7 @@
 package com.google.turbine.type;
 
 import static com.google.common.collect.Iterables.getLast;
+import static java.lang.Math.max;
 import static java.util.Objects.requireNonNull;
 
 import com.google.auto.value.AutoValue;
@@ -144,15 +145,23 @@
       StringBuilder sb = new StringBuilder();
       boolean first = true;
       for (SimpleClassTy c : classes()) {
-        for (AnnoInfo anno : c.annos()) {
-          sb.append(anno);
-          sb.append(' ');
-        }
+        String binaryName = c.sym().binaryName();
         if (!first) {
+          for (AnnoInfo anno : c.annos()) {
+            sb.append(anno);
+            sb.append(' ');
+          }
           sb.append('.');
-          sb.append(c.sym().binaryName().substring(c.sym().binaryName().lastIndexOf('$') + 1));
+          sb.append(binaryName, binaryName.lastIndexOf('$') + 1, binaryName.length());
         } else {
-          sb.append(c.sym().binaryName().replace('/', '.').replace('$', '.'));
+          int idx = max(binaryName.lastIndexOf('/'), binaryName.lastIndexOf('$')) + 1;
+          String name = binaryName.replace('/', '.').replace('$', '.');
+          sb.append(name, 0, idx);
+          for (AnnoInfo anno : c.annos()) {
+            sb.append(anno);
+            sb.append(' ');
+          }
+          sb.append(name, idx, name.length());
         }
         if (!c.targs().isEmpty()) {
           sb.append('<');
diff --git a/java/com/google/turbine/zip/Zip.java b/java/com/google/turbine/zip/Zip.java
index d732b35..c08999b 100644
--- a/java/com/google/turbine/zip/Zip.java
+++ b/java/com/google/turbine/zip/Zip.java
@@ -18,7 +18,6 @@
 
 import static java.nio.charset.StandardCharsets.UTF_8;
 
-import com.google.common.io.ByteStreams;
 import com.google.common.primitives.UnsignedInts;
 import java.io.ByteArrayInputStream;
 import java.io.Closeable;
@@ -332,9 +331,9 @@
         fc.get(bytes);
         if (deflate) {
           bytes =
-              ByteStreams.toByteArray(
-                  new InflaterInputStream(
-                      new ByteArrayInputStream(bytes), new Inflater(/*nowrap=*/ true)));
+              new InflaterInputStream(
+                      new ByteArrayInputStream(bytes), new Inflater(/*nowrap=*/ true))
+                  .readAllBytes();
         }
         return bytes;
       } catch (IOException e) {
diff --git a/javatests/com/google/turbine/binder/bytecode/BytecodeBoundClassTest.java b/javatests/com/google/turbine/binder/bytecode/BytecodeBoundClassTest.java
index 65d973d..e2d54bd 100644
--- a/javatests/com/google/turbine/binder/bytecode/BytecodeBoundClassTest.java
+++ b/javatests/com/google/turbine/binder/bytecode/BytecodeBoundClassTest.java
@@ -24,7 +24,6 @@
 import static java.util.Objects.requireNonNull;
 
 import com.google.common.collect.Iterables;
-import com.google.common.io.ByteStreams;
 import com.google.turbine.binder.bound.TurbineClassValue;
 import com.google.turbine.binder.bound.TypeBoundClass;
 import com.google.turbine.binder.bound.TypeBoundClass.FieldInfo;
@@ -195,7 +194,7 @@
 
   private static byte[] toByteArrayOrDie(InputStream is) {
     try {
-      return ByteStreams.toByteArray(is);
+      return is.readAllBytes();
     } catch (IOException e) {
       throw new UncheckedIOException(e);
     }
diff --git a/javatests/com/google/turbine/deps/TransitiveTest.java b/javatests/com/google/turbine/deps/TransitiveTest.java
index f08e899..3829ddd 100644
--- a/javatests/com/google/turbine/deps/TransitiveTest.java
+++ b/javatests/com/google/turbine/deps/TransitiveTest.java
@@ -26,7 +26,6 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Iterables;
-import com.google.common.io.ByteStreams;
 import com.google.protobuf.ExtensionRegistry;
 import com.google.turbine.bytecode.ClassFile;
 import com.google.turbine.bytecode.ClassFile.InnerClass;
@@ -87,7 +86,7 @@
       Enumeration<JarEntry> entries = jf.entries();
       while (entries.hasMoreElements()) {
         JarEntry je = entries.nextElement();
-        jarEntries.put(je.getName(), ByteStreams.toByteArray(jf.getInputStream(je)));
+        jarEntries.put(je.getName(), jf.getInputStream(je).readAllBytes());
       }
     }
     return jarEntries;
diff --git a/javatests/com/google/turbine/lower/LongStringIntegrationTest.java b/javatests/com/google/turbine/lower/LongStringIntegrationTest.java
index a462b69..7bb61e5 100644
--- a/javatests/com/google/turbine/lower/LongStringIntegrationTest.java
+++ b/javatests/com/google/turbine/lower/LongStringIntegrationTest.java
@@ -44,7 +44,7 @@
   public void test() throws Exception {
     Map<String, byte[]> output =
         runTurbineWithStack(
-            /* stackSize= */ 1,
+            /* stackSize= */ 100_000,
             /* input= */ ImmutableMap.of("Test.java", source()),
             /* classpath= */ ImmutableList.of());
 
@@ -70,7 +70,7 @@
             },
             /* name= */ "turbine",
             stackSize);
-    t.run();
+    t.start();
     t.join();
     return output;
   }
diff --git a/javatests/com/google/turbine/lower/LowerTest.java b/javatests/com/google/turbine/lower/LowerTest.java
index 6d3a6df..a6410db 100644
--- a/javatests/com/google/turbine/lower/LowerTest.java
+++ b/javatests/com/google/turbine/lower/LowerTest.java
@@ -312,7 +312,7 @@
       String attributeName = pool.utf8(reader.u2());
       switch (attributeName) {
         case "Signature":
-          reader.u4(); // length
+          int unusedLength = reader.u4();
           signature = pool.utf8(reader.u2());
           break;
         default:
diff --git a/javatests/com/google/turbine/main/MainTest.java b/javatests/com/google/turbine/main/MainTest.java
index 3504891..c894d9d 100644
--- a/javatests/com/google/turbine/main/MainTest.java
+++ b/javatests/com/google/turbine/main/MainTest.java
@@ -28,7 +28,6 @@
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
-import com.google.common.io.ByteStreams;
 import com.google.common.io.MoreFiles;
 import com.google.protobuf.ExtensionRegistry;
 import com.google.turbine.diag.TurbineError;
@@ -148,7 +147,7 @@
       Enumeration<JarEntry> entries = jf.entries();
       while (entries.hasMoreElements()) {
         JarEntry je = entries.nextElement();
-        data.put(je.getName(), ByteStreams.toByteArray(jf.getInputStream(je)));
+        data.put(je.getName(), jf.getInputStream(je).readAllBytes());
       }
     }
     return data;
diff --git a/javatests/com/google/turbine/options/LanguageVersionTest.java b/javatests/com/google/turbine/options/LanguageVersionTest.java
index 601652c..a5b303d 100644
--- a/javatests/com/google/turbine/options/LanguageVersionTest.java
+++ b/javatests/com/google/turbine/options/LanguageVersionTest.java
@@ -140,8 +140,6 @@
   public void unsupportedSourceVersion() {
     LanguageVersion languageVersion =
         LanguageVersion.fromJavacopts(ImmutableList.of("-source", "9999"));
-    IllegalArgumentException expected =
-        assertThrows(IllegalArgumentException.class, languageVersion::sourceVersion);
-    assertThat(expected).hasMessageThat().contains("invalid -source version:");
+    assertThat(languageVersion.sourceVersion()).isEqualTo(SourceVersion.latestSupported());
   }
 }
diff --git a/javatests/com/google/turbine/parse/CommentParserTest.java b/javatests/com/google/turbine/parse/CommentParserTest.java
index a2f84d5..d10d34d 100644
--- a/javatests/com/google/turbine/parse/CommentParserTest.java
+++ b/javatests/com/google/turbine/parse/CommentParserTest.java
@@ -58,6 +58,13 @@
                     "   * class C",
                     "   */",
                     "  class C {}",
+                    "  /** This is an enum. */",
+                    "  enum E {",
+                    "    /** This is H. */",
+                    "    H,",
+                    "    /** This is I. */",
+                    "    I",
+                    "  }",
                     "}\n"));
     TyDecl decl = getOnlyElement(unit.decls());
     assertThat(decl.javadoc()).isEqualTo(" hello world ");
@@ -68,11 +75,17 @@
                 .collect(toImmutableMap(c -> c.name().value(), c -> c.javadoc())))
         .containsExactly(
             "A", "\n   * This is\n   * class A\n   ",
-            "C", "\n   * This is\n   * class C\n   ");
+            "C", "\n   * This is\n   * class C\n   ",
+            "E", " This is an enum. ");
     TyDecl a = (TyDecl) decl.members().get(0);
     MethDecl f = (MethDecl) a.members().get(0);
     assertThat(f.javadoc()).isEqualTo(" This is a method ");
     VarDecl g = (VarDecl) a.members().get(1);
     assertThat(g.javadoc()).isEqualTo(" This is a field ");
+    TyDecl e = (TyDecl) decl.members().get(3);
+    VarDecl h = (VarDecl) e.members().get(0);
+    assertThat(h.javadoc()).isEqualTo(" This is H. ");
+    VarDecl i = (VarDecl) e.members().get(1);
+    assertThat(i.javadoc()).isEqualTo(" This is I. ");
   }
 }
diff --git a/javatests/com/google/turbine/parse/ParseErrorTest.java b/javatests/com/google/turbine/parse/ParseErrorTest.java
index 0187ce0..4a92648 100644
--- a/javatests/com/google/turbine/parse/ParseErrorTest.java
+++ b/javatests/com/google/turbine/parse/ParseErrorTest.java
@@ -320,6 +320,19 @@
                 "                         ^"));
   }
 
+  @Test
+  public void annotationClassLiteral() {
+    String input = "@interface A { A value() default @Integer.class; }";
+    TurbineError e = assertThrows(TurbineError.class, () -> Parser.parse(input));
+    assertThat(e)
+        .hasMessageThat()
+        .isEqualTo(
+            lines(
+                "<>:1: error: unexpected token: ;",
+                "@interface A { A value() default @Integer.class; }",
+                "                                               ^"));
+  }
+
   private static String lines(String... lines) {
     return Joiner.on(System.lineSeparator()).join(lines);
   }
diff --git a/javatests/com/google/turbine/processing/AbstractTurbineTypesBiFunctionTest.java b/javatests/com/google/turbine/processing/AbstractTurbineTypesBiFunctionTest.java
new file mode 100644
index 0000000..e00673d
--- /dev/null
+++ b/javatests/com/google/turbine/processing/AbstractTurbineTypesBiFunctionTest.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2022 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.processing;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import javax.lang.model.element.Element;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.util.Types;
+
+/**
+ * A combo test for {@link TurbineTypes} that compares the behaviour of bifunctions like {@link
+ * Types#asMemberOf(DeclaredType, Element)} with javac's implementation.
+ */
+abstract class AbstractTurbineTypesBiFunctionTest<T> extends AbstractTurbineTypesTest {
+
+  final String testDescription;
+  final TypesBiFunctionInput javacInput;
+  final TypesBiFunctionInput turbineInput;
+
+  public AbstractTurbineTypesBiFunctionTest(
+      String testDescription, TypesBiFunctionInput javacInput, TypesBiFunctionInput turbineInput) {
+    this.testDescription = testDescription;
+    this.javacInput = javacInput;
+    this.turbineInput = turbineInput;
+  }
+
+  protected void test(String symbol, TypeBiFunction<T> predicate) {
+    assertWithMessage("%s = %s", javacInput.format(symbol), turbineInput.format(symbol))
+        .that(turbineInput.apply(predicate))
+        .isEqualTo(javacInput.apply(predicate));
+  }
+}
diff --git a/javatests/com/google/turbine/processing/AbstractTurbineTypesBiPredicateTest.java b/javatests/com/google/turbine/processing/AbstractTurbineTypesBiPredicateTest.java
index 6ea6e72..08891eb 100644
--- a/javatests/com/google/turbine/processing/AbstractTurbineTypesBiPredicateTest.java
+++ b/javatests/com/google/turbine/processing/AbstractTurbineTypesBiPredicateTest.java
@@ -16,8 +16,6 @@
 
 package com.google.turbine.processing;
 
-import static com.google.common.truth.Truth.assertWithMessage;
-
 import javax.lang.model.type.TypeMirror;
 import javax.lang.model.util.Types;
 
@@ -25,22 +23,10 @@
  * A combo test for {@link TurbineTypes} that compares the behaviour of bipredicates like {@link
  * Types#isSubtype(TypeMirror, TypeMirror)} with javac's implementation.
  */
-abstract class AbstractTurbineTypesBiPredicateTest extends AbstractTurbineTypesTest {
-
-  final String testDescription;
-  final TypesBiFunctionInput javacInput;
-  final TypesBiFunctionInput turbineInput;
-
+abstract class AbstractTurbineTypesBiPredicateTest
+    extends AbstractTurbineTypesBiFunctionTest<Boolean> {
   public AbstractTurbineTypesBiPredicateTest(
       String testDescription, TypesBiFunctionInput javacInput, TypesBiFunctionInput turbineInput) {
-    this.testDescription = testDescription;
-    this.javacInput = javacInput;
-    this.turbineInput = turbineInput;
-  }
-
-  protected void test(String symbol, TypeBiPredicate predicate) {
-    assertWithMessage("%s = %s", javacInput.format(symbol), turbineInput.format(symbol))
-        .that(turbineInput.apply(predicate))
-        .isEqualTo(javacInput.apply(predicate));
+    super(testDescription, javacInput, turbineInput);
   }
 }
diff --git a/javatests/com/google/turbine/processing/AbstractTurbineTypesTest.java b/javatests/com/google/turbine/processing/AbstractTurbineTypesTest.java
index 7d8d479..02df1ec 100644
--- a/javatests/com/google/turbine/processing/AbstractTurbineTypesTest.java
+++ b/javatests/com/google/turbine/processing/AbstractTurbineTypesTest.java
@@ -31,6 +31,7 @@
 import com.google.common.collect.Lists;
 import com.google.common.collect.Multimap;
 import com.google.common.collect.MultimapBuilder;
+import com.google.common.collect.ObjectArrays;
 import com.google.common.collect.Streams;
 import com.google.turbine.binder.Binder;
 import com.google.turbine.binder.Binder.BindingResult;
@@ -93,8 +94,8 @@
     }
   }
 
-  protected interface TypeBiPredicate {
-    boolean apply(Types types, TypeMirror a, TypeMirror b);
+  protected interface TypeBiFunction<T> {
+    T apply(Types types, TypeMirror a, TypeMirror b);
   }
 
   static class TypesBiFunctionInput {
@@ -108,8 +109,8 @@
       this.rhs = rhs;
     }
 
-    boolean apply(TypeBiPredicate predicate) {
-      return predicate.apply(types, lhs, rhs);
+    <T> T apply(TypeBiFunction<T> function) {
+      return function.apply(types, lhs, rhs);
     }
 
     String format(String symbol) {
@@ -232,18 +233,23 @@
         "Float",
         "Double",
       },
-      // type annotations
-      {
-        "@A List<@B Integer>",
-        "@A List",
-        "@A int @B []",
-        "@A List<@A int @B []>",
-        "Map.@A Entry<@B Integer, @C Number>",
-      },
     };
+
+    // type annotations
+    List<String> annotatedTypes = new ArrayList<>();
+    annotatedTypes.add("@A int @B []");
+    // The string representation of these types changed in JDK 19, see JDK-8281238
+    if (Runtime.version().feature() >= 19) {
+      annotatedTypes.add("@A List<@B Integer>");
+      annotatedTypes.add("@A List");
+      annotatedTypes.add("@A List<@A int @B []>");
+      annotatedTypes.add("Map.@A Entry<@B Integer, @C Number>");
+    }
+
     List<String> files = new ArrayList<>();
     AtomicInteger idx = new AtomicInteger();
-    for (String[] group : types) {
+    for (String[] group :
+        ObjectArrays.<String[]>concat(annotatedTypes.toArray(new String[0]), types)) {
       StringBuilder sb = new StringBuilder();
       Joiner.on('\n')
           .appendTo(
diff --git a/javatests/com/google/turbine/processing/TurbineFilerTest.java b/javatests/com/google/turbine/processing/TurbineFilerTest.java
index 83dcc70..96c325b 100644
--- a/javatests/com/google/turbine/processing/TurbineFilerTest.java
+++ b/javatests/com/google/turbine/processing/TurbineFilerTest.java
@@ -23,7 +23,6 @@
 
 import com.google.common.base.Function;
 import com.google.common.base.Supplier;
-import com.google.common.io.ByteStreams;
 import com.google.common.io.CharStreams;
 import com.google.turbine.diag.SourceFile;
 import java.io.FileNotFoundException;
@@ -127,8 +126,7 @@
     Collection<SourceFile> unused = filer.finishRound();
 
     FileObject output = filer.getResource(StandardLocation.SOURCE_OUTPUT, "com.foo", "Bar.java");
-    assertThat(new String(ByteStreams.toByteArray(output.openInputStream()), UTF_8))
-        .isEqualTo("hello");
+    assertThat(new String(output.openInputStream().readAllBytes(), UTF_8)).isEqualTo("hello");
     assertThat(output.getCharContent(false).toString()).isEqualTo("hello");
     assertThat(CharStreams.toString(output.openReader(true))).isEqualTo("hello");
   }
@@ -142,8 +140,7 @@
     Collection<SourceFile> unused = filer.finishRound();
 
     FileObject output = filer.getResource(StandardLocation.CLASS_OUTPUT, "com.foo", "Baz.class");
-    assertThat(new String(ByteStreams.toByteArray(output.openInputStream()), UTF_8))
-        .isEqualTo("goodbye");
+    assertThat(new String(output.openInputStream().readAllBytes(), UTF_8)).isEqualTo("goodbye");
     assertThat(output.getCharContent(false).toString()).isEqualTo("goodbye");
     assertThat(CharStreams.toString(output.openReader(true))).isEqualTo("goodbye");
   }
@@ -153,7 +150,7 @@
     FileObject resource =
         filer.getResource(StandardLocation.ANNOTATION_PROCESSOR_PATH, "META-INF", "MANIFEST.MF");
 
-    assertThat(new String(ByteStreams.toByteArray(resource.openInputStream()), UTF_8))
+    assertThat(new String(resource.openInputStream().readAllBytes(), UTF_8))
         .contains("Manifest-Version:");
     assertThat(CharStreams.toString(resource.openReader(true))).contains("Manifest-Version:");
     assertThat(resource.getCharContent(false).toString()).contains("Manifest-Version:");
diff --git a/javatests/com/google/turbine/processing/TurbineTypesAsMemberOfTest.java b/javatests/com/google/turbine/processing/TurbineTypesAsMemberOfTest.java
new file mode 100644
index 0000000..1a368c9
--- /dev/null
+++ b/javatests/com/google/turbine/processing/TurbineTypesAsMemberOfTest.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2022 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.processing;
+
+import static com.google.common.truth.TruthJUnit.assume;
+import static org.junit.Assert.assertThrows;
+
+import javax.lang.model.element.Element;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.type.TypeVariable;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class TurbineTypesAsMemberOfTest extends AbstractTurbineTypesBiFunctionTest<String> {
+
+  @Parameters(name = "{index}: {0}")
+  public static Iterable<Object[]> parameters() throws Exception {
+    return binaryParameters();
+  }
+
+  public TurbineTypesAsMemberOfTest(
+      String testDescription, TypesBiFunctionInput javacInput, TypesBiFunctionInput turbineInput) {
+    super(testDescription, javacInput, turbineInput);
+  }
+
+  @Test
+  public void asMemberOf() {
+    assume().that(javacInput.lhs.getKind()).isEqualTo(TypeKind.DECLARED);
+    assume().that(javacInput.rhs.getKind()).isAnyOf(TypeKind.TYPEVAR, TypeKind.DECLARED);
+
+    TypeBiFunction<String> predicate =
+        (types, lhs, rhs) -> types.asMemberOf((DeclaredType) lhs, element(rhs)).toString();
+
+    try {
+      String unused = javacInput.apply(predicate);
+    } catch (IllegalArgumentException e) {
+      assertThrows(
+          turbineInput.format("asMemberOf"),
+          IllegalArgumentException.class,
+          () -> turbineInput.apply(predicate));
+      return;
+    }
+
+    test("asMemberOf", predicate);
+  }
+
+  private static Element element(TypeMirror rhs) {
+    switch (rhs.getKind()) {
+      case TYPEVAR:
+        return ((TypeVariable) rhs).asElement();
+      case DECLARED:
+        return ((DeclaredType) rhs).asElement();
+      default:
+        throw new AssertionError(rhs.getKind());
+    }
+  }
+}
diff --git a/javatests/com/google/turbine/testing/TestResources.java b/javatests/com/google/turbine/testing/TestResources.java
index 86c7632..6c456ab 100644
--- a/javatests/com/google/turbine/testing/TestResources.java
+++ b/javatests/com/google/turbine/testing/TestResources.java
@@ -19,7 +19,6 @@
 import static java.nio.charset.StandardCharsets.UTF_8;
 import static java.util.Objects.requireNonNull;
 
-import com.google.common.io.ByteStreams;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.UncheckedIOException;
@@ -32,7 +31,7 @@
 
   public static byte[] getResourceBytes(Class<?> clazz, String resource) {
     try (InputStream is = requireNonNull(clazz.getResourceAsStream(resource), resource)) {
-      return ByteStreams.toByteArray(is);
+      return is.readAllBytes();
     } catch (IOException e) {
       throw new UncheckedIOException(e);
     }
diff --git a/javatests/com/google/turbine/zip/ZipTest.java b/javatests/com/google/turbine/zip/ZipTest.java
index 2b6636d..b64531a 100644
--- a/javatests/com/google/turbine/zip/ZipTest.java
+++ b/javatests/com/google/turbine/zip/ZipTest.java
@@ -22,7 +22,6 @@
 
 import com.google.common.collect.ImmutableMap;
 import com.google.common.hash.Hashing;
-import com.google.common.io.ByteStreams;
 import java.io.IOException;
 import java.net.URI;
 import java.nio.ByteBuffer;
@@ -116,9 +115,7 @@
         JarEntry je = entries.nextElement();
         result.put(
             je.getName(),
-            Hashing.goodFastHash(128)
-                .hashBytes(ByteStreams.toByteArray(jf.getInputStream(je)))
-                .padToLong());
+            Hashing.goodFastHash(128).hashBytes(jf.getInputStream(je).readAllBytes()).padToLong());
       }
     }
     return result;
diff --git a/pom.xml b/pom.xml
index 38e91d8..a2bf088 100644
--- a/pom.xml
+++ b/pom.xml
@@ -30,14 +30,15 @@
   <url>https://github.com/google/turbine</url>
 
   <properties>
-    <asm.version>9.3</asm.version>
+    <asm.version>9.4</asm.version>
     <guava.version>31.0.1-jre</guava.version>
-    <errorprone.version>2.11.0</errorprone.version>
+    <errorprone.version>2.16</errorprone.version>
     <maven-javadoc-plugin.version>3.3.1</maven-javadoc-plugin.version>
     <maven-source-plugin.version>3.2.1</maven-source-plugin.version>
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     <protobuf.version>3.19.2</protobuf.version>
     <grpc.version>1.43.2</grpc.version>
+    <native.maven.plugin.version>0.9.11</native.maven.plugin.version>
   </properties>
 
   <organization>
@@ -333,5 +334,44 @@
         </plugins>
       </build>
     </profile>
+    <profile>
+      <id>native</id>
+      <build>
+        <plugins>
+          <plugin>
+            <groupId>org.graalvm.buildtools</groupId>
+            <artifactId>native-maven-plugin</artifactId>
+            <version>${native.maven.plugin.version}</version>
+            <extensions>true</extensions>
+            <executions>
+              <execution>
+                <id>build-native</id>
+                <goals>
+                  <goal>build</goal>
+                </goals>
+                <phase>package</phase>
+              </execution>
+              <execution>
+                <id>test-native</id>
+                <goals>
+                  <goal>test</goal>
+                </goals>
+                <phase>test</phase>
+              </execution>
+            </executions>
+            <configuration>
+              <imageName>turbine</imageName>
+              <mainClass>com.google.turbine.main.Main</mainClass>
+              <classpath>
+                <param>${project.build.directory}/${project.artifactId}-${project.version}-all-deps.jar</param>
+              </classpath>
+              <buildArgs>
+                <buildArg>--no-fallback</buildArg>
+              </buildArgs>
+            </configuration>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
   </profiles>
 </project>