Merge "Tests for processing of CovariantReturnType annotation"
diff --git a/build.gradle b/build.gradle
index 86ba02f..0f782b2 100644
--- a/build.gradle
+++ b/build.gradle
@@ -514,7 +514,7 @@
     classifier = null
     version = null
     manifest {
-        attributes 'Main-Class': 'com.android.tools.r8.R8'
+        attributes 'Main-Class': 'com.android.tools.r8.SwissArmyKnife'
     }
     // In order to build without dependencies, pass the exclude_deps property using:
     // gradle -Pexclude_deps R8
@@ -527,164 +527,27 @@
 }
 
 task D8(type: ShadowJar) {
-    from consolidatedLicense.outputs.files
-    exclude { path ->
-      path.getRelativePath().getPathString().startsWith("META-INF")
-    }
+    from R8.outputs.files
     baseName 'd8'
-    classifier = null
-    version = null
     manifest {
         attributes 'Main-Class': 'com.android.tools.r8.D8'
     }
-    // In order to build without dependencies, pass the exclude_deps property using:
-    // gradle -Pexclude_deps D8
-    if (!project.hasProperty('exclude_deps')) {
-        from repackageSources.outputs.files
-        from repackageDeps.outputs.files
-    } else {
-        from sourceSets.main.output
-    }
 }
 
