Merge "Fix LambdaTestRunner using Android jar for Java Runtime test"
diff --git a/src/main/java/com/android/tools/r8/graph/ObjectToOffsetMapping.java b/src/main/java/com/android/tools/r8/graph/ObjectToOffsetMapping.java
index 09d6696..b3be107 100644
--- a/src/main/java/com/android/tools/r8/graph/ObjectToOffsetMapping.java
+++ b/src/main/java/com/android/tools/r8/graph/ObjectToOffsetMapping.java
@@ -5,9 +5,9 @@
 
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.errors.CompilationError;
-import it.unimi.dsi.fastutil.objects.Reference2IntArrayMap;
 import it.unimi.dsi.fastutil.objects.Reference2IntLinkedOpenHashMap;
 import it.unimi.dsi.fastutil.objects.Reference2IntMap;
+import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
@@ -96,35 +96,43 @@
    * superclasses and interfaces is 1.
    */
   private static class ProgramClassDepthsMemoized {
+
+    private final static int UNKNOWN_DEPTH = -1;
+
     private final DexApplication application;
-    private final Reference2IntMap<DexProgramClass> depthOfClasses = new Reference2IntArrayMap<>();
+    private final Reference2IntMap<DexProgramClass> depthOfClasses = new Reference2IntOpenHashMap<>();
 
     ProgramClassDepthsMemoized(DexApplication application) {
       this.application = application;
+      depthOfClasses.defaultReturnValue(UNKNOWN_DEPTH);
     }
 
     int getDepth(DexProgramClass programClass) {
-      return depthOfClasses.computeIfAbsent(
-          programClass,
-          programClassToCompute -> {
-            // Emulating the algorithm of com.android.dx.merge.SortableType.tryAssignDepth().
-            DexType superType = programClassToCompute.superType;
-            int maxDepth;
-            if (superType == null) {
-              maxDepth = 0;
-            } else {
-              maxDepth = 1;
-              DexProgramClass superClass = application.programDefinitionFor(superType);
-              if (superClass != null) {
-                maxDepth = getDepth(superClass);
-              }
-            }
-            for (DexType inf : programClassToCompute.interfaces.values) {
-              DexProgramClass infClass = application.programDefinitionFor(inf);
-              maxDepth = Math.max(maxDepth, infClass == null ? 1 : getDepth(infClass));
-            }
-            return maxDepth + 1;
-          });
+      int depth = depthOfClasses.getInt(programClass);
+
+      // TODO(b/65536002): use "computeIntIfAbsent" after upgrading to fastutils 8.x.
+      if (depth == UNKNOWN_DEPTH) {
+        // Emulating the algorithm of com.android.dx.merge.SortableType.tryAssignDepth().
+        DexType superType = programClass.superType;
+        int maxDepth;
+        if (superType == null) {
+          maxDepth = 0;
+        } else {
+          maxDepth = 1;
+          DexProgramClass superClass = application.programDefinitionFor(superType);
+          if (superClass != null) {
+            maxDepth = getDepth(superClass);
+          }
+        }
+        for (DexType inf : programClass.interfaces.values) {
+          DexProgramClass infClass = application.programDefinitionFor(inf);
+          maxDepth = Math.max(maxDepth, infClass == null ? 1 : getDepth(infClass));
+        }
+        depth = maxDepth + 1;
+        depthOfClasses.put(programClass, depth);
+      }
+
+      return depth;
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
index 38aa903..9b89374 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -615,7 +615,9 @@
     }
 
     private void parseRuleModifiers(ProguardKeepRule.Builder builder) {
+      skipWhitespace();
       while (acceptChar(',')) {
+        skipWhitespace();
         if (acceptString("allow")) {
           if (acceptString("shrinking")) {
             builder.getModifiersBuilder().setAllowsShrinking(true);
@@ -627,6 +629,7 @@
         } else if (acceptString("includedescriptorclasses")) {
           builder.getModifiersBuilder().setIncludeDescriptorClasses(true);
         }
+        skipWhitespace();
       }
     }
 
diff --git a/src/main/java/com/android/tools/r8/utils/FeatureClassMapping.java b/src/main/java/com/android/tools/r8/utils/FeatureClassMapping.java
index ec7a22a..7c53ff8 100644
--- a/src/main/java/com/android/tools/r8/utils/FeatureClassMapping.java
+++ b/src/main/java/com/android/tools/r8/utils/FeatureClassMapping.java
@@ -81,7 +81,7 @@
 
   private FeatureClassMapping() {}
 
-  private void addMapping(String clazz, String feature) throws FeatureMappingException {
+  public void addMapping(String clazz, String feature) throws FeatureMappingException {
     addRule(clazz, feature, 0);
   }
 
diff --git a/src/test/java/com/android/tools/r8/naming/LambdaRenamingTestRunner.java b/src/test/java/com/android/tools/r8/naming/LambdaRenamingTestRunner.java
index 7a2123e..9e062b3 100644
--- a/src/test/java/com/android/tools/r8/naming/LambdaRenamingTestRunner.java
+++ b/src/test/java/com/android/tools/r8/naming/LambdaRenamingTestRunner.java
@@ -123,7 +123,7 @@
       // TODO(b/75997473): Enable inlining when supported by CF backend
       ToolHelper.runR8(builder.build(), options -> options.enableInlining = false);
     } else {
-      builder.setMinApiLevel(AndroidApiLevel.O.getLevel());
+      builder.setMinApiLevel(ToolHelper.getMinApiLevelForDexVm().getLevel());
       ToolHelper.runR8(builder.build());
     }
   }
diff --git a/src/test/java/com/android/tools/r8/shaking/InstantiatedLambdasTestRunner.java b/src/test/java/com/android/tools/r8/shaking/InstantiatedLambdasTestRunner.java
index 4e638df..27f5321 100644
--- a/src/test/java/com/android/tools/r8/shaking/InstantiatedLambdasTestRunner.java
+++ b/src/test/java/com/android/tools/r8/shaking/InstantiatedLambdasTestRunner.java
@@ -116,7 +116,7 @@
       // TODO(b/75997473): Enable inlining when supported by CF backend
       ToolHelper.runR8(builder.build(), options -> options.enableInlining = false);
     } else {
-      builder.setMinApiLevel(AndroidApiLevel.O.getLevel());
+      builder.setMinApiLevel(ToolHelper.getMinApiLevelForDexVm().getLevel());
       ToolHelper.runR8(builder.build());
     }
   }
