Simple cleanup of access to methods and fields in DexClass.

Bug:
Change-Id: I74e211e5ee13b6a68404ddfbd331ba92caa63d30
diff --git a/src/main/java/com/android/tools/r8/PrintClassList.java b/src/main/java/com/android/tools/r8/PrintClassList.java
index 6c49769..bc274a8 100644
--- a/src/main/java/com/android/tools/r8/PrintClassList.java
+++ b/src/main/java/com/android/tools/r8/PrintClassList.java
@@ -19,6 +19,7 @@
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.Timing;
+
 import java.io.IOException;
 import java.nio.file.Paths;
 import java.util.Arrays;
@@ -46,39 +47,32 @@
     for (DexProgramClass clazz : application.classes()) {
       System.out.print(maybeDeobfuscateType(map, clazz.type));
       System.out.println();
-      printMethods(clazz.directMethods(), map);
-      printMethods(clazz.virtualMethods(), map);
-      printFields(clazz.staticFields(), map);
-      printFields(clazz.instanceFields(), map);
+      clazz.forEachMethod(method -> printMethod(method, map));
+      clazz.forEachField(field -> printField(field, map));
     }
     executorService.shutdown();
   }
 
-  private static void printMethods(DexEncodedMethod[] methods, ClassNameMapper map) {
-    for (DexEncodedMethod encodedMethod : methods) {
-      DexMethod method = encodedMethod.method;
-
-      if (map != null) {
-        System.out.println(map.originalNameOf(method));
-      } else {
-        // Detour via Signature to get the same formatting.
-        MethodSignature signature = MethodSignature.fromDexMethod(method);
-        System.out.println(method.holder.toSourceString() + " " + signature);
-      }
+  private static void printMethod(DexEncodedMethod encodedMethod, ClassNameMapper map) {
+    DexMethod method = encodedMethod.method;
+    if (map != null) {
+      System.out.println(map.originalNameOf(method));
+    } else {
+      // Detour via Signature to get the same formatting.
+      MethodSignature signature = MethodSignature.fromDexMethod(method);
+      System.out.println(method.holder.toSourceString() + " " + signature);
     }
   }
 
-  private static void printFields(DexEncodedField[] fields, ClassNameMapper map) {
-    for (DexEncodedField encodedField : fields) {
-      DexField field = encodedField.field;
-      if (map != null) {
-        System.out.println(map.originalNameOf(field));
-      } else {
-        // Detour via Signature to get the same formatting.
-        FieldSignature signature = new FieldSignature(field.name.toSourceString(),
-            field.type.toSourceString());
-        System.out.println(field.clazz.toSourceString() + " " + signature);
-      }
+  private static void printField(DexEncodedField encodedField, ClassNameMapper map) {
+    DexField field = encodedField.field;
+    if (map != null) {
+      System.out.println(map.originalNameOf(field));
+    } else {
+      // Detour via Signature to get the same formatting.
+      FieldSignature signature = new FieldSignature(field.name.toSourceString(),
+          field.type.toSourceString());
+      System.out.println(field.clazz.toSourceString() + " " + signature);
     }
   }
 
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 a215ece..b56f712 100644
--- a/src/main/java/com/android/tools/r8/dex/FileWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/FileWriter.java
@@ -5,6 +5,8 @@
 
 import static com.android.tools.r8.utils.LebUtils.sizeAsUleb128;
 
+import com.google.common.collect.Sets;
+
 import com.android.tools.r8.code.Instruction;
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.graph.AppInfo;
@@ -49,11 +51,12 @@
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.LebUtils;
-import com.google.common.collect.Sets;
+
 import it.unimi.dsi.fastutil.objects.Object2IntLinkedOpenHashMap;
 import it.unimi.dsi.fastutil.objects.Object2IntMap;
 import it.unimi.dsi.fastutil.objects.Reference2IntLinkedOpenHashMap;
 import it.unimi.dsi.fastutil.objects.Reference2IntMap;
+
 import java.security.MessageDigest;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -153,17 +156,14 @@
     return this;
   }
 
-  private void rewriteCodeWithJumboStrings(IRConverter converter, DexEncodedMethod[] methods) {
-    for (int i = 0; i < methods.length; i++) {
-      DexEncodedMethod method = methods[i];
-      if (method.getCode() == null) {
-        continue;
-      }
-      DexCode code = method.getCode().asDexCode();
-      if (code.highestSortingString != null) {
-        if (mapping.getOffsetFor(code.highestSortingString) > Constants.MAX_NON_JUMBO_INDEX) {
-          converter.processJumboStrings(method, mapping.getFirstJumboString());
-        }
+  private void rewriteCodeWithJumboStrings(IRConverter converter, DexEncodedMethod method) {
+    if (method.getCode() == null) {
+      return;
+    }
+    DexCode code = method.getCode().asDexCode();
+    if (code.highestSortingString != null) {
+      if (mapping.getOffsetFor(code.highestSortingString) > Constants.MAX_NON_JUMBO_INDEX) {
+        converter.processJumboStrings(method, mapping.getFirstJumboString());
       }
     }
   }
@@ -181,8 +181,7 @@
     // At least one method needs a jumbo string.
     IRConverter converter = new IRConverter(application, appInfo, options, false);
     for (DexProgramClass clazz : classes) {
-      rewriteCodeWithJumboStrings(converter, clazz.directMethods());
-      rewriteCodeWithJumboStrings(converter, clazz.virtualMethods());
+      clazz.forEachMethod(method -> rewriteCodeWithJumboStrings(converter, method));
     }
     return this;
   }
@@ -275,56 +274,53 @@
   private void checkInterfaceMethods() {
     for (DexProgramClass clazz : mapping.getClasses()) {
       if (clazz.isInterface()) {
-        checkInterfaceMethods(clazz.directMethods());
-        checkInterfaceMethods(clazz.virtualMethods());
+        clazz.forEachMethod(this::checkInterfaceMethod);
       }
     }
   }
 
-  // Ensures interface methods comply with requirements imposed by Android runtime:
+  // Ensures interface method comply with requirements imposed by Android runtime:
   //  -- in pre-N Android versions interfaces may only have class
   //     initializer and public abstract methods.
   //  -- starting with N interfaces may also have public or private
   //     static methods, as well as public non-abstract (default)
   //     and private instance methods.
-  private void checkInterfaceMethods(DexEncodedMethod[] methods) {
-    for (DexEncodedMethod method : methods) {
-      if (application.dexItemFactory.isClassConstructor(method.method)) {
-        continue; // Class constructor is always OK.
-      }
-      if (method.accessFlags.isStatic()) {
-        if (!options.canUseDefaultAndStaticInterfaceMethods()) {
-          throw new CompilationError("Static interface methods are only supported "
-              + "starting with Android N (--min-api " + Constants.ANDROID_N_API + "): "
-              + method.method.toSourceString());
-        }
-
-      } else {
-        if (method.accessFlags.isConstructor()) {
-          throw new CompilationError(
-              "Interface must not have constructors: " + method.method.toSourceString());
-        }
-        if (!method.accessFlags.isAbstract() && !method.accessFlags.isPrivate() &&
-            !options.canUseDefaultAndStaticInterfaceMethods()) {
-          throw new CompilationError("Default interface methods are only supported "
-              + "starting with Android N (--min-api " + Constants.ANDROID_N_API + "): "
-              + method.method.toSourceString());
-        }
-      }
-
-      if (method.accessFlags.isPrivate()) {
-        if (options.canUsePrivateInterfaceMethods()) {
-          continue;
-        }
-        throw new CompilationError("Private interface methods are only supported "
+  private void checkInterfaceMethod(DexEncodedMethod method) {
+    if (application.dexItemFactory.isClassConstructor(method.method)) {
+      return; // Class constructor is always OK.
+    }
+    if (method.accessFlags.isStatic()) {
+      if (!options.canUseDefaultAndStaticInterfaceMethods()) {
+        throw new CompilationError("Static interface methods are only supported "
             + "starting with Android N (--min-api " + Constants.ANDROID_N_API + "): "
             + method.method.toSourceString());
       }
 
-      if (!method.accessFlags.isPublic()) {
-        throw new CompilationError("Interface methods must not be "
-            + "protected or package private: " + method.method.toSourceString());
+    } else {
+      if (method.accessFlags.isConstructor()) {
+        throw new CompilationError(
+            "Interface must not have constructors: " + method.method.toSourceString());
       }
+      if (!method.accessFlags.isAbstract() && !method.accessFlags.isPrivate() &&
+          !options.canUseDefaultAndStaticInterfaceMethods()) {
+        throw new CompilationError("Default interface methods are only supported "
+            + "starting with Android N (--min-api " + Constants.ANDROID_N_API + "): "
+            + method.method.toSourceString());
+      }
+    }
+
+    if (method.accessFlags.isPrivate()) {
+      if (options.canUsePrivateInterfaceMethods()) {
+        return;
+      }
+      throw new CompilationError("Private interface methods are only supported "
+          + "starting with Android N (--min-api " + Constants.ANDROID_N_API + "): "
+          + method.method.toSourceString());
+    }
+
+    if (!method.accessFlags.isPublic()) {
+      throw new CompilationError("Interface methods must not be "
+          + "protected or package private: " + method.method.toSourceString());
     }
   }
 
@@ -332,34 +328,30 @@
       DexApplication application) {
     Map<DexCode, String> codeToSignatureMap = new IdentityHashMap<>();
     for (DexProgramClass clazz : mapping.getClasses()) {
-      addSignaturesFromMethods(clazz.directMethods(), codeToSignatureMap,
-          application.getProguardMap());
-      addSignaturesFromMethods(clazz.virtualMethods(), codeToSignatureMap,
-          application.getProguardMap());
+      clazz.forEachMethod(method ->
+          addSignaturesFromMethod(method, codeToSignatureMap, application.getProguardMap()));
     }
     DexCode[] codesArray = codes.toArray(new DexCode[codes.size()]);
     Arrays.sort(codesArray, Comparator.comparing(codeToSignatureMap::get));
     return Arrays.asList(codesArray);
   }
 
-  private static void addSignaturesFromMethods(DexEncodedMethod[] methods,
+  private static void addSignaturesFromMethod(DexEncodedMethod method,
       Map<DexCode, String> codeToSignatureMap,
       ClassNameMapper proguardMap) {
-    for (DexEncodedMethod method : methods) {
-      if (method.getCode() == null) {
-        assert method.accessFlags.isAbstract() || method.accessFlags.isNative();
+    if (method.getCode() == null) {
+      assert method.accessFlags.isAbstract() || method.accessFlags.isNative();
+    } else {
+      Signature signature;
+      String originalClassName;
+      if (proguardMap != null) {
+        signature = proguardMap.originalSignatureOf(method.method);
+        originalClassName = proguardMap.originalNameOf(method.method.holder);
       } else {
-        Signature signature;
-        String originalClassName;
-        if (proguardMap != null) {
-          signature = proguardMap.originalSignatureOf(method.method);
-          originalClassName = proguardMap.originalNameOf(method.method.holder);
-        } else {
-          signature = MethodSignature.fromDexMethod(method.method);
-          originalClassName = method.method.holder.toSourceString();
-        }
-        codeToSignatureMap.put(method.getCode().asDexCode(), originalClassName + signature);
+        signature = MethodSignature.fromDexMethod(method.method);
+        originalClassName = method.method.holder.toSourceString();
       }
+      codeToSignatureMap.put(method.getCode().asDexCode(), originalClassName + signature);
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfo.java b/src/main/java/com/android/tools/r8/graph/AppInfo.java
index a5c8ff8..3619b9c 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfo.java
@@ -42,21 +42,12 @@
     Builder<Descriptor, KeyedDexItem> builder = ImmutableMap.builder();
     DexClass clazz = app.definitionFor(type);
     if (clazz != null) {
-      registerDefinitions(builder, clazz.directMethods());
-      registerDefinitions(builder, clazz.virtualMethods());
-      registerDefinitions(builder, clazz.instanceFields());
-      registerDefinitions(builder, clazz.staticFields());
+      clazz.forEachMethod(method -> builder.put(method.getKey(), method));
+      clazz.forEachField(field -> builder.put(field.getKey(), field));
     }
     return builder.build();
   }
 
-  private void registerDefinitions(Builder<Descriptor, KeyedDexItem> builder,
-      KeyedDexItem<? extends Descriptor>[] items) {
-    for (KeyedDexItem<? extends Descriptor> item : items) {
-      builder.put(item.getKey(), item);
-    }
-  }
-
   public Iterable<DexProgramClass> classes() {
     return app.classes();
   }
diff --git a/src/main/java/com/android/tools/r8/graph/ClassAndMemberPublicizer.java b/src/main/java/com/android/tools/r8/graph/ClassAndMemberPublicizer.java
index 9583e9c..6a6b3d8 100644
--- a/src/main/java/com/android/tools/r8/graph/ClassAndMemberPublicizer.java
+++ b/src/main/java/com/android/tools/r8/graph/ClassAndMemberPublicizer.java
@@ -12,8 +12,8 @@
   public static DexApplication run(DexApplication application) {
     for (DexClass clazz : application.classes()) {
       clazz.accessFlags.promoteToPublic();
-      clazz.forEachMethod( method -> method.accessFlags.promoteNonPrivateToPublic());
-      clazz.forEachField( field -> field.accessFlags.promoteToPublic());
+      clazz.forEachMethod(method -> method.accessFlags.promoteNonPrivateToPublic());
+      clazz.forEachField(field -> field.accessFlags.promoteToPublic());
     }
     return application;
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexApplication.java b/src/main/java/com/android/tools/r8/graph/DexApplication.java
index d84dd8c..e57cd05 100644
--- a/src/main/java/com/android/tools/r8/graph/DexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/DexApplication.java
@@ -193,16 +193,11 @@
    */
   public void disassemble(Path outputDir, InternalOptions options) {
     for (DexProgramClass clazz : programClasses.getAllClasses()) {
-      for (DexEncodedMethod method : clazz.virtualMethods()) {
+      clazz.forEachMethod(method -> {
         if (options.methodMatchesFilter(method)) {
           disassemble(method, getProguardMap(), outputDir);
         }
-      }
-      for (DexEncodedMethod method : clazz.directMethods()) {
-        if (options.methodMatchesFilter(method)) {
-          disassemble(method, getProguardMap(), outputDir);
-        }
-      }
+      });
     }
   }
 
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 b28f06c..a029a61 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -11,6 +11,7 @@
 import com.google.common.base.MoreObjects;
 
 import java.util.Arrays;
+import java.util.Comparator;
 import java.util.function.Consumer;
 
 public abstract class DexClass extends DexItem {
@@ -168,10 +169,6 @@
     return false;
   }
 
-  public DexClasspathClass asClasspathClass() {
-    return null;
-  }
-
   public boolean isLibraryClass() {
     return false;
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java b/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java
index 036b8bc..27ab302 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java
@@ -41,11 +41,6 @@
   }
 
   @Override
-  public DexClasspathClass asClasspathClass() {
-    return this;
-  }
-
-  @Override
   public DexClasspathClass get() {
     return this;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CallGraph.java b/src/main/java/com/android/tools/r8/ir/conversion/CallGraph.java
index 9c76d74..8b348a0 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CallGraph.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CallGraph.java
@@ -186,7 +186,7 @@
 
   private static boolean allMethodsExists(DexApplication application, CallGraph graph) {
     for (DexProgramClass clazz : application.classes()) {
-      clazz.forEachMethod( method -> { assert graph.nodes.get(method) != null; });
+      clazz.forEachMethod(method -> { assert graph.nodes.get(method) != null; });
     }
     return true;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index a345ddf..51aa284 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -6,6 +6,8 @@
 import static com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.Flavor.ExcludeDexResources;
 import static com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.Flavor.IncludeAllResources;
 
+import com.google.common.collect.ImmutableSet;
+
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.AppInfoWithSubtyping;
@@ -38,7 +40,6 @@
 import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.Timing;
 
-import com.google.common.collect.ImmutableSet;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
@@ -229,25 +230,19 @@
       ExecutorService executor) throws ExecutionException {
     List<Future<?>> futures = new ArrayList<>();
     for (DexProgramClass clazz : classes) {
-      futures.add(executor.submit(() -> {
-        convertMethodsToDex(clazz.directMethods());
-        convertMethodsToDex(clazz.virtualMethods());
-      }));
+      futures.add(executor.submit(() -> clazz.forEachMethod(this::convertMethodToDex)));
     }
     ThreadUtils.awaitFutures(futures);
   }
 
-  private void convertMethodsToDex(DexEncodedMethod[] methods) {
-    for (int i = 0; i < methods.length; i++) {
-      DexEncodedMethod method = methods[i];
-      if (method.getCode() != null) {
-        boolean matchesMethodFilter = options.methodMatchesFilter(method);
-        if (matchesMethodFilter) {
-          if (method.getCode().isJarCode()) {
-            rewriteCode(method, ignoreOptimizationFeedback, Outliner::noProcessing);
-          }
-          updateHighestSortingStrings(method);
+  private void convertMethodToDex(DexEncodedMethod method) {
+    if (method.getCode() != null) {
+      boolean matchesMethodFilter = options.methodMatchesFilter(method);
+      if (matchesMethodFilter) {
+        if (method.getCode().isJarCode()) {
+          rewriteCode(method, ignoreOptimizationFeedback, Outliner::noProcessing);
         }
+        updateHighestSortingStrings(method);
       }
     }
   }
diff --git a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
index d7b17c6..ec7fa46 100644
--- a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
@@ -3,6 +3,9 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.naming;
 
+import com.google.common.base.Equivalence.Wrapper;
+import com.google.common.collect.Sets;
+
 import com.android.tools.r8.graph.AppInfoWithSubtyping;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedMethod;
@@ -13,8 +16,7 @@
 import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
 import com.android.tools.r8.utils.MethodSignatureEquivalence;
 import com.android.tools.r8.utils.Timing;
-import com.google.common.base.Equivalence.Wrapper;
-import com.google.common.collect.Sets;
+
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -337,19 +339,16 @@
                 : parent.createChild());
     if (holder != null) {
       boolean keepAll = holder.isLibraryClass() || holder.accessFlags.isAnnotation();
-      reserveNamesForMethods(holder.virtualMethods(), keepAll, state);
-      reserveNamesForMethods(holder.directMethods(), keepAll, state);
+      holder.forEachMethod(method -> reserveNamesForMethod(method, keepAll, state));
     }
     return state;
   }
 
-  private void reserveNamesForMethods(DexEncodedMethod[] methods,
+  private void reserveNamesForMethod(DexEncodedMethod method,
       boolean keepAll, NamingState<DexProto> state) {
-    for (DexEncodedMethod method : methods) {
-      if (keepAll || rootSet.noObfuscation.contains(method)) {
-        state.reserveName(method.method.name, method.method.proto);
-        globalState.reserveName(method.method.name, method.method.proto);
-      }
+    if (keepAll || rootSet.noObfuscation.contains(method)) {
+      state.reserveName(method.method.name, method.method.proto);
+      globalState.reserveName(method.method.name, method.method.proto);
     }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/optimize/BridgeMethodAnalysis.java b/src/main/java/com/android/tools/r8/optimize/BridgeMethodAnalysis.java
index f36e8c5..1a28332 100644
--- a/src/main/java/com/android/tools/r8/optimize/BridgeMethodAnalysis.java
+++ b/src/main/java/com/android/tools/r8/optimize/BridgeMethodAnalysis.java
@@ -12,6 +12,7 @@
 import com.android.tools.r8.graph.GraphLense;
 import com.android.tools.r8.logging.Log;
 import com.android.tools.r8.optimize.InvokeSingleTargetExtractor.InvokeKind;
+
 import java.util.IdentityHashMap;
 import java.util.Map;
 
@@ -28,35 +29,32 @@
 
   public GraphLense run() {
     for (DexClass clazz : appInfo.classes()) {
-      identifyBridgeMethods(clazz.virtualMethods());
-      identifyBridgeMethods(clazz.directMethods());
+      clazz.forEachMethod(this::identifyBridgeMethod);
     }
     return new BridgeLense(lense, bridgeTargetToBridgeMap);
   }
 
-  private void identifyBridgeMethods(DexEncodedMethod[] dexEncodedMethods) {
-    for (DexEncodedMethod method : dexEncodedMethods) {
-      if (method.accessFlags.isBridge()) {
-        InvokeSingleTargetExtractor targetExtractor = new InvokeSingleTargetExtractor();
-        method.getCode().registerReachableDefinitions(targetExtractor);
-        DexMethod target = targetExtractor.getTarget();
-        InvokeKind kind = targetExtractor.getKind();
-        if (target != null &&
-            target.proto.parameters.values.length == method.method.proto.parameters.values.length) {
-          assert !method.accessFlags.isPrivate() && !method.accessFlags.isConstructor();
-          target = lense.lookupMethod(target, method);
-          if (kind == InvokeKind.STATIC) {
-            assert method.accessFlags.isStatic();
-            DexEncodedMethod targetMethod = appInfo.lookupStaticTarget(target);
-            if (targetMethod != null) {
-              addForwarding(method, targetMethod);
-            }
-          } else if (kind == InvokeKind.VIRTUAL) {
-            // TODO(herhut): Add support for bridges with multiple targets.
-            DexEncodedMethod targetMethod = appInfo.lookupSingleVirtualTarget(target);
-            if (targetMethod != null) {
-              addForwarding(method, targetMethod);
-            }
+  private void identifyBridgeMethod(DexEncodedMethod method) {
+    if (method.accessFlags.isBridge()) {
+      InvokeSingleTargetExtractor targetExtractor = new InvokeSingleTargetExtractor();
+      method.getCode().registerReachableDefinitions(targetExtractor);
+      DexMethod target = targetExtractor.getTarget();
+      InvokeKind kind = targetExtractor.getKind();
+      if (target != null &&
+          target.proto.parameters.values.length == method.method.proto.parameters.values.length) {
+        assert !method.accessFlags.isPrivate() && !method.accessFlags.isConstructor();
+        target = lense.lookupMethod(target, method);
+        if (kind == InvokeKind.STATIC) {
+          assert method.accessFlags.isStatic();
+          DexEncodedMethod targetMethod = appInfo.lookupStaticTarget(target);
+          if (targetMethod != null) {
+            addForwarding(method, targetMethod);
+          }
+        } else if (kind == InvokeKind.VIRTUAL) {
+          // TODO(herhut): Add support for bridges with multiple targets.
+          DexEncodedMethod targetMethod = appInfo.lookupSingleVirtualTarget(target);
+          if (targetMethod != null) {
+            addForwarding(method, targetMethod);
           }
         }
       }
diff --git a/src/main/java/com/android/tools/r8/optimize/DebugStripper.java b/src/main/java/com/android/tools/r8/optimize/DebugStripper.java
index 3f83606..0eda1e5 100644
--- a/src/main/java/com/android/tools/r8/optimize/DebugStripper.java
+++ b/src/main/java/com/android/tools/r8/optimize/DebugStripper.java
@@ -17,8 +17,10 @@
 import com.android.tools.r8.naming.MemberNaming.Range;
 import com.android.tools.r8.naming.MemberNaming.Signature;
 import com.android.tools.r8.utils.InternalOptions;
+
 import it.unimi.dsi.fastutil.objects.Reference2IntMap;
 import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
+
 import java.util.List;
 
 public class DebugStripper {
@@ -162,16 +164,6 @@
     processCode(method, naming, nameCounts);
   }
 
-  private void processMethods(DexEncodedMethod[] methods, ClassNaming naming,
-      Reference2IntMap<DexString> nameCounts) {
-    if (methods == null) {
-      return;
-    }
-    for (DexEncodedMethod method : methods) {
-      processMethod(method, naming, nameCounts);
-    }
-  }
-
   public void processClass(DexProgramClass clazz) {
     if (!clazz.hasMethodsOrFields()) {
       return;
@@ -179,20 +171,16 @@
     String name = descriptorToName(clazz.type.toDescriptorString());
     ClassNaming naming = classNameMapper == null ? null : classNameMapper.getClassNaming(name);
     Reference2IntMap<DexString> nameCounts = new Reference2IntOpenHashMap<>();
-    setIntialNameCounts(nameCounts, clazz.directMethods());
-    setIntialNameCounts(nameCounts, clazz.virtualMethods());
-    processMethods(clazz.directMethods(), naming, nameCounts);
-    processMethods(clazz.virtualMethods(), naming, nameCounts);
+    clazz.forEachMethod(method -> setIntialNameCounts(nameCounts, method));
+    clazz.forEachMethod(method -> processMethod(method, naming, nameCounts));
   }
 
   private void setIntialNameCounts(Reference2IntMap<DexString> nameCounts,
-      DexEncodedMethod[] methods) {
-    for (DexEncodedMethod method : methods) {
-      if (nameCounts.containsKey(method.method.name)) {
-        nameCounts.put(method.method.name, USED_MORE_THAN_ONCE);
-      } else {
-        nameCounts.put(method.method.name, USED_ONCE);
-      }
+      DexEncodedMethod method) {
+    if (nameCounts.containsKey(method.method.name)) {
+      nameCounts.put(method.method.name, USED_MORE_THAN_ONCE);
+    } else {
+      nameCounts.put(method.method.name, USED_ONCE);
     }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java b/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java
index 6c3e715..31c7f1f 100644
--- a/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java
+++ b/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java
@@ -3,6 +3,8 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.optimize;
 
+import com.google.common.collect.Sets;
+
 import com.android.tools.r8.graph.AppInfoWithSubtyping;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexClass;
@@ -11,7 +13,7 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.logging.Log;
 import com.android.tools.r8.optimize.InvokeSingleTargetExtractor.InvokeKind;
-import com.google.common.collect.Sets;
+
 import java.util.Arrays;
 import java.util.List;
 import java.util.Set;
@@ -27,27 +29,25 @@
     this.application = application;
   }
 
-  private void identifyBridgeMethods(DexEncodedMethod[] dexEncodedMethods) {
-    for (DexEncodedMethod method : dexEncodedMethods) {
-      if (method.accessFlags.isBridge()) {
-        InvokeSingleTargetExtractor targetExtractor = new InvokeSingleTargetExtractor();
-        method.getCode().registerReachableDefinitions(targetExtractor);
-        DexMethod target = targetExtractor.getTarget();
-        InvokeKind kind = targetExtractor.getKind();
-        if (target != null &&
-            target.proto == method.method.proto) {
-          assert !method.accessFlags.isPrivate() && !method.accessFlags.isConstructor();
-          if (kind == InvokeKind.SUPER) {
-            // This is a visibility forward, so check for the direct target.
-            DexEncodedMethod targetMethod
-                = appInfo.lookupVirtualDefinition(target.getHolder(), target);
-            if (targetMethod != null && targetMethod.accessFlags.isPublic()) {
-              if (Log.ENABLED) {
-                Log.info(getClass(), "Removing visibility forwarding %s -> %s", method.method,
-                    targetMethod.method);
-              }
-              unneededVisibilityBridges.add(method);
+  private void identifyBridgeMethod(DexEncodedMethod method) {
+    if (method.accessFlags.isBridge()) {
+      InvokeSingleTargetExtractor targetExtractor = new InvokeSingleTargetExtractor();
+      method.getCode().registerReachableDefinitions(targetExtractor);
+      DexMethod target = targetExtractor.getTarget();
+      InvokeKind kind = targetExtractor.getKind();
+      if (target != null &&
+          target.proto == method.method.proto) {
+        assert !method.accessFlags.isPrivate() && !method.accessFlags.isConstructor();
+        if (kind == InvokeKind.SUPER) {
+          // This is a visibility forward, so check for the direct target.
+          DexEncodedMethod targetMethod
+              = appInfo.lookupVirtualDefinition(target.getHolder(), target);
+          if (targetMethod != null && targetMethod.accessFlags.isPublic()) {
+            if (Log.ENABLED) {
+              Log.info(getClass(), "Removing visibility forwarding %s -> %s", method.method,
+                  targetMethod.method);
             }
+            unneededVisibilityBridges.add(method);
           }
         }
       }
@@ -76,8 +76,7 @@
 
   public DexApplication run() {
     for (DexClass clazz : appInfo.classes()) {
-      identifyBridgeMethods(clazz.virtualMethods());
-      identifyBridgeMethods(clazz.directMethods());
+      clazz.forEachMethod(this::identifyBridgeMethod);
     }
     if (!unneededVisibilityBridges.isEmpty()) {
       removeUnneededVisibilityBridges();
diff --git a/src/main/java/com/android/tools/r8/shaking/MainDexListBuilder.java b/src/main/java/com/android/tools/r8/shaking/MainDexListBuilder.java
index 8a7ee43..f6ac140 100644
--- a/src/main/java/com/android/tools/r8/shaking/MainDexListBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/MainDexListBuilder.java
@@ -150,8 +150,8 @@
       addMainDexType(type);
       // Super and interfaces are live, no need to add them.
       traceAnnotationsDirectDendencies(clazz.annotations);
-      clazz.forEachField( field -> addMainDexType(field.field.type));
-      clazz.forEachMethod( method -> {
+      clazz.forEachField(field -> addMainDexType(field.field.type));
+      clazz.forEachMethod(method -> {
         traceMethodDirectDependencies(method.method);
         method.registerReachableDefinitions(codeDirectReferenceCollector);
       });
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
index 8afe151..6ac5694 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -3,6 +3,9 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking;
 
+import com.google.common.base.Equivalence.Wrapper;
+import com.google.common.collect.Sets;
+
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.DexAnnotation;
 import com.android.tools.r8.graph.DexAnnotationSet;
@@ -21,8 +24,7 @@
 import com.android.tools.r8.shaking.ProguardTypeMatcher.MatchSpecificType;
 import com.android.tools.r8.utils.MethodSignatureEquivalence;
 import com.android.tools.r8.utils.ThreadUtils;
-import com.google.common.base.Equivalence.Wrapper;
-import com.google.common.collect.Sets;
+
 import java.io.PrintStream;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -246,8 +248,8 @@
       DexType onlyIfClassKept) {
     Set<Wrapper<DexMethod>> methodsMarked = new HashSet<>();
     while (clazz != null) {
-      markMethods(clazz.directMethods(), memberKeepRules, rule, methodsMarked, onlyIfClassKept);
-      markMethods(clazz.virtualMethods(), memberKeepRules, rule, methodsMarked, onlyIfClassKept);
+      clazz.forEachMethod(method ->
+          markMethod(method, memberKeepRules, rule, methodsMarked, onlyIfClassKept));
       clazz = application.definitionFor(clazz.superType);
     }
   }
@@ -255,8 +257,7 @@
   private void markMatchingFields(DexClass clazz,
       Collection<ProguardMemberRule> memberKeepRules, ProguardConfigurationRule rule,
       DexType onlyIfClassKept) {
-    markFields(clazz.staticFields(), memberKeepRules, rule, onlyIfClassKept);
-    markFields(clazz.instanceFields(), memberKeepRules, rule, onlyIfClassKept);
+    clazz.forEachField(field -> markField(field, memberKeepRules, rule, onlyIfClassKept));
   }
 
   public static void writeSeeds(Iterable<DexItem> seeds, PrintStream out) {
@@ -371,36 +372,32 @@
     return typeCache.computeIfAbsent(type, DexType::toSourceString);
   }
 
-  private void markMethods(DexEncodedMethod[] methods, Collection<ProguardMemberRule> rules,
+  private void markMethod(DexEncodedMethod method, Collection<ProguardMemberRule> rules,
       ProguardConfigurationRule context, Set<Wrapper<DexMethod>> methodsMarked,
       DexType onlyIfClassKept) {
-    for (DexEncodedMethod method : methods) {
-      if (methodsMarked.contains(MethodSignatureEquivalence.get().wrap(method.method))) {
-        continue;
-      }
-      for (ProguardMemberRule rule : rules) {
-        if (rule.matches(method, this)) {
-          if (Log.ENABLED) {
-            Log.verbose(getClass(), "Marking method `%s` due to `%s { %s }`.", method, context,
-                rule);
-          }
-          addItemToSets(method, context, rule, onlyIfClassKept);
+    if (methodsMarked.contains(MethodSignatureEquivalence.get().wrap(method.method))) {
+      return;
+    }
+    for (ProguardMemberRule rule : rules) {
+      if (rule.matches(method, this)) {
+        if (Log.ENABLED) {
+          Log.verbose(getClass(), "Marking method `%s` due to `%s { %s }`.", method, context,
+              rule);
         }
+        addItemToSets(method, context, rule, onlyIfClassKept);
       }
     }
   }
 
-  private void markFields(DexEncodedField[] fields, Collection<ProguardMemberRule> rules,
+  private void markField(DexEncodedField field, Collection<ProguardMemberRule> rules,
       ProguardConfigurationRule context, DexType onlyIfClassKept) {
-    for (DexEncodedField field : fields) {
-      for (ProguardMemberRule rule : rules) {
-        if (rule.matches(field, this)) {
-          if (Log.ENABLED) {
-            Log.verbose(getClass(), "Marking field `%s` due to `%s { %s }`.", field, context,
-                rule);
-          }
-          addItemToSets(field, context, rule, onlyIfClassKept);
+    for (ProguardMemberRule rule : rules) {
+      if (rule.matches(field, this)) {
+        if (Log.ENABLED) {
+          Log.verbose(getClass(), "Marking field `%s` due to `%s { %s }`.", field, context,
+              rule);
         }
+        addItemToSets(field, context, rule, onlyIfClassKept);
       }
     }
   }