-task CompatDx(type: Jar) {
-    from sourceSets.main.output
+task CompatDx(type: ShadowJar) {
+    from R8.outputs.files
     baseName 'compatdx'
     manifest {
       attributes 'Main-Class': 'com.android.tools.r8.compatdx.CompatDx'
     }
-    // In order to build without dependencies, pass the exclude_deps property using:
-    // gradle -Pexclude_deps CompatDx
-    if (!project.hasProperty('exclude_deps')) {
-        // Also include dependencies
-        from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
-    }
 }
 
-task DexFileMerger(type: Jar) {
-    from sourceSets.main.output
-    baseName 'dexfilemerger'
-    manifest {
-      attributes 'Main-Class': 'com.android.tools.r8.dexfilemerger.DexFileMerger'
-    }
-    // In order to build without dependencies, pass the exclude_deps property using:
-    // gradle -Pexclude_deps CompatDx
-    if (!project.hasProperty('exclude_deps')) {
-        // Also include dependencies
-        from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
-    }
-}
-
-task DexSplitter(type: Jar) {
-    from sourceSets.main.output
-    baseName 'dexsplitter'
-    manifest {
-      attributes 'Main-Class': 'com.android.tools.r8.dexsplitter.DexSplitter'
-    }
-    // In order to build without dependencies, pass the exclude_deps property using:
-    // gradle -Pexclude_deps CompatDx
-    if (!project.hasProperty('exclude_deps')) {
-        // Also include dependencies
-        from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
-    }
-}
-
-task CompatProguard(type: Jar) {
-    from sourceSets.main.output
+task CompatProguard(type: ShadowJar) {
+    from R8.outputs.files
     baseName 'compatproguard'
     manifest {
         attributes 'Main-Class': 'com.android.tools.r8.compatproguard.CompatProguard'
     }
-    // In order to build without dependencies, pass the exclude_deps property using:
-    // gradle -Pexclude_deps CompatProguard
-    if (!project.hasProperty('exclude_deps')) {
-        // Also include dependencies
-        from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
-    }
-}
-
-task D8Logger(type: Jar) {
-    from sourceSets.main.output
-    baseName 'd8logger'
-    manifest {
-      attributes 'Main-Class': 'com.android.tools.r8.D8Logger'
-    }
-    // In order to build without dependencies, pass the exclude_deps property using:
-    // gradle -Pexclude_deps D8Logger
-    if (!project.hasProperty('exclude_deps')) {
-        // Also include dependencies
-        from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
-    }
-}
-
-task disasm(type: Jar) {
-    from sourceSets.main.output
-    baseName 'disasm'
-    manifest {
-        attributes 'Main-Class': 'com.android.tools.r8.Disassemble'
-    }
-    // In order to build without dependencies, pass the exclude_deps property using:
-    // gradle -Pexclude_deps D8
-    if (!project.hasProperty('exclude_deps')) {
-        // Also include dependencies
-        from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
-    }
-}
-
-task bisect(type: Jar) {
-    from sourceSets.main.output
-    baseName 'bisect'
-    manifest {
-      attributes 'Main-Class': 'com.android.tools.r8.bisect.Bisect'
-    }
-    // In order to build without dependencies, pass the exclude_deps property using:
-    // gradle -Pexclude_deps R8
-    if (!project.hasProperty('exclude_deps')) {
-        // Also include dependencies
-        from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
-    }
-}
-
-task DexSegments(type: Jar) {
-    from sourceSets.main.output
-    baseName 'dexsegments'
-    manifest {
-      attributes 'Main-Class': 'com.android.tools.r8.DexSegments'
-    }
-    // In order to build without dependencies, pass the exclude_deps property using:
-    // gradle -Pexclude_deps DexSegments
-    if (!project.hasProperty('exclude_deps')) {
-        // Also include dependencies
-        from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
-    }
-}
-
-task maindex(type: Jar) {
-    from sourceSets.main.output
-    baseName 'maindex'
-    manifest {
-      attributes 'Main-Class': 'com.android.tools.r8.GenerateMainDexList'
-    }
-    // In order to build without dependencies, pass the exclude_deps property using:
-    // gradle -Pexclude_deps maindex
-    if (!project.hasProperty('exclude_deps')) {
-        // Also include dependencies
-        from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
-    }
-}
-
-task ExtractMarker(type: Jar) {
-    from sourceSets.main.output
-    baseName 'extractmarker'
-    manifest {
-      attributes 'Main-Class': 'com.android.tools.r8.ExtractMarker'
-    }
-    // In order to build without dependencies, pass the exclude_deps property using:
-    // gradle -Pexclude_deps ExtractMarker
-    if (!project.hasProperty('exclude_deps')) {
-        // Also include dependencies
-        from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
-    }
 }
 
 task bspatch(type: Jar) {
@@ -694,7 +557,7 @@
         attributes 'Main-Class': 'com.android.tools.r8.dex.BSPatch'
     }
     // In order to build without dependencies, pass the exclude_deps property using:
-    // gradle -Pexclude_deps maindex
+    // gradle -Pexclude_deps bspatch
     if (!project.hasProperty('exclude_deps')) {
         // Also include dependencies
         from {
@@ -703,20 +566,6 @@
     }
 }
 
-task jardiff(type: Jar) {
-    from sourceSets.main.output
-    baseName 'jardiff'
-    manifest {
-      attributes 'Main-Class': 'com.android.tools.r8.JarDiff'
-    }
-    // In order to build without dependencies, pass the exclude_deps property using:
-    // gradle -Pexclude_deps jardiff
-    if (!project.hasProperty('exclude_deps')) {
-        // Also include dependencies
-        from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
-    }
-}
-
 task sourceJar(type: Jar, dependsOn: classes) {
     classifier = 'src'
     from sourceSets.main.allSource
diff --git a/src/main/java/com/android/tools/r8/SwissArmyKnife.java b/src/main/java/com/android/tools/r8/SwissArmyKnife.java
new file mode 100644
index 0000000..0e35847
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/SwissArmyKnife.java
@@ -0,0 +1,83 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8;
+
+import com.android.tools.r8.bisect.Bisect;
+import com.android.tools.r8.compatdx.CompatDx;
+import com.android.tools.r8.compatproguard.CompatProguard;
+import com.android.tools.r8.dexfilemerger.DexFileMerger;
+import com.android.tools.r8.dexsplitter.DexSplitter;
+import java.util.Arrays;
+
+/**
+ * Common entry point to everything in the R8 project.
+ *
+ * <p>This class is used as the main class in {@code r8.jar}. It checks the first command-line
+ * argument to find the tool to run, or runs {@link R8} if the first argument is not a recognized
+ * tool name.
+ *
+ * <p>The set of tools recognized by this class is defined by a switch statement in {@link
+ * SwissArmyKnife#main(String[])}.
+ */
+public class SwissArmyKnife {
+
+  public static void main(String[] args) throws Exception {
+    if (args.length == 0) {
+      runDefault(args);
+      return;
+    }
+    switch (args[0]) {
+      case "r8":
+        R8.main(shift(args));
+        break;
+      case "d8":
+        D8.main(shift(args));
+        break;
+      case "compatdx":
+        CompatDx.main(shift(args));
+        break;
+      case "dexfilemerger":
+        DexFileMerger.main(shift(args));
+        break;
+      case "dexsplitter":
+        DexSplitter.main(shift(args));
+        break;
+      case "compatproguard":
+        CompatProguard.main(shift(args));
+        break;
+      case "d8logger":
+        D8Logger.main(shift(args));
+        break;
+      case "disasm":
+        Disassemble.main(shift(args));
+        break;
+      case "bisect":
+        Bisect.main(shift(args));
+        break;
+      case "dexsegments":
+        DexSegments.main(shift(args));
+        break;
+      case "maindex":
+        GenerateMainDexList.main(shift(args));
+        break;
+      case "extractmarker":
+        ExtractMarker.main(shift(args));
+        break;
+      case "jardiff":
+        JarDiff.main(shift(args));
+        break;
+      default:
+        runDefault(args);
+        break;
+    }
+  }
+
+  private static void runDefault(String[] args) {
+    R8.main(args);
+  }
+
+  private static String[] shift(String[] args) {
+    return Arrays.copyOfRange(args, 1, args.length);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
index df16622..382bc66 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -17,7 +17,6 @@
 import com.android.tools.r8.graph.DexAnnotation;
 import com.android.tools.r8.graph.DexAnnotationDirectory;
 import com.android.tools.r8.graph.DexAnnotationSet;
-import com.android.tools.r8.graph.DexAnnotationSetRefList;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexCode;
 import com.android.tools.r8.graph.DexDebugInfo;
@@ -30,6 +29,7 @@
 import com.android.tools.r8.graph.EnclosingMethodAttribute;
 import com.android.tools.r8.graph.InnerClassAttribute;
 import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.graph.ParameterAnnotationsList;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.naming.ProguardMapSupplier;
 import com.android.tools.r8.utils.DescriptorUtils;
@@ -106,7 +106,7 @@
     }
 
     @Override
-    public boolean add(DexAnnotationSetRefList annotationSetRefList) {
+    public boolean add(ParameterAnnotationsList parameterAnnotationsList) {
       return true;
     }
 
diff --git a/src/main/java/com/android/tools/r8/dex/DexParser.java b/src/main/java/com/android/tools/r8/dex/DexParser.java
index 8042a1a..d762646 100644
--- a/src/main/java/com/android/tools/r8/dex/DexParser.java
+++ b/src/main/java/com/android/tools/r8/dex/DexParser.java
@@ -19,7 +19,6 @@
 import com.android.tools.r8.graph.DexAnnotation;
 import com.android.tools.r8.graph.DexAnnotationElement;
 import com.android.tools.r8.graph.DexAnnotationSet;
-import com.android.tools.r8.graph.DexAnnotationSetRefList;
 import com.android.tools.r8.graph.DexCallSite;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexCode;
@@ -56,6 +55,7 @@
 import com.android.tools.r8.graph.InnerClassAttribute;
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.ParameterAnnotationsList;
 import com.android.tools.r8.logging.Log;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.origin.PathOrigin;
@@ -340,11 +340,11 @@
     return result;
   }
 
-  private DexAnnotationSetRefList annotationSetRefListAt(int offset) {
-    return (DexAnnotationSetRefList) cacheAt(offset, this::parseAnnotationSetRefList);
+  private ParameterAnnotationsList annotationSetRefListAt(int offset) {
+    return (ParameterAnnotationsList) cacheAt(offset, this::parseAnnotationSetRefList);
   }
 
-  private DexAnnotationSetRefList parseAnnotationSetRefList() {
+  private ParameterAnnotationsList parseAnnotationSetRefList() {
     int size = dexReader.getUint();
     int[] annotationOffsets = new int[size];
     for (int i = 0; i < size; i++) {
@@ -354,7 +354,7 @@
     for (int i = 0; i < size; i++) {
       values[i] = annotationSetAt(annotationOffsets[i]);
     }
-    return new DexAnnotationSetRefList(values);
+    return new ParameterAnnotationsList(values);
   }
 
   private DexParameterAnnotation[] parseParameterAnnotations(int size) {
@@ -373,7 +373,8 @@
       DexMethod method = indexedItems.getMethod(methodIndices[i]);
       result[i] = new DexParameterAnnotation(
           method,
-          annotationSetRefListAt(annotationOffsets[i]));
+          annotationSetRefListAt(annotationOffsets[i])
+              .withParameterCount(method.proto.parameters.size()));
     }
     dexReader.position(saved);
     return result;
@@ -589,8 +590,8 @@
     int methodIndex = 0;
     MemberAnnotationIterator<DexMethod, DexAnnotationSet> annotationIterator =
         new MemberAnnotationIterator<>(annotations, DexAnnotationSet::empty);
-    MemberAnnotationIterator<DexMethod, DexAnnotationSetRefList> parameterAnnotationsIterator =
-        new MemberAnnotationIterator<>(parameters, DexAnnotationSetRefList::empty);
+    MemberAnnotationIterator<DexMethod, ParameterAnnotationsList> parameterAnnotationsIterator =
+        new MemberAnnotationIterator<>(parameters, ParameterAnnotationsList::empty);
     for (int i = 0; i < size; i++) {
       methodIndex += dexReader.getUleb128();
       MethodAccessFlags accessFlags = MethodAccessFlags.fromDexAccessFlags(dexReader.getUleb128());
diff --git a/src/main/java/com/android/tools/r8/dex/FileWriter.java b/src/main/java/com/android/tools/r8/dex/FileWriter.java
index ced33f6..4767c64 100644
--- a/src/main/java/com/android/tools/r8/dex/FileWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/FileWriter.java
@@ -13,7 +13,6 @@
 import com.android.tools.r8.graph.DexAnnotationDirectory;
 import com.android.tools.r8.graph.DexAnnotationElement;
 import com.android.tools.r8.graph.DexAnnotationSet;
-import com.android.tools.r8.graph.DexAnnotationSetRefList;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexCallSite;
 import com.android.tools.r8.graph.DexClass;
@@ -40,6 +39,7 @@
 import com.android.tools.r8.graph.IndexedDexItem;
 import com.android.tools.r8.graph.KeyedDexItem;
 import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.graph.ParameterAnnotationsList;
 import com.android.tools.r8.graph.PresortedComparable;
 import com.android.tools.r8.graph.ProgramClassVisitor;
 import com.android.tools.r8.logging.Log;
@@ -510,12 +510,17 @@
     }
   }
 
-  private void writeAnnotationSetRefList(DexAnnotationSetRefList setRefList) {
-    assert !setRefList.isEmpty();
-    mixedSectionOffsets.setOffsetFor(setRefList, dest.align(4));
-    dest.putInt(setRefList.values.length);
-    for (DexAnnotationSet set : setRefList.values) {
-      dest.putInt(mixedSectionOffsets.getOffsetFor(set));
+  private void writeAnnotationSetRefList(ParameterAnnotationsList parameterAnnotationsList) {
+    assert !parameterAnnotationsList.isEmpty();
+    mixedSectionOffsets.setOffsetFor(parameterAnnotationsList, dest.align(4));
+    dest.putInt(parameterAnnotationsList.countNonMissing());
+    for (int i = 0; i < parameterAnnotationsList.size(); i++) {
+      if (parameterAnnotationsList.isMissing(i)) {
+        // b/62300145: Maintain broken ParameterAnnotations attribute by only outputting the
+        // non-missing annotation lists.
+        continue;
+      }
+      dest.putInt(mixedSectionOffsets.getOffsetFor(parameterAnnotationsList.get(i)));
     }
   }
 
@@ -541,7 +546,7 @@
     writeMemberAnnotations(methodAnnotations,
         item -> mixedSectionOffsets.getOffsetFor(item.annotations));
     writeMemberAnnotations(parameterAnnotations,
-        item -> mixedSectionOffsets.getOffsetFor(item.parameterAnnotations));
+        item -> mixedSectionOffsets.getOffsetFor(item.parameterAnnotationsList));
   }
 
   private void writeEncodedFields(DexEncodedField[] fields) {
@@ -990,7 +995,7 @@
     private final Reference2IntMap<DexString> stringData = createReference2IntMap();
     private final Object2IntMap<DexAnnotation> annotations = createObject2IntMap();
     private final Object2IntMap<DexAnnotationSet> annotationSets = createObject2IntMap();
-    private final Object2IntMap<DexAnnotationSetRefList> annotationSetRefLists
+    private final Object2IntMap<ParameterAnnotationsList> annotationSetRefLists
         = createObject2IntMap();
     private final Object2IntMap<DexAnnotationDirectory> annotationDirectories
         = createObject2IntMap();
@@ -1072,7 +1077,7 @@
     }
 
     @Override
-    public boolean add(DexAnnotationSetRefList annotationSetRefList) {
+    public boolean add(ParameterAnnotationsList annotationSetRefList) {
       if (annotationSetRefList.isEmpty()) {
         return false;
       }
@@ -1120,7 +1125,7 @@
       return annotationSets.keySet();
     }
 
-    public Collection<DexAnnotationSetRefList> getAnnotationSetRefLists() {
+    public Collection<ParameterAnnotationsList> getAnnotationSetRefLists() {
       return annotationSetRefLists.keySet();
     }
 
@@ -1200,7 +1205,7 @@
       return lookup(annotationSet, annotationSets);
     }
 
-    public int getOffsetFor(DexAnnotationSetRefList annotationSetRefList) {
+    public int getOffsetFor(ParameterAnnotationsList annotationSetRefList) {
       if (annotationSetRefList.isEmpty()) {
         return 0;
       }
@@ -1261,7 +1266,7 @@
       setOffsetFor(encodedArray, offset, encodedArrays);
     }
 
-    void setOffsetFor(DexAnnotationSetRefList annotationSetRefList, int offset) {
+    void setOffsetFor(ParameterAnnotationsList annotationSetRefList, int offset) {
       assert offset != 0 && !annotationSetRefList.isEmpty();
       setOffsetFor(annotationSetRefList, offset, annotationSetRefLists);
     }
diff --git a/src/main/java/com/android/tools/r8/dex/MixedSectionCollection.java b/src/main/java/com/android/tools/r8/dex/MixedSectionCollection.java
index eb7ae98..dee12fc 100644
--- a/src/main/java/com/android/tools/r8/dex/MixedSectionCollection.java
+++ b/src/main/java/com/android/tools/r8/dex/MixedSectionCollection.java
@@ -6,13 +6,13 @@
 import com.android.tools.r8.graph.DexAnnotation;
 import com.android.tools.r8.graph.DexAnnotationDirectory;
 import com.android.tools.r8.graph.DexAnnotationSet;
-import com.android.tools.r8.graph.DexAnnotationSetRefList;
 import com.android.tools.r8.graph.DexCode;
 import com.android.tools.r8.graph.DexDebugInfo;
 import com.android.tools.r8.graph.DexEncodedArray;
 import com.android.tools.r8.graph.DexItem;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexTypeList;
+import com.android.tools.r8.graph.ParameterAnnotationsList;
 
 /**
  * Collection of the various components of the mixed section of a dex file.
@@ -86,7 +86,7 @@
    *
    * @return true if the item was not added before
    */
-  public abstract boolean add(DexAnnotationSetRefList annotationSetRefList);
+  public abstract boolean add(ParameterAnnotationsList annotationSetRefList);
 
   /**
    * Adds the given annotation to the collection.
diff --git a/src/main/java/com/android/tools/r8/graph/CfCode.java b/src/main/java/com/android/tools/r8/graph/CfCode.java
index 1ac2246..61595b9 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -5,23 +5,15 @@
 
 import com.android.tools.r8.ApiLevelException;
 import com.android.tools.r8.cf.CfPrinter;
-import com.android.tools.r8.cf.code.CfConstNull;
 import com.android.tools.r8.cf.code.CfInstruction;
 import com.android.tools.r8.cf.code.CfLabel;
 import com.android.tools.r8.cf.code.CfPosition;
 import com.android.tools.r8.cf.code.CfReturnVoid;
-import com.android.tools.r8.cf.code.CfThrow;
 import com.android.tools.r8.cf.code.CfTryCatch;
 import com.android.tools.r8.errors.Unimplemented;
-import com.android.tools.r8.ir.code.BasicBlock;
-import com.android.tools.r8.ir.code.ConstNumber;
 import com.android.tools.r8.ir.code.IRCode;
-import com.android.tools.r8.ir.code.Instruction;
 import com.android.tools.r8.ir.code.Position;
-import com.android.tools.r8.ir.code.Throw;
-import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.ir.code.ValueNumberGenerator;
-import com.android.tools.r8.ir.code.ValueType;
 import com.android.tools.r8.ir.conversion.CfSourceCode;
 import com.android.tools.r8.ir.conversion.IRBuilder;
 import com.android.tools.r8.naming.ClassNameMapper;
@@ -29,7 +21,6 @@
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.InternalOptions;
 import java.util.Collections;
-import java.util.LinkedList;
 import java.util.List;
 import org.objectweb.asm.Label;
 import org.objectweb.asm.MethodVisitor;
@@ -211,21 +202,6 @@
   @Override
   public IRCode buildIR(DexEncodedMethod encodedMethod, InternalOptions options, Origin origin)
       throws ApiLevelException {
-    if (instructions.size() == 2
-        && instructions.get(0) instanceof CfConstNull
-        && instructions.get(1) instanceof CfThrow) {
-      BasicBlock block = new BasicBlock();
-      block.setNumber(1);
-      Value nullValue = new Value(0, ValueType.OBJECT, null);
-      block.add(new ConstNumber(nullValue, 0L));
-      block.add(new Throw(nullValue));
-      block.close(null);
-      for (Instruction insn : block.getInstructions()) {
-        insn.setPosition(Position.none());
-      }
-      LinkedList<BasicBlock> blocks = new LinkedList<>(Collections.singleton(block));
-      return new IRCode(options, encodedMethod, blocks, null, false);
-    }
     return internalBuild(encodedMethod, options, null, null, origin);
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotationDirectory.java b/src/main/java/com/android/tools/r8/graph/DexAnnotationDirectory.java
index aa5d145..fa5d8a8 100644
--- a/src/main/java/com/android/tools/r8/graph/DexAnnotationDirectory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexAnnotationDirectory.java
@@ -33,7 +33,7 @@
       if (!method.annotations.isEmpty()) {
         methodAnnotations.add(method);
       }
-      if (!method.parameterAnnotations.isEmpty()) {
+      if (!method.parameterAnnotationsList.isEmpty()) {
         parameterAnnotations.add(method);
       }
     }
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java b/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java
index 9ee8fba..d093a20 100644
--- a/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java
+++ b/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java
@@ -5,7 +5,9 @@
 
 import com.android.tools.r8.dex.IndexedItemCollection;
 import com.android.tools.r8.dex.MixedSectionCollection;
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.function.Predicate;
 
 public class DexAnnotationSet extends CachedHashValueDexItem {
 
@@ -115,4 +117,30 @@
     extendedArray[annotations.length] = newAnnotation;
     return new DexAnnotationSet(extendedArray);
   }
+
+  public DexAnnotationSet keepIf(Predicate<DexAnnotation> filter) {
+    ArrayList<DexAnnotation> filtered = null;
+    for (int i = 0; i < annotations.length; i++) {
+      DexAnnotation annotation = annotations[i];
+      if (filter.test(annotation)) {
+        if (filtered != null) {
+          filtered.add(annotation);
+        }
+      } else {
+        if (filtered == null) {
+          filtered = new ArrayList<>(annotations.length);
+          for (int j = 0; j < i; j++) {
+            filtered.add(annotations[j]);
+          }
+        }
+      }
+    }
+    if (filtered == null) {
+      return this;
+    } else if (filtered.isEmpty()) {
+      return DexAnnotationSet.empty();
+    } else {
+      return new DexAnnotationSet(filtered.toArray(new DexAnnotation[filtered.size()]));
+    }
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotationSetRefList.java b/src/main/java/com/android/tools/r8/graph/DexAnnotationSetRefList.java
deleted file mode 100644
index 4bb1314..0000000
--- a/src/main/java/com/android/tools/r8/graph/DexAnnotationSetRefList.java
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.graph;
-
-import com.android.tools.r8.dex.IndexedItemCollection;
-import com.android.tools.r8.dex.MixedSectionCollection;
-import java.util.Arrays;
-
-public class DexAnnotationSetRefList extends DexItem {
-
-  private static final DexAnnotationSetRefList theEmptyTypeList = new DexAnnotationSetRefList();
-
-  public final DexAnnotationSet[] values;
-  private final int missingParameterAnnotations;
-
-  public static DexAnnotationSetRefList empty() {
-    return theEmptyTypeList;
-  }
-
-  private DexAnnotationSetRefList() {
-    this.values = new DexAnnotationSet[0];
-    this.missingParameterAnnotations = 0;
-  }
-
-  public DexAnnotationSetRefList(DexAnnotationSet[] values) {
-    this(values, 0);
-  }
-
-  public DexAnnotationSetRefList(DexAnnotationSet[] values, int missingParameterAnnotations) {
-    assert values != null && values.length > 0;
-    this.values = values;
-    this.missingParameterAnnotations = missingParameterAnnotations;
-  }
-
-  @Override
-  public int hashCode() {
-    return Arrays.hashCode(values);
-  }
-
-  @Override
-  public boolean equals(Object other) {
-    if (this == other) {
-      return true;
-    }
-    if (other instanceof DexAnnotationSetRefList) {
-      return Arrays.equals(values, ((DexAnnotationSetRefList) other).values);
-    }
-    return false;
-  }
-
-  @Override
-  public void collectIndexedItems(IndexedItemCollection indexedItems,
-      DexMethod method, int instructionOffset) {
-    collectAll(indexedItems, values);
-  }
-
-  @Override
-  void collectMixedSectionItems(MixedSectionCollection mixedItems) {
-    // Collect values first so that the annotation sets have sorted themselves before adding this.
-    collectAll(mixedItems, values);
-    mixedItems.add(this);
-  }
-
-  public boolean isEmpty() {
-    return values.length == 0;
-  }
-
-  public int getMissingParameterAnnotations() {
-    return missingParameterAnnotations;
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index 7b09cab..23139c5 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -159,21 +159,13 @@
       for (DexAnnotation annotation : method.annotations.annotations) {
         consumer.accept(annotation);
       }
-      for (DexAnnotationSet parameterAnnotations : method.parameterAnnotations.values) {
-        for (DexAnnotation annotation : parameterAnnotations.annotations) {
-          consumer.accept(annotation);
-        }
-      }
+      method.parameterAnnotationsList.forEachAnnotation(consumer);
     }
     for (DexEncodedMethod method : virtualMethods()) {
       for (DexAnnotation annotation : method.annotations.annotations) {
         consumer.accept(annotation);
       }
-      for (DexAnnotationSet parameterAnnotations : method.parameterAnnotations.values) {
-        for (DexAnnotation annotation : parameterAnnotations.annotations) {
-          consumer.accept(annotation);
-        }
-      }
+      method.parameterAnnotationsList.forEachAnnotation(consumer);
     }
     for (DexEncodedField field : instanceFields()) {
       for (DexAnnotation annotation : field.annotations.annotations) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index d514a9a..0cabaf1 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -93,7 +93,7 @@
   public final DexMethod method;
   public final MethodAccessFlags accessFlags;
   public DexAnnotationSet annotations;
-  public DexAnnotationSetRefList parameterAnnotations;
+  public ParameterAnnotationsList parameterAnnotationsList;
   private Code code;
   private CompilationState compilationState = CompilationState.NOT_PROCESSED;
   private OptimizationInfo optimizationInfo = DefaultOptimizationInfo.DEFAULT;
@@ -102,12 +102,12 @@
       DexMethod method,
       MethodAccessFlags accessFlags,
       DexAnnotationSet annotations,
-      DexAnnotationSetRefList parameterAnnotations,
+      ParameterAnnotationsList parameterAnnotationsList,
       Code code) {
     this.method = method;
     this.accessFlags = accessFlags;
     this.annotations = annotations;
-    this.parameterAnnotations = parameterAnnotations;
+    this.parameterAnnotationsList = parameterAnnotationsList;
     this.code = code;
     assert code == null || !accessFlags.isAbstract();
   }
@@ -272,7 +272,7 @@
       code.collectIndexedItems(indexedItems, this.method);
     }
     annotations.collectIndexedItems(indexedItems);
-    parameterAnnotations.collectIndexedItems(indexedItems);
+    parameterAnnotationsList.collectIndexedItems(indexedItems);
   }
 
   @Override
@@ -281,7 +281,7 @@
       code.collectMixedSectionItems(mixedItems);
     }
     annotations.collectMixedSectionItems(mixedItems);
-    parameterAnnotations.collectMixedSectionItems(mixedItems);
+    parameterAnnotationsList.collectMixedSectionItems(mixedItems);
   }
 
   public Code getCode() {
@@ -537,7 +537,7 @@
   }
 
   public boolean hasAnnotation() {
-    return !annotations.isEmpty() || !parameterAnnotations.isEmpty();
+    return !annotations.isEmpty() || !parameterAnnotationsList.isEmpty();
   }
 
   public void registerCodeReferences(UseRegistry registry) {
@@ -727,7 +727,7 @@
     private DexMethod method;
     private final MethodAccessFlags accessFlags;
     private final DexAnnotationSet annotations;
-    private final DexAnnotationSetRefList parameterAnnotations;
+    private final ParameterAnnotationsList parameterAnnotations;
     private Code code;
     private CompilationState compilationState = CompilationState.NOT_PROCESSED;
     private OptimizationInfo optimizationInfo = DefaultOptimizationInfo.DEFAULT;
@@ -737,7 +737,7 @@
       method = from.method;
       accessFlags = from.accessFlags.copy();
       annotations = from.annotations;
-      parameterAnnotations = from.parameterAnnotations;
+      parameterAnnotations = from.parameterAnnotationsList;
       code = from.code;
       compilationState = from.compilationState;
       optimizationInfo = from.optimizationInfo.copy();
diff --git a/src/main/java/com/android/tools/r8/graph/DexMemberAnnotation.java b/src/main/java/com/android/tools/r8/graph/DexMemberAnnotation.java
index f005588..6cc2fb4 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMemberAnnotation.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMemberAnnotation.java
@@ -60,9 +60,9 @@
   }
 
   public static class DexParameterAnnotation extends
-      DexMemberAnnotation<DexMethod, DexAnnotationSetRefList> {
+      DexMemberAnnotation<DexMethod, ParameterAnnotationsList> {
 
-    public DexParameterAnnotation(DexMethod item, DexAnnotationSetRefList annotations) {
+    public DexParameterAnnotation(DexMethod item, ParameterAnnotationsList annotations) {
       super(item, annotations);
     }
   }
diff --git a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
index e403c14..02f0801 100644
--- a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
+++ b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
@@ -446,7 +446,7 @@
     private List<DexAnnotation> annotations = null;
     private DexValue defaultAnnotation = null;
     private int fakeParameterAnnotations = 0;
-    private List<List<DexAnnotation>> parameterAnnotations = null;
+    private List<List<DexAnnotation>> parameterAnnotationsLists = null;
     private List<DexValue> parameterNames = null;
     private List<DexValue> parameterFlags = null;
     final DexMethod method;
@@ -508,21 +508,21 @@
         // We can iterate through all the parameters twice. Once for visible and once for
         // invisible parameter annotations. We only record the number of fake parameter
         // annotations once.
-        if (parameterAnnotations == null) {
+        if (parameterAnnotationsLists == null) {
           fakeParameterAnnotations++;
         }
         return null;
       }
-      if (parameterAnnotations == null) {
+      if (parameterAnnotationsLists == null) {
         int adjustedParameterCount = parameterCount - fakeParameterAnnotations;
-        parameterAnnotations = new ArrayList<>(adjustedParameterCount);
+        parameterAnnotationsLists = new ArrayList<>(adjustedParameterCount);
         for (int i = 0; i < adjustedParameterCount; i++) {
-          parameterAnnotations.add(new ArrayList<>());
+          parameterAnnotationsLists.add(new ArrayList<>());
         }
       }
       assert mv == null;
       return createAnnotationVisitor(desc, visible,
-          parameterAnnotations.get(parameter - fakeParameterAnnotations), parent.application);
+          parameterAnnotationsLists.get(parameter - fakeParameterAnnotations), parent.application);
     }
 
     @Override
@@ -573,15 +573,15 @@
     public void visitEnd() {
       assert flags.isAbstract() || flags.isNative() || parent.classKind != ClassKind.PROGRAM
           || code != null;
-      DexAnnotationSetRefList parameterAnnotationSets;
-      if (parameterAnnotations == null) {
-        parameterAnnotationSets = DexAnnotationSetRefList.empty();
+      ParameterAnnotationsList annotationsList;
+      if (parameterAnnotationsLists == null) {
+        annotationsList = ParameterAnnotationsList.empty();
       } else {
-        DexAnnotationSet[] sets = new DexAnnotationSet[parameterAnnotations.size()];
-        for (int i = 0; i < parameterAnnotations.size(); i++) {
-          sets[i] = createAnnotationSet(parameterAnnotations.get(i));
+        DexAnnotationSet[] sets = new DexAnnotationSet[parameterAnnotationsLists.size()];
+        for (int i = 0; i < parameterAnnotationsLists.size(); i++) {
+          sets[i] = createAnnotationSet(parameterAnnotationsLists.get(i));
         }
-        parameterAnnotationSets = new DexAnnotationSetRefList(sets, fakeParameterAnnotations);
+        annotationsList = new ParameterAnnotationsList(sets, fakeParameterAnnotations);
       }
       InternalOptions internalOptions = parent.application.options;
       if (parameterNames != null && internalOptions.canUseParameterNameAnnotations()) {
@@ -596,7 +596,7 @@
             parent.application.getFactory()));
       }
       DexEncodedMethod dexMethod = new DexEncodedMethod(method, flags,
-          createAnnotationSet(annotations), parameterAnnotationSets, code);
+          createAnnotationSet(annotations), annotationsList, code);
       if (flags.isStatic() || flags.isConstructor() || flags.isPrivate()) {
         parent.directMethods.add(dexMethod);
       } else {
diff --git a/src/main/java/com/android/tools/r8/graph/ParameterAnnotationsList.java b/src/main/java/com/android/tools/r8/graph/ParameterAnnotationsList.java
new file mode 100644
index 0000000..23d5a38
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/ParameterAnnotationsList.java
@@ -0,0 +1,184 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.dex.MixedSectionCollection;
+import java.util.Arrays;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+
+/**
+ * List of parameter annotations.
+ *
+ * <p>Due to a javac bug that went unfixed for multiple Java versions, the JVM specification does
+ * not require that the number of entries in the ParameterAnnotations attribute of a method matches
+ * the number of parameters in the method prototype; the number of ParameterAnnotations entries may
+ * be less than the number of prototype parameters for methods on inner classes.
+ *
+ * <p>There are two ways of accessing the parameter annotations:
+ *
+ * <ul>
+ *   <li>Using {@link ParameterAnnotationsList#forEachAnnotation(Consumer)}
+ *   <li>Using {@link ParameterAnnotationsList#size()}, {@link
+ *       ParameterAnnotationsList#isMissing(int)} and {@link ParameterAnnotationsList#get(int)}
+ * </ul>
+ *
+ * <p>The {@link ParameterAnnotationsList#forEachAnnotation(Consumer)} method visits all the {@link
+ * DexAnnotation}s specified in the ParameterAnnotations attribute. In contrast, the {@link
+ * ParameterAnnotationsList#size()} and {@link ParameterAnnotationsList#get(int)} methods may be
+ * used to access the annotations on individual parameters; these methods automatically shift
+ * parameter annotations up to mitigate the javac bug. The {@link
+ * ParameterAnnotationsList#isMissing(int)} accessor is used to determine whether a given parameter
+ * is missing in the ParameterAnnotations attribute.
+ */
+public class ParameterAnnotationsList extends DexItem {
+
+  private static final ParameterAnnotationsList EMPTY_PARAMETER_ANNOTATIONS_LIST =
+      new ParameterAnnotationsList();
+
+  private final DexAnnotationSet[] values;
+  private final int missingParameterAnnotations;
+
+  public static ParameterAnnotationsList empty() {
+    return EMPTY_PARAMETER_ANNOTATIONS_LIST;
+  }
+
+  private ParameterAnnotationsList() {
+    this.values = new DexAnnotationSet[0];
+    this.missingParameterAnnotations = 0;
+  }
+
+  public ParameterAnnotationsList(DexAnnotationSet[] values) {
+    this(values, 0);
+  }
+
+  public ParameterAnnotationsList(DexAnnotationSet[] values, int missingParameterAnnotations) {
+    assert values != null && values.length > 0;
+    this.values = values;
+    this.missingParameterAnnotations = missingParameterAnnotations;
+  }
+
+  @Override
+  public int hashCode() {
+    return Arrays.hashCode(values);
+  }
+
+  @Override
+  public boolean equals(Object other) {
+    if (this == other) {
+      return true;
+    }
+    if (other instanceof ParameterAnnotationsList) {
+      return Arrays.equals(values, ((ParameterAnnotationsList) other).values);
+    }
+    return false;
+  }
+
+  @Override
+  public void collectIndexedItems(
+      IndexedItemCollection indexedItems, DexMethod method, int instructionOffset) {
+    collectAll(indexedItems, values);
+  }
+
+  @Override
+  void collectMixedSectionItems(MixedSectionCollection mixedItems) {
+    // Collect values first so that the annotation sets have sorted themselves before adding this.
+    collectAll(mixedItems, values);
+    mixedItems.add(this);
+  }
+
+  public boolean isEmpty() {
+    return values.length == 0;
+  }
+
+  /** Iterate over the {@link DexAnnotation}s of all parameters. */
+  public void forEachAnnotation(Consumer<DexAnnotation> consumer) {
+    for (DexAnnotationSet parameterAnnotations : values) {
+      for (DexAnnotation annotation : parameterAnnotations.annotations) {
+        consumer.accept(annotation);
+      }
+    }
+  }
+
+  /**
+   * Return the number of parameters in the method prototype, or zero if the method's parameters
+   * have no annotations.
+   */
+  public int size() {
+    return missingParameterAnnotations + values.length;
+  }
+
+  /**
+   * Return the number of parameters specified in the ParameterAnnotations attribute, that is, the
+   * number of parameters for which {@link ParameterAnnotationsList#isMissing(int)} returns false.
+   */
+  public int countNonMissing() {
+    return values.length;
+  }
+
+  /**
+   * Return true if the ParameterAnnotations attribute is missing an entry for this parameter. This
+   * is sometimes the case for the first parameter in a method on an inner class.
+   *
+   * @param i Index of the parameter in the method prototype.
+   */
+  public boolean isMissing(int i) {
+    assert i >= 0;
+    return i < missingParameterAnnotations;
+  }
+
+  /**
+   * Return the annotations on the {@code i}th parameter (indexed according to the method
+   * prototype). If the parameter's annotation list is missing, or {@code i} is not less than the
+   * number of parameters (see {@link ParameterAnnotationsList#isMissing(int)}), {@link
+   * DexAnnotationSet#empty()} is returned.
+   *
+   * @param i Index of the parameter in the method prototype.
+   */
+  public DexAnnotationSet get(int i) {
+    assert i >= 0;
+    int adjustedIndex = i - missingParameterAnnotations;
+    return (0 <= adjustedIndex && adjustedIndex < values.length)
+        ? values[adjustedIndex]
+        : DexAnnotationSet.empty();
+  }
+
+  /** Return a ParameterAnnotationsList extended to the given number of parameters. */
+  public ParameterAnnotationsList withParameterCount(int parameterCount) {
+    assert parameterCount >= size();
+    if (this == EMPTY_PARAMETER_ANNOTATIONS_LIST || parameterCount == size()) {
+      return this;
+    }
+    return new ParameterAnnotationsList(values, parameterCount - values.length);
+  }
+
+  /**
+   * Return a new ParameterAnnotationsList that keeps only the annotations matched by {@code
+   * filter}.
+   */
+  public ParameterAnnotationsList keepIf(Predicate<DexAnnotation> filter) {
+    DexAnnotationSet[] filtered = null;
+    boolean allEmpty = true;
+    for (int i = 0; i < values.length; i++) {
+      DexAnnotationSet updated = values[i].keepIf(filter);
+      if (updated != values[i]) {
+        if (filtered == null) {
+          filtered = values.clone();
+        }
+        filtered[i] = updated;
+      }
+      if (!updated.isEmpty()) {
+        allEmpty = false;
+      }
+    }
+    if (filtered == null) {
+      return this;
+    }
+    if (allEmpty) {
+      return ParameterAnnotationsList.empty();
+    }
+    return new ParameterAnnotationsList(filtered, missingParameterAnnotations);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
index 7befc69..c75456d 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
@@ -111,7 +111,7 @@
     // Some debuggers (like IntelliJ) automatically skip synthetic methods on single step.
     newFlags.setSynthetic();
     return new DexEncodedMethod(newMethod, newFlags,
-        defaultMethod.annotations, defaultMethod.parameterAnnotations,
+        defaultMethod.annotations, defaultMethod.parameterAnnotationsList,
         new SynthesizedCode(new ForwardMethodSourceCode(
             clazz.type, method.proto, /* static method */ null,
             rewriter.defaultAsMethodOfCompanionClass(method),
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
index 898a2b2..1e8d57a 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
@@ -70,7 +70,7 @@
             || (companionMethod.getArity() == dexCode.getDebugInfo().parameters.length);
 
         companionMethods.add(new DexEncodedMethod(companionMethod,
-            newFlags, virtual.annotations, virtual.parameterAnnotations, code));
+            newFlags, virtual.annotations, virtual.parameterAnnotationsList, code));
 
         // Make the method abstract.
         virtual.accessFlags.setAbstract();
@@ -106,7 +106,7 @@
             + "either be public or private in " + iface.origin;
         companionMethods.add(new DexEncodedMethod(
             rewriter.staticAsMethodOfCompanionClass(direct.method), newFlags,
-            direct.annotations, direct.parameterAnnotations, direct.getCode()));
+            direct.annotations, direct.parameterAnnotationsList, direct.getCode()));
 
       } else {
         if (originalFlags.isPrivate()) {
@@ -129,7 +129,7 @@
               || (companionMethod.getArity() == dexCode.getDebugInfo().parameters.length);
 
           companionMethods.add(new DexEncodedMethod(companionMethod,
-              newFlags, direct.annotations, direct.parameterAnnotations, code));
+              newFlags, direct.annotations, direct.parameterAnnotationsList, code));
 
         } else {
           // Since there are no interface constructors at this point,
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
index 82958da..d9bd4bd 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -10,7 +10,6 @@
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.ClassAccessFlags;
 import com.android.tools.r8.graph.DexAnnotationSet;
-import com.android.tools.r8.graph.DexAnnotationSetRefList;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexCode;
 import com.android.tools.r8.graph.DexEncodedField;
@@ -27,6 +26,7 @@
 import com.android.tools.r8.graph.DexValue.DexValueNull;
 import com.android.tools.r8.graph.FieldAccessFlags;
 import com.android.tools.r8.graph.MethodAccessFlags;
+import com.android.tools.r8.graph.ParameterAnnotationsList;
 import com.android.tools.r8.ir.code.Invoke;
 import com.android.tools.r8.ir.synthetic.SynthesizedCode;
 import com.android.tools.r8.origin.SynthesizedOrigin;
@@ -171,7 +171,7 @@
             MethodAccessFlags.fromSharedAccessFlags(
                 Constants.ACC_PUBLIC | Constants.ACC_FINAL, false),
             DexAnnotationSet.empty(),
-            DexAnnotationSetRefList.empty(),
+            ParameterAnnotationsList.empty(),
             new SynthesizedCode(new LambdaMainMethodSourceCode(this, mainMethod)));
 
     // Synthesize bridge methods.
@@ -187,7 +187,7 @@
                       | Constants.ACC_BRIDGE,
                   false),
               DexAnnotationSet.empty(),
-              DexAnnotationSetRefList.empty(),
+              ParameterAnnotationsList.empty(),
               new SynthesizedCode(
                   new LambdaBridgeMethodSourceCode(this, mainMethod, bridgeMethod)));
     }
@@ -208,7 +208,7 @@
                     | Constants.ACC_SYNTHETIC,
                 true),
             DexAnnotationSet.empty(),
-            DexAnnotationSetRefList.empty(),
+            ParameterAnnotationsList.empty(),
             new SynthesizedCode(new LambdaConstructorSourceCode(this)));
 
     // Class constructor for stateless lambda classes.
@@ -219,7 +219,7 @@
               MethodAccessFlags.fromSharedAccessFlags(
                   Constants.ACC_SYNTHETIC | Constants.ACC_STATIC, true),
               DexAnnotationSet.empty(),
-              DexAnnotationSetRefList.empty(),
+              ParameterAnnotationsList.empty(),
               new SynthesizedCode(new LambdaClassConstructorSourceCode(this)));
     }
     return methods;