diff --git a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
index da4d57f..89a7291 100644
--- a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
@@ -808,6 +808,33 @@
   }
 
   @Test
+  public void parseKeepModifiers() {
+    List<String> ws = ImmutableList.of("", " ", "   ", "\t", " \t", " \t", " \t ", " \t\t \t ");
+
+    for (String before : ws) {
+      for (String after : ws) {
+        reset();
+        ProguardConfiguration config = parseAndVerifyParserEndsCleanly(ImmutableList.of(
+            "-keep"
+                + before + "," + after + "includedescriptorclasses"
+                + before + "," + after + "allowshrinking"
+                + before + "," + after + "allowobfuscation"
+                + before + "," + after + "allowoptimization "
+                + "class A { *; }"
+        ));
+      }
+    }
+  }
+
+  @Test
+  public void regress78442725() {
+    parseAndVerifyParserEndsCleanly(ImmutableList.of(
+        "-keep, includedescriptorclasses class in.uncod.android.bypass.Document { *; }",
+        "-keep, includedescriptorclasses class in.uncod.android.bypass.Element { *; }"
+    ));
+  }
+
+  @Test
   public void parseSeeds() throws Exception {
     ProguardConfigurationParser parser =
         new ProguardConfigurationParser(new DexItemFactory(), reporter);