@@ -492,7 +492,7 @@
           // relax its accessibility without making it virtual.
           DexEncodedMethod newMethod = new DexEncodedMethod(
               callTarget, encodedMethod.accessFlags, encodedMethod.annotations,
-              encodedMethod.parameterAnnotations, encodedMethod.getCode());
+              encodedMethod.parameterAnnotationsList, encodedMethod.getCode());
           // TODO(ager): Should we give the new first parameter an actual name? Maybe 'this'?
           encodedMethod.accessFlags.setStatic();
           encodedMethod.accessFlags.unsetPrivate();
@@ -532,7 +532,7 @@
               Constants.ACC_SYNTHETIC | Constants.ACC_STATIC | Constants.ACC_PUBLIC,
               false);
       DexEncodedMethod accessorEncodedMethod = new DexEncodedMethod(
-          callTarget, accessorFlags, DexAnnotationSet.empty(), DexAnnotationSetRefList.empty(),
+          callTarget, accessorFlags, DexAnnotationSet.empty(), ParameterAnnotationsList.empty(),
           new SynthesizedCode(new AccessorMethodSourceCode(LambdaClass.this)));
       accessorClass.setDirectMethods(appendMethod(
           accessorClass.directMethods(), accessorEncodedMethod));
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
index 5a257df..7af555c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
@@ -11,7 +11,6 @@
 import com.android.tools.r8.graph.Code;
 import com.android.tools.r8.graph.DebugLocalInfo;
 import com.android.tools.r8.graph.DexAnnotationSet;
-import com.android.tools.r8.graph.DexAnnotationSetRefList;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexItemFactory;
@@ -22,6 +21,7 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DexTypeList;
 import com.android.tools.r8.graph.MethodAccessFlags;
+import com.android.tools.r8.graph.ParameterAnnotationsList;
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.code.Add;
 import com.android.tools.r8.ir.code.BasicBlock;
@@ -1057,7 +1057,7 @@
       DexString methodName = dexItemFactory.createString(OutlineOptions.METHOD_PREFIX + count);
       DexMethod method = outline.buildMethod(type, methodName);
       direct[count] = new DexEncodedMethod(method, methodAccess, DexAnnotationSet.empty(),
-          DexAnnotationSetRefList.empty(), new OutlineCode(outline));
+          ParameterAnnotationsList.empty(), new OutlineCode(outline));
       generatedOutlines.put(outline, method);
       count++;
     }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
index 03daf44..466117f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
@@ -327,12 +327,12 @@
 
     for (DexEncodedMethod method : clazz.directMethods()) {
       lambdaInvalidator.accept(method.annotations);
-      lambdaInvalidator.accept(method.parameterAnnotations);
+      lambdaInvalidator.accept(method.parameterAnnotationsList);
       lambdaInvalidator.accept(method.method, clazz.type);
     }
     for (DexEncodedMethod method : clazz.virtualMethods()) {
       lambdaInvalidator.accept(method.annotations);
-      lambdaInvalidator.accept(method.parameterAnnotations);
+      lambdaInvalidator.accept(method.parameterAnnotationsList);
       lambdaInvalidator.accept(method.method, clazz.type);
     }
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaTypeVisitor.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaTypeVisitor.java
index 4f56306..05c71e6 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaTypeVisitor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaTypeVisitor.java
@@ -7,7 +7,6 @@
 import com.android.tools.r8.graph.DexAnnotation;
 import com.android.tools.r8.graph.DexAnnotationElement;
 import com.android.tools.r8.graph.DexAnnotationSet;
-import com.android.tools.r8.graph.DexAnnotationSetRefList;
 import com.android.tools.r8.graph.DexCallSite;
 import com.android.tools.r8.graph.DexEncodedAnnotation;
 import com.android.tools.r8.graph.DexField;
@@ -24,6 +23,7 @@
 import com.android.tools.r8.graph.DexValue.DexValueMethodHandle;
 import com.android.tools.r8.graph.DexValue.DexValueMethodType;
 import com.android.tools.r8.graph.DexValue.DexValueType;
+import com.android.tools.r8.graph.ParameterAnnotationsList;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
 
@@ -116,10 +116,8 @@
     }
   }
 
-  void accept(DexAnnotationSetRefList annotationSetRefList) {
-    for (DexAnnotationSet annotationSet : annotationSetRefList.values) {
-      accept(annotationSet);
-    }
+  void accept(ParameterAnnotationsList parameterAnnotationsList) {
+    parameterAnnotationsList.forEachAnnotation(this::accept);
   }
 
   private void accept(DexAnnotation annotation) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupClassBuilder.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupClassBuilder.java
index d441a00..1fe0317 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupClassBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupClassBuilder.java
@@ -7,7 +7,6 @@
 import com.android.tools.r8.graph.ClassAccessFlags;
 import com.android.tools.r8.graph.DexAnnotation;
 import com.android.tools.r8.graph.DexAnnotationSet;
-import com.android.tools.r8.graph.DexAnnotationSetRefList;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexItemFactory;
@@ -19,6 +18,7 @@
 import com.android.tools.r8.graph.EnclosingMethodAttribute;
 import com.android.tools.r8.graph.InnerClassAttribute;
 import com.android.tools.r8.graph.MethodAccessFlags;
+import com.android.tools.r8.graph.ParameterAnnotationsList;
 import com.android.tools.r8.ir.optimize.lambda.LambdaGroupClassBuilder;
 import com.android.tools.r8.ir.synthetic.SynthesizedCode;
 import com.android.tools.r8.ir.synthetic.SyntheticSourceCode;
@@ -113,7 +113,7 @@
             factory.createMethod(group.getGroupClassType(), methodProto, methodName),
             accessFlags,
             isMainMethod ? id.mainMethodAnnotations : DexAnnotationSet.empty(),
-            isMainMethod ? id.mainMethodParamAnnotations : DexAnnotationSetRefList.empty(),
+            isMainMethod ? id.mainMethodParamAnnotations : ParameterAnnotationsList.empty(),
             new SynthesizedCode(
                 new KotlinLambdaVirtualMethodSourceCode(factory, group.getGroupClassType(),
                     methodProto, group.getLambdaIdField(factory), implMethods))));
@@ -159,7 +159,7 @@
         factory.createMethod(groupClassType, initializerProto, factory.constructorMethodName),
         CONSTRUCTOR_FLAGS_RELAXED,  // always create access-relaxed constructor.
         DexAnnotationSet.empty(),
-        DexAnnotationSetRefList.empty(),
+        ParameterAnnotationsList.empty(),
         new SynthesizedCode(createInstanceInitializerSourceCode(groupClassType, initializerProto)));
 
     // Static class initializer for stateless lambdas.
@@ -170,7 +170,7 @@
               factory.classConstructorMethodName),
           CLASS_INITIALIZER_FLAGS,
           DexAnnotationSet.empty(),
-          DexAnnotationSetRefList.empty(),
+          ParameterAnnotationsList.empty(),
           new SynthesizedCode(new ClassInitializerSourceCode(factory, group)));
     }
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupId.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupId.java
index 0540bef..996099b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupId.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupId.java
@@ -5,13 +5,13 @@
 package com.android.tools.r8.ir.optimize.lambda.kotlin;
 
 import com.android.tools.r8.graph.DexAnnotationSet;
-import com.android.tools.r8.graph.DexAnnotationSetRefList;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.EnclosingMethodAttribute;
 import com.android.tools.r8.graph.InnerClassAttribute;
+import com.android.tools.r8.graph.ParameterAnnotationsList;
 import com.android.tools.r8.ir.optimize.lambda.LambdaGroup;
 import com.android.tools.r8.ir.optimize.lambda.LambdaGroupId;
 
@@ -41,7 +41,7 @@
   final DexString mainMethodName;
   final DexProto mainMethodProto;
   final DexAnnotationSet mainMethodAnnotations;
-  final DexAnnotationSetRefList mainMethodParamAnnotations;
+  final ParameterAnnotationsList mainMethodParamAnnotations;
 
   final EnclosingMethodAttribute enclosing;
 
@@ -61,7 +61,7 @@
     this.mainMethodName = mainMethod.method.name;
     this.mainMethodProto = mainMethod.method.proto;
     this.mainMethodAnnotations = mainMethod.annotations;
-    this.mainMethodParamAnnotations = mainMethod.parameterAnnotations;
+    this.mainMethodParamAnnotations = mainMethod.parameterAnnotationsList;
     this.innerClassAccess = inner != null ? inner.getAccess() : MISSING_INNER_CLASS_ATTRIBUTE;
     this.enclosing = enclosing;
     this.hash = computeHashCode();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupIdFactory.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupIdFactory.java
index f5698aa..42e0bb1 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupIdFactory.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupIdFactory.java
@@ -191,7 +191,7 @@
       throw new LambdaStructureError("unexpected method annotations [" +
           method.annotations.toSmaliString() + "] on " + method.method.toSourceString());
     }
-    if (!method.parameterAnnotations.isEmpty()) {
+    if (!method.parameterAnnotationsList.isEmpty()) {
       throw new LambdaStructureError("unexpected method parameters annotations [" +
           method.annotations.toSmaliString() + "] on " + method.method.toSourceString());
     }
diff --git a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
index e49ad85..2970fb4 100644
--- a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
@@ -13,7 +13,6 @@
 import com.android.tools.r8.graph.DexAnnotation;
 import com.android.tools.r8.graph.DexAnnotationElement;
 import com.android.tools.r8.graph.DexAnnotationSet;
-import com.android.tools.r8.graph.DexAnnotationSetRefList;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexEncodedAnnotation;
 import com.android.tools.r8.graph.DexEncodedField;
@@ -34,6 +33,7 @@
 import com.android.tools.r8.graph.DexValue.UnknownDexValue;
 import com.android.tools.r8.graph.InnerClassAttribute;
 import com.android.tools.r8.graph.JarClassFileReader;
+import com.android.tools.r8.graph.ParameterAnnotationsList;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.naming.ProguardMapSupplier;
 import com.android.tools.r8.utils.ExceptionUtils;
@@ -252,7 +252,7 @@
       }
     }
     writeAnnotations(visitor::visitAnnotation, method.annotations.annotations);
-    writeParameterAnnotations(visitor, method.parameterAnnotations);
+    writeParameterAnnotations(visitor, method.parameterAnnotationsList);
     if (!method.accessFlags.isAbstract() && !method.accessFlags.isNative()) {
       writeCode(method.getCode(), visitor);
     }
@@ -260,21 +260,21 @@
   }
 
   private void writeParameterAnnotations(
-      MethodVisitor visitor, DexAnnotationSetRefList parameterAnnotations) {
-    int missingParameterAnnotations = parameterAnnotations.getMissingParameterAnnotations();
-    for (int i = 0; i < missingParameterAnnotations; i++) {
-      AnnotationVisitor av =
-          visitor.visitParameterAnnotation(i, JarClassFileReader.SYNTHETIC_ANNOTATION, false);
-      if (av != null) {
-        av.visitEnd();
+      MethodVisitor visitor, ParameterAnnotationsList parameterAnnotations) {
+    for (int i = 0; i < parameterAnnotations.size(); i++) {
+      if (parameterAnnotations.isMissing(i)) {
+        AnnotationVisitor av =
+            visitor.visitParameterAnnotation(i, JarClassFileReader.SYNTHETIC_ANNOTATION, false);
+        if (av != null) {
+          av.visitEnd();
+        }
+      } else {
+        int iFinal = i;
+        writeAnnotations(
+            (d, vis) -> visitor.visitParameterAnnotation(iFinal, d, vis),
+            parameterAnnotations.get(i).annotations);
       }
     }
-    for (int i = 0; i < parameterAnnotations.values.length; i++) {
-      int parameterIndex = i + missingParameterAnnotations;
-      writeAnnotations(
-          (d, vis) -> visitor.visitParameterAnnotation(parameterIndex, d, vis),
-          parameterAnnotations.values[i].annotations);
-    }
   }
 
   private interface AnnotationConsumer {
diff --git a/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
index 871812a..28ca9fc 100644
--- a/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
+++ b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
@@ -5,8 +5,6 @@
 
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.DexAnnotation;
-import com.android.tools.r8.graph.DexAnnotationSet;
-import com.android.tools.r8.graph.DexAnnotationSetRefList;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
@@ -15,9 +13,6 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
 import com.android.tools.r8.utils.InternalOptions;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.function.Predicate;
 
 public class AnnotationRemover {
 
@@ -127,20 +122,20 @@
     keep.ensureValid(options.forceProguardCompatibility, compatibility);
     for (DexProgramClass clazz : appInfo.classes()) {
       stripAttributes(clazz);
-      clazz.annotations = stripAnnotations(clazz.annotations, this::filterAnnotations);
+      clazz.annotations = clazz.annotations.keepIf(this::filterAnnotations);
       clazz.forEachMethod(this::processMethod);
       clazz.forEachField(this::processField);
     }
   }
 
   private void processMethod(DexEncodedMethod method) {
-    method.annotations = stripAnnotations(method.annotations, this::filterAnnotations);
-    method.parameterAnnotations = stripAnnotations(method.parameterAnnotations,
-        this::filterParameterAnnotations);
+    method.annotations = method.annotations.keepIf(this::filterAnnotations);
+    method.parameterAnnotationsList =
+        method.parameterAnnotationsList.keepIf(this::filterParameterAnnotations);
   }
 
   private void processField(DexEncodedField field) {
-      field.annotations = stripAnnotations(field.annotations, this::filterAnnotations);
+    field.annotations = field.annotations.keepIf(this::filterAnnotations);
   }
 
   private void stripAttributes(DexProgramClass clazz) {
@@ -152,52 +147,4 @@
     }
   }
 
-  private DexAnnotationSetRefList stripAnnotations(DexAnnotationSetRefList annotations,
-      Predicate<DexAnnotation> filter) {
-    DexAnnotationSet[] filtered = null;
-    for (int i = 0; i < annotations.values.length; i++) {
-      DexAnnotationSet updated = stripAnnotations(annotations.values[i], filter);
-      if (updated != annotations.values[i]) {
-        if (filtered == null) {
-          filtered = annotations.values.clone();
-          filtered[i] = updated;
-        }
-      }
-    }
-    if (filtered == null) {
-      return annotations;
-    } else {
-      if (Arrays.stream(filtered).allMatch(DexAnnotationSet::isEmpty)) {
-        return DexAnnotationSetRefList.empty();
-      }
-      return new DexAnnotationSetRefList(filtered);
-    }
-  }
-
-  private DexAnnotationSet stripAnnotations(DexAnnotationSet annotations,
-      Predicate<DexAnnotation> filter) {
-    ArrayList<DexAnnotation> filtered = null;
-    for (int i = 0; i < annotations.annotations.length; i++) {
-      DexAnnotation annotation = annotations.annotations[i];
-      if (filter.test(annotation)) {
-        if (filtered != null) {
-          filtered.add(annotation);
-        }
-      } else {
-        if (filtered == null) {
-          filtered = new ArrayList<>(annotations.annotations.length);
-          for (int j = 0; j < i; j++) {
-            filtered.add(annotations.annotations[j]);
-          }
-        }
-      }
-    }
-    if (filtered == null) {
-      return annotations;
-    } else if (filtered.isEmpty()) {
-      return DexAnnotationSet.empty();
-    } else {
-      return new DexAnnotationSet(filtered.toArray(new DexAnnotation[filtered.size()]));
-    }
-  }
 }
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index 25df235..c359fc7 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -15,7 +15,6 @@
 import com.android.tools.r8.graph.AppInfoWithSubtyping;
 import com.android.tools.r8.graph.Descriptor;
 import com.android.tools.r8.graph.DexAnnotation;
-import com.android.tools.r8.graph.DexAnnotationSet;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexCallSite;
 import com.android.tools.r8.graph.DexClass;
@@ -552,14 +551,18 @@
 
   private void processAnnotations(DexAnnotation[] annotations) {
     for (DexAnnotation annotation : annotations) {
-      DexType type = annotation.annotation.type;
-      if (liveTypes.contains(type)) {
-        // The type of this annotation is already live, so pick up its dependencies.
-        handleAnnotationOfLiveType(annotation);
-      } else {
-        // Remember this annotation for later.
-        deferredAnnotations.computeIfAbsent(type, ignore -> new HashSet<>()).add(annotation);
-      }
+      processAnnotation(annotation);
+    }
+  }
+
+  private void processAnnotation(DexAnnotation annotation) {
+    DexType type = annotation.annotation.type;
+    if (liveTypes.contains(type)) {
+      // The type of this annotation is already live, so pick up its dependencies.
+      handleAnnotationOfLiveType(annotation);
+    } else {
+      // Remember this annotation for later.
+      deferredAnnotations.computeIfAbsent(type, ignore -> new HashSet<>()).add(annotation);
     }
   }
 
@@ -1239,9 +1242,7 @@
         }
       }
       processAnnotations(method.annotations.annotations);
-      for (DexAnnotationSet parameterAnnotation : method.parameterAnnotations.values) {
-        processAnnotations(parameterAnnotation.annotations);
-      }
+      method.parameterAnnotationsList.forEachAnnotation(this::processAnnotation);
       if (protoLiteExtension != null && protoLiteExtension.appliesTo(method)) {
         protoLiteExtension.processMethod(method, new UseRegistry(method), protoLiteFields);
       } else {
diff --git a/src/test/java/com/android/tools/r8/debug/ContinuousSteppingTest.java b/src/test/java/com/android/tools/r8/debug/ContinuousSteppingTest.java
index 45e3c96..eca0441 100644
--- a/src/test/java/com/android/tools/r8/debug/ContinuousSteppingTest.java
+++ b/src/test/java/com/android/tools/r8/debug/ContinuousSteppingTest.java
@@ -51,8 +51,9 @@
       .add(KotlinD8Config.DEBUGGEE_KOTLIN_JAR, ContinuousSteppingTest::allVersions)
       .addAll(findAllJarsIn(Paths.get(ToolHelper.EXAMPLES_ANDROID_N_BUILD_DIR)),
           ContinuousSteppingTest::fromAndroidN)
-      .addAll(findAllJarsIn(Paths.get(ToolHelper.EXAMPLES_ANDROID_O_BUILD_DIR)),
-          ContinuousSteppingTest::fromAndroidO)
+      // TODO(b/79911828) Investigate timeout issues for Android O examples.
+      //  .addAll(findAllJarsIn(Paths.get(ToolHelper.EXAMPLES_ANDROID_O_BUILD_DIR)),
+      //      ContinuousSteppingTest::fromAndroidO)
       .build();
 
   private static final Map<Path, DebugTestConfig> compiledJarConfig = new HashMap<>();
diff --git a/src/test/java/com/android/tools/r8/debug/UnusedCheckCastTargetOptimizationTest.java b/src/test/java/com/android/tools/r8/debug/UnusedCheckCastTargetOptimizationTest.java
new file mode 100644
index 0000000..8fd6cd8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debug/UnusedCheckCastTargetOptimizationTest.java
@@ -0,0 +1,16 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.debug;
+
+public class UnusedCheckCastTargetOptimizationTest {
+
+  class Super {}
+
+  class Subclass extends Super {}
+
+  public static void main(String[] args) {
+    Super[] b = new Subclass[10];
+    Subclass[] c = (Subclass[]) b;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/debug/UnusedCheckCastTargetOptimizationTestRunner.java b/src/test/java/com/android/tools/r8/debug/UnusedCheckCastTargetOptimizationTestRunner.java
new file mode 100644
index 0000000..2b93218
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debug/UnusedCheckCastTargetOptimizationTestRunner.java
@@ -0,0 +1,32 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.debug;
+
+import org.junit.Test;
+
+public class UnusedCheckCastTargetOptimizationTestRunner extends DebugTestBase {
+
+  private static final Class MAIN_CLASS = UnusedCheckCastTargetOptimizationTest.class;
+  private static final Class SUPER_CLASS = UnusedCheckCastTargetOptimizationTest.Super.class;
+  private static final Class SUBCLASS_CLASS = UnusedCheckCastTargetOptimizationTest.Subclass.class;
+  private static final String FILE = MAIN_CLASS.getSimpleName() + ".java";
+  private static final String NAME = MAIN_CLASS.getCanonicalName();
+
+  @Test
+  public void test() throws Throwable {
+    runDebugTest(
+        new D8DebugTestConfig().compileAndAddClasses(temp, MAIN_CLASS, SUPER_CLASS, SUBCLASS_CLASS),
+        NAME,
+        breakpoint(NAME, "main", 14),
+        run(),
+        checkLine(FILE, 14),
+        checkLocal("b"),
+        checkNoLocal("c"),
+        stepOver(),
+        checkLine(FILE, 15),
+        checkLocal("b"),
+        checkLocal("c"),
+        run());
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java b/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java
index 9edc735..89935a7 100644
--- a/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java
+++ b/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java
@@ -11,7 +11,6 @@
 import com.android.tools.r8.errors.DexOverflowException;
 import com.android.tools.r8.graph.ClassAccessFlags;
 import com.android.tools.r8.graph.DexAnnotationSet;
-import com.android.tools.r8.graph.DexAnnotationSetRefList;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexCode;
 import com.android.tools.r8.graph.DexCode.Try;
@@ -25,6 +24,7 @@
 import com.android.tools.r8.graph.DexTypeList;
 import com.android.tools.r8.graph.DirectMappedDexApplication;
 import com.android.tools.r8.graph.MethodAccessFlags;
+import com.android.tools.r8.graph.ParameterAnnotationsList;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.origin.SynthesizedOrigin;
 import com.android.tools.r8.utils.DefaultDiagnosticsHandler;
@@ -80,7 +80,7 @@
             holder, dexItemFactory.createProto(dexItemFactory.voidType), "theMethod"),
         MethodAccessFlags.fromSharedAccessFlags(Constants.ACC_PUBLIC, false),
         DexAnnotationSet.empty(),
-        DexAnnotationSetRefList.empty(),
+        ParameterAnnotationsList.empty(),
         code);
   }
 
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
index 2ff2f71..9de9f5a 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
@@ -29,7 +29,6 @@
 import com.android.tools.r8.graph.Code;
 import com.android.tools.r8.graph.DebugLocalInfo;
 import com.android.tools.r8.graph.DexAnnotationSet;
-import com.android.tools.r8.graph.DexAnnotationSetRefList;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
@@ -40,6 +39,7 @@
 import com.android.tools.r8.graph.DexTypeList;
 import com.android.tools.r8.graph.DirectMappedDexApplication;
 import com.android.tools.r8.graph.MethodAccessFlags;
+import com.android.tools.r8.graph.ParameterAnnotationsList;
 import com.android.tools.r8.ir.code.CatchHandlers;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.Position;
@@ -617,7 +617,7 @@
                     DexString.EMPTY_ARRAY),
                 access,
                 DexAnnotationSet.empty(),
-                DexAnnotationSetRefList.empty(),
+                ParameterAnnotationsList.empty(),
                 code);
         IRCode ir = code.buildIR(method, options, Origin.unknown());
         RegisterAllocator allocator = new LinearScanRegisterAllocator(ir, options);
diff --git a/tools/archive.py b/tools/archive.py
index 76df65e..07f138a 100755
--- a/tools/archive.py
+++ b/tools/archive.py
@@ -3,22 +3,25 @@
 # for details. All rights reserved. Use of this source code is governed by a
 # BSD-style license that can be found in the LICENSE file.
 
-import gradle
 import create_maven_release
-import d8
+import gradle
 import os
-import r8
+import shutil
 import subprocess
 import sys
+import toolhelper
 import utils
-import shutil
 import zipfile
 
 ARCHIVE_BUCKET = 'r8-releases'
 
+def GetToolVersion(jar_path):
+  output = subprocess.check_output(['java', '-jar', jar_path, '--version'])
+  return output.splitlines()[0].strip()
+
 def GetVersion():
-  r8_version = r8.run(['--version'], build = False).splitlines()[0].strip()
-  d8_version = d8.run(['--version'], build = False).splitlines()[0].strip()
+  r8_version = GetToolVersion(utils.R8_JAR)
+  d8_version = GetToolVersion(utils.D8_JAR)
   # The version printed is "D8 vVERSION_NUMBER" and "R8 vVERSION_NUMBER"
   # Sanity check that versions match.
   if d8_version.split()[1] != r8_version.split()[1]:
diff --git a/tools/bisect.py b/tools/bisect.py
index c8d1501..74c880e 100755
--- a/tools/bisect.py
+++ b/tools/bisect.py
@@ -3,33 +3,8 @@
 # for details. All rights reserved. Use of this source code is governed by a
 # BSD-style license that can be found in the LICENSE file.
 
-import gradle
-import os
-import subprocess
 import sys
-import utils
-
-JAR = os.path.join(utils.REPO_ROOT, 'build', 'libs', 'bisect.jar')
-
-def run(args, build, debug):
-  if build:
-    gradle.RunGradle(['bisect'])
-  cmd = ['java']
-  if debug:
-    cmd.append('-ea')
-  cmd.extend(['-jar', JAR])
-  cmd.extend(args)
-  subprocess.check_call(cmd)
-
-def main():
-  build = True
-  args = []
-  for arg in sys.argv[1:]:
-    if arg in ("--build", "--no-build"):
-      build = arg == "--build"
-    else:
-      args.append(arg)
-  run(args, build, True)
+import toolhelper
 
 if __name__ == '__main__':
-  sys.exit(main())
+  sys.exit(toolhelper.run('bisect', sys.argv[1:]))
diff --git a/tools/compatdx.py b/tools/compatdx.py
index 25a59db..c4cb320 100755
--- a/tools/compatdx.py
+++ b/tools/compatdx.py
@@ -3,42 +3,8 @@
 # for details. All rights reserved. Use of this source code is governed by a
 # BSD-style license that can be found in the LICENSE file.
 
-import gradle
-import os
-import subprocess
 import sys
-import utils
-
-def run(args, build = True, debug = True, profile = False, track_memory_file=None):
-  if build:
-    gradle.RunGradle(['CompatDX'])
-  cmd = []
-  if track_memory_file:
-    cmd.extend(['tools/track_memory.sh', track_memory_file])
-  cmd.append('java')
-  if debug:
-    cmd.append('-ea')
-  if profile:
-    cmd.append('-agentlib:hprof=cpu=samples,interval=1,depth=8')
-  cmd.extend(['-jar', utils.COMPATDX_JAR])
-  cmd.extend(args)
-  subprocess.check_call(cmd)
-
-def main():
-  build = True
-  args = []
-  for arg in sys.argv[1:]:
-    if arg in ("--build", "--no-build"):
-      build = arg == "--build"
-    else:
-      args.append(arg)
-  try:
-    run(args, build)
-  except subprocess.CalledProcessError as e:
-    # In case anything relevant was printed to stdout, normally this is already
-    # on stderr.
-    print(e.output)
-    return e.returncode
+import toolhelper
 
 if __name__ == '__main__':
-  sys.exit(main())
+  sys.exit(toolhelper.run('compatdx', sys.argv[1:]))
diff --git a/tools/compatproguard.py b/tools/compatproguard.py
new file mode 100755
index 0000000..10542e3
--- /dev/null
+++ b/tools/compatproguard.py
@@ -0,0 +1,10 @@
+#!/usr/bin/env python
+# Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+import sys
+import toolhelper
+
+if __name__ == '__main__':
+  sys.exit(toolhelper.run('compatproguard', sys.argv[1:]))
diff --git a/tools/d8.py b/tools/d8.py
index bf97845..18a4a67 100755
--- a/tools/d8.py
+++ b/tools/d8.py
@@ -3,46 +3,8 @@
 # for details. All rights reserved. Use of this source code is governed by a
 # BSD-style license that can be found in the LICENSE file.
 
-import gradle
-import os
-import subprocess
 import sys
-import utils
-
-def run(args, build=True, debug=True, profile=False,
-        track_memory_file=None):
-  if build:
-    gradle.RunGradle(['D8'])
-  cmd = []
-  if track_memory_file:
-    cmd.extend(['tools/track_memory.sh', track_memory_file])
-  cmd.append('java')
-  if debug:
-    cmd.append('-ea')
-  if profile:
-    cmd.append('-agentlib:hprof=cpu=samples,interval=1,depth=8')
-  cmd.extend(['-jar', utils.D8_JAR])
-  cmd.extend(args)
-  utils.PrintCmd(cmd)
-  result = subprocess.check_output(cmd)
-  print(result)
-  return result
-
-def main():
-  build = True
-  args = []
-  for arg in sys.argv[1:]:
-    if arg in ("--build", "--no-build"):
-      build = arg == "--build"
-    else:
-      args.append(arg)
-  try:
-    run(args, build)
-  except subprocess.CalledProcessError as e:
-    # In case anything relevant was printed to stdout, normally this is already
-    # on stderr.
-    print(e.output)
-    return e.returncode
+import toolhelper
 
 if __name__ == '__main__':
-  sys.exit(main())
+  sys.exit(toolhelper.run('d8', sys.argv[1:]))
diff --git a/tools/d8logger.py b/tools/d8logger.py
new file mode 100755
index 0000000..9fb3508
--- /dev/null
+++ b/tools/d8logger.py
@@ -0,0 +1,10 @@
+#!/usr/bin/env python
+# Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+import sys
+import toolhelper
+
+if __name__ == '__main__':
+  sys.exit(toolhelper.run('d8logger', sys.argv[1:]))
diff --git a/tools/dexfilemerger.py b/tools/dexfilemerger.py
new file mode 100755
index 0000000..7bdfc22
--- /dev/null
+++ b/tools/dexfilemerger.py
@@ -0,0 +1,10 @@
+#!/usr/bin/env python
+# Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+import sys
+import toolhelper
+
+if __name__ == '__main__':
+  sys.exit(toolhelper.run('dexfilemerger', sys.argv[1:]))
diff --git a/tools/dexsegments.py b/tools/dexsegments.py
new file mode 100755
index 0000000..f984063
--- /dev/null
+++ b/tools/dexsegments.py
@@ -0,0 +1,10 @@
+#!/usr/bin/env python
+# Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+import sys
+import toolhelper
+
+if __name__ == '__main__':
+  sys.exit(toolhelper.run('dexsegments', sys.argv[1:]))
diff --git a/tools/dexsplitter.py b/tools/dexsplitter.py
new file mode 100755
index 0000000..415b149
--- /dev/null
+++ b/tools/dexsplitter.py
@@ -0,0 +1,10 @@
+#!/usr/bin/env python
+# Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+import sys
+import toolhelper
+
+if __name__ == '__main__':
+  sys.exit(toolhelper.run('dexsplitter', sys.argv[1:]))
diff --git a/tools/disasm.py b/tools/disasm.py
new file mode 100755
index 0000000..0d2599a
--- /dev/null
+++ b/tools/disasm.py
@@ -0,0 +1,10 @@
+#!/usr/bin/env python
+# Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+import sys
+import toolhelper
+
+if __name__ == '__main__':
+  sys.exit(toolhelper.run('disasm', sys.argv[1:]))
diff --git a/tools/extractmarker.py b/tools/extractmarker.py
new file mode 100755
index 0000000..36a9c88
--- /dev/null
+++ b/tools/extractmarker.py
@@ -0,0 +1,10 @@
+#!/usr/bin/env python
+# Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+import sys
+import toolhelper
+
+if __name__ == '__main__':
+  sys.exit(toolhelper.run('extractmarker', sys.argv[1:]))
diff --git a/tools/jardiff.py b/tools/jardiff.py
new file mode 100755
index 0000000..894131c
--- /dev/null
+++ b/tools/jardiff.py
@@ -0,0 +1,10 @@
+#!/usr/bin/env python
+# Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+import sys
+import toolhelper
+
+if __name__ == '__main__':
+  sys.exit(toolhelper.run('jardiff', sys.argv[1:]))
diff --git a/tools/maindex.py b/tools/maindex.py
new file mode 100755
index 0000000..ff5329a
--- /dev/null
+++ b/tools/maindex.py
@@ -0,0 +1,10 @@
+#!/usr/bin/env python
+# Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+import sys
+import toolhelper
+
+if __name__ == '__main__':
+  sys.exit(toolhelper.run('maindex', sys.argv[1:]))
diff --git a/tools/r8.py b/tools/r8.py
index 3ae4da7..60c60a0 100755
--- a/tools/r8.py
+++ b/tools/r8.py
@@ -3,46 +3,8 @@
 # for details. All rights reserved. Use of this source code is governed by a
 # BSD-style license that can be found in the LICENSE file.
 
-import gradle
-import os
-import subprocess
 import sys
-import utils
-
-def run(args, build=True, debug=True, profile=False,
-        track_memory_file=None):
-  if build:
-    gradle.RunGradle(['r8'])
-  cmd = []
-  if track_memory_file:
-    cmd.extend(['tools/track_memory.sh', track_memory_file])
-  cmd.append('java')
-  if debug:
-    cmd.append('-ea')
-  if profile:
-    cmd.append('-agentlib:hprof=cpu=samples,interval=1,depth=8')
-  cmd.extend(['-jar', utils.R8_JAR])
-  cmd.extend(args)
-  utils.PrintCmd(cmd)
-  result = subprocess.check_output(cmd)
-  print(result)
-  return result
-
-def main():
-  build = True
-  args = []
-  for arg in sys.argv[1:]:
-    if arg in ("--build", "--no-build"):
-      build = arg == "--build"
-    else:
-      args.append(arg)
-  try:
-    run(args, build)
-  except subprocess.CalledProcessError as e:
-    # In case anything relevant was printed to stdout, normally this is already
-    # on stderr.
-    print(e.output)
-    return e.returncode
+import toolhelper
 
 if __name__ == '__main__':
-  sys.exit(main())
+  sys.exit(toolhelper.run('r8', sys.argv[1:]))
diff --git a/tools/run-d8-on-gmscore.py b/tools/run-d8-on-gmscore.py
index 63865d0..b1bdc6e 100755
--- a/tools/run-d8-on-gmscore.py
+++ b/tools/run-d8-on-gmscore.py
@@ -3,11 +3,11 @@
 # for details. All rights reserved. Use of this source code is governed by a
 # BSD-style license that can be found in the LICENSE file.
 
-import d8
 import gmscore_data
 import optparse
 import os
 import sys
+import toolhelper
 
 def ParseOptions():
   result = optparse.OptionParser()
@@ -70,8 +70,13 @@
     with open(options.dump_args_file, 'w') as args_file:
       args_file.writelines([arg + os.linesep for arg in args])
   else:
-    d8.run(args, not options.no_build, not options.no_debug, options.profile,
-           options.track_memory_to_file)
+    toolhelper.run(
+        'd8',
+        args,
+        build=not options.no_build,
+        debug=not options.no_debug,
+        profile=options.profile,
+        track_memory_to_file=options.track_memory_to_file)
 
 if __name__ == '__main__':
   sys.exit(main())
diff --git a/tools/run_on_app.py b/tools/run_on_app.py
index b9bcc60..571d96d 100755
--- a/tools/run_on_app.py
+++ b/tools/run_on_app.py
@@ -10,10 +10,9 @@
 import sys
 import time
 
-import d8
 import gmail_data
 import gmscore_data
-import r8
+import toolhelper
 import utils
 import youtube_data
 
@@ -191,22 +190,20 @@
       if options.print_memoryuse and not options.track_memory_to_file:
         options.track_memory_to_file = os.path.join(temp,
             utils.MEMORY_USE_TMP_FILE)
-      if options.compiler == 'd8':
-        d8.run(args, not options.no_build, not options.no_debug,
-            options.profile, options.track_memory_to_file)
-      else:
-        if app_provided_pg_conf:
-          # Ensure that output of -printmapping and -printseeds go to the output
-          # location and not where the app Proguard configuration places them.
-          if outdir.endswith('.zip') or outdir.endswith('.jar'):
-            pg_outdir = os.path.dirname(outdir)
-          else:
-            pg_outdir = outdir
-          additional_pg_conf = GenerateAdditionalProguardConfiguration(
-              temp, os.path.abspath(pg_outdir))
-          args.extend(['--pg-conf', additional_pg_conf])
-        r8.run(args, not options.no_build, not options.no_debug,
-            options.profile, options.track_memory_to_file)
+      if options.compiler == 'r8' and app_provided_pg_conf:
+        # Ensure that output of -printmapping and -printseeds go to the output
+        # location and not where the app Proguard configuration places them.
+        if outdir.endswith('.zip') or outdir.endswith('.jar'):
+          pg_outdir = os.path.dirname(outdir)
+        else:
+          pg_outdir = outdir
+        additional_pg_conf = GenerateAdditionalProguardConfiguration(
+            temp, os.path.abspath(pg_outdir))
+        args.extend(['--pg-conf', additional_pg_conf])
+      toolhelper.run(options.compiler, args, build=not options.no_build,
+                     debug=not options.no_debug,
+                     profile=options.profile,
+                     track_memory_to_file=options.track_memory_to_file)
       if options.print_memoryuse:
         print('{}(MemoryUse): {}'
             .format(options.print_memoryuse,
diff --git a/tools/toolhelper.py b/tools/toolhelper.py
new file mode 100644
index 0000000..a7f509c
--- /dev/null
+++ b/tools/toolhelper.py
@@ -0,0 +1,38 @@
+# Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+import gradle
+import os
+import subprocess
+import sys
+import utils
+
+def run(tool, args, build=None, debug=True,
+        profile=False, track_memory_file=None):
+  if build is None:
+    build, args = extract_build_from_args(args)
+  if build:
+    gradle.RunGradle(['r8'])
+  cmd = []
+  if track_memory_file:
+    cmd.extend(['tools/track_memory.sh', track_memory_file])
+  cmd.append('java')
+  if debug:
+    cmd.append('-ea')
+  if profile:
+    cmd.append('-agentlib:hprof=cpu=samples,interval=1,depth=8')
+  cmd.extend(['-jar', utils.R8_JAR, tool])
+  cmd.extend(args)
+  utils.PrintCmd(cmd)
+  return subprocess.call(cmd)
+
+def extract_build_from_args(input_args):
+  build = True
+  args = []
+  for arg in input_args:
+    if arg in ("--build", "--no-build"):
+      build = arg == "--build"
+    else:
+      args.append(arg)
+  return build, args