Add @NoMethodStaticizing annotation for testing

Change-Id: I3f3b5f26654243de5ffa991b5332a84ecc86c069
diff --git a/src/main/java/com/android/tools/r8/shaking/GlobalKeepInfoConfiguration.java b/src/main/java/com/android/tools/r8/shaking/GlobalKeepInfoConfiguration.java
index 1eeaa21..d10b35c 100644
--- a/src/main/java/com/android/tools/r8/shaking/GlobalKeepInfoConfiguration.java
+++ b/src/main/java/com/android/tools/r8/shaking/GlobalKeepInfoConfiguration.java
@@ -16,6 +16,8 @@
 
   boolean isAccessModificationEnabled();
 
+  boolean isMethodStaticizingEnabled();
+
   boolean isRepackagingEnabled();
 
   boolean isForceProguardCompatibilityEnabled();
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java b/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java
index 22fdb01..b43c44d 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java
@@ -26,6 +26,7 @@
 
   private final boolean allowClassInlining;
   private final boolean allowInlining;
+  private final boolean allowMethodStaticizing;
   private final boolean allowParameterTypeStrengthening;
   private final boolean allowReturnTypeStrengthening;
 
@@ -33,6 +34,7 @@
     super(builder);
     this.allowClassInlining = builder.isClassInliningAllowed();
     this.allowInlining = builder.isInliningAllowed();
+    this.allowMethodStaticizing = builder.isMethodStaticizingAllowed();
     this.allowParameterTypeStrengthening = builder.isParameterTypeStrengtheningAllowed();
     this.allowReturnTypeStrengthening = builder.isReturnTypeStrengtheningAllowed();
   }
@@ -64,6 +66,17 @@
     return allowInlining;
   }
 
+  public boolean isMethodStaticizingAllowed(GlobalKeepInfoConfiguration configuration) {
+    return isOptimizationAllowed(configuration)
+        && isShrinkingAllowed(configuration)
+        && configuration.isMethodStaticizingEnabled()
+        && internalIsMethodStaticizingAllowed();
+  }
+
+  boolean internalIsMethodStaticizingAllowed() {
+    return allowMethodStaticizing;
+  }
+
   public boolean isParameterTypeStrengtheningAllowed(GlobalKeepInfoConfiguration configuration) {
     return isOptimizationAllowed(configuration)
         && isShrinkingAllowed(configuration)
@@ -103,6 +116,7 @@
 
     private boolean allowClassInlining;
     private boolean allowInlining;
+    private boolean allowMethodStaticizing;
     private boolean allowParameterTypeStrengthening;
     private boolean allowReturnTypeStrengthening;
 
@@ -114,6 +128,7 @@
       super(original);
       allowClassInlining = original.internalIsClassInliningAllowed();
       allowInlining = original.internalIsInliningAllowed();
+      allowMethodStaticizing = original.internalIsMethodStaticizingAllowed();
       allowParameterTypeStrengthening = original.internalIsParameterTypeStrengtheningAllowed();
       allowReturnTypeStrengthening = original.internalIsReturnTypeStrengtheningAllowed();
     }
@@ -152,6 +167,23 @@
       return setAllowInlining(false);
     }
 
+    public boolean isMethodStaticizingAllowed() {
+      return allowMethodStaticizing;
+    }
+
+    public Builder setAllowMethodStaticizing(boolean allowMethodStaticizing) {
+      this.allowMethodStaticizing = allowMethodStaticizing;
+      return self();
+    }
+
+    public Builder allowMethodStaticizing() {
+      return setAllowMethodStaticizing(true);
+    }
+
+    public Builder disallowMethodStaticizing() {
+      return setAllowMethodStaticizing(false);
+    }
+
     public boolean isParameterTypeStrengtheningAllowed() {
       return allowParameterTypeStrengthening;
     }
@@ -211,6 +243,7 @@
       return super.internalIsEqualTo(other)
           && isClassInliningAllowed() == other.internalIsClassInliningAllowed()
           && isInliningAllowed() == other.internalIsInliningAllowed()
+          && isMethodStaticizingAllowed() == other.internalIsMethodStaticizingAllowed()
           && isParameterTypeStrengtheningAllowed()
               == other.internalIsParameterTypeStrengtheningAllowed()
           && isReturnTypeStrengtheningAllowed() == other.internalIsReturnTypeStrengtheningAllowed();
@@ -226,6 +259,7 @@
       return super.makeTop()
           .disallowClassInlining()
           .disallowInlining()
+          .disallowMethodStaticizing()
           .disallowParameterTypeStrengthening()
           .disallowReturnTypeStrengthening();
     }
@@ -235,6 +269,7 @@
       return super.makeBottom()
           .allowClassInlining()
           .allowInlining()
+          .allowMethodStaticizing()
           .allowParameterTypeStrengthening()
           .allowReturnTypeStrengthening();
     }
@@ -256,6 +291,11 @@
       return self();
     }
 
+    public Joiner disallowMethodStaticizing() {
+      builder.disallowMethodStaticizing();
+      return self();
+    }
+
     public Joiner disallowParameterTypeStrengthening() {
       builder.disallowParameterTypeStrengthening();
       return self();
@@ -277,6 +317,7 @@
       return super.merge(joiner)
           .applyIf(!joiner.builder.isClassInliningAllowed(), Joiner::disallowClassInlining)
           .applyIf(!joiner.builder.isInliningAllowed(), Joiner::disallowInlining)
+          .applyIf(!joiner.builder.isMethodStaticizingAllowed(), Joiner::disallowMethodStaticizing)
           .applyIf(
               !joiner.builder.isParameterTypeStrengtheningAllowed(),
               Joiner::disallowParameterTypeStrengthening)
diff --git a/src/main/java/com/android/tools/r8/shaking/NoMethodStaticizingRule.java b/src/main/java/com/android/tools/r8/shaking/NoMethodStaticizingRule.java
new file mode 100644
index 0000000..84fd67d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/NoMethodStaticizingRule.java
@@ -0,0 +1,84 @@
+// Copyright (c) 2022, 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.shaking;
+
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
+import java.util.List;
+
+public class NoMethodStaticizingRule extends NoOptimizationBaseRule<NoMethodStaticizingRule> {
+
+  public static final String RULE_NAME = "nomethodstaticizing";
+
+  public static class Builder
+      extends NoOptimizationBaseRule.Builder<NoMethodStaticizingRule, Builder> {
+
+    Builder() {
+      super();
+    }
+
+    @Override
+    public NoMethodStaticizingRule.Builder self() {
+      return this;
+    }
+
+    @Override
+    public NoMethodStaticizingRule build() {
+      return new NoMethodStaticizingRule(
+          origin,
+          getPosition(),
+          source,
+          buildClassAnnotations(),
+          classAccessFlags,
+          negatedClassAccessFlags,
+          classTypeNegated,
+          classType,
+          classNames,
+          buildInheritanceAnnotations(),
+          inheritanceClassName,
+          inheritanceIsExtends,
+          memberRules);
+    }
+  }
+
+  NoMethodStaticizingRule(
+      Origin origin,
+      Position position,
+      String source,
+      List<ProguardTypeMatcher> classAnnotations,
+      ProguardAccessFlags classAccessFlags,
+      ProguardAccessFlags negatedClassAccessFlags,
+      boolean classTypeNegated,
+      ProguardClassType classType,
+      ProguardClassNameList classNames,
+      List<ProguardTypeMatcher> inheritanceAnnotations,
+      ProguardTypeMatcher inheritanceClassName,
+      boolean inheritanceIsExtends,
+      List<ProguardMemberRule> memberRules) {
+    super(
+        origin,
+        position,
+        source,
+        classAnnotations,
+        classAccessFlags,
+        negatedClassAccessFlags,
+        classTypeNegated,
+        classType,
+        classNames,
+        inheritanceAnnotations,
+        inheritanceClassName,
+        inheritanceIsExtends,
+        memberRules);
+  }
+
+  public static Builder builder() {
+    return new Builder();
+  }
+
+  @Override
+  String typeString() {
+    return RULE_NAME;
+  }
+}
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 e4774ce..b1fdef2 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -500,6 +500,12 @@
           configurationBuilder.addRule(rule);
           return true;
         }
+        if (acceptString(NoMethodStaticizingRule.RULE_NAME)) {
+          ProguardConfigurationRule rule =
+              parseNoOptimizationRule(optionStart, NoMethodStaticizingRule.builder());
+          configurationBuilder.addRule(rule);
+          return true;
+        }
         if (acceptString(NoParameterTypeStrengtheningRule.RULE_NAME)) {
           ProguardConfigurationRule rule =
               parseNoOptimizationRule(optionStart, NoParameterTypeStrengtheningRule.builder());
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
index 6127004..9629fb2 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
@@ -267,6 +267,7 @@
         markMatchingFields(clazz, memberKeepRules, rule, null, ifRule);
       } else if (rule instanceof InlineRule
           || rule instanceof ConstantArgumentRule
+          || rule instanceof NoMethodStaticizingRule
           || rule instanceof NoParameterTypeStrengtheningRule
           || rule instanceof NoReturnTypeStrengtheningRule
           || rule instanceof UnusedArgumentRule
@@ -1254,6 +1255,13 @@
       } else if (context instanceof NoHorizontalClassMergingRule) {
         noHorizontalClassMerging.add(item.asClass().type);
         context.markAsUsed();
+      } else if (context instanceof NoMethodStaticizingRule) {
+        assert item.isProgramMethod();
+        dependentMinimumKeepInfo
+            .getOrCreateUnconditionalMinimumKeepInfoFor(item.getReference())
+            .asMethodJoiner()
+            .disallowMethodStaticizing();
+        context.markAsUsed();
       } else if (context instanceof NoParameterTypeStrengtheningRule) {
         assert item.isProgramMethod();
         dependentMinimumKeepInfo
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 1000a75..1541441 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -690,6 +690,11 @@
         && getProguardConfiguration().isAccessModificationAllowed();
   }
 
+  @Override
+  public boolean isMethodStaticizingEnabled() {
+    return callSiteOptimizationOptions().isMethodStaticizingEnabled();
+  }
+
   public boolean keepInnerClassStructure() {
     return getProguardConfiguration().getKeepAttributes().signature
         || getProguardConfiguration().getKeepAttributes().innerClasses;
@@ -1258,21 +1263,12 @@
   public class CallSiteOptimizationOptions {
 
     private boolean enabled = true;
-
-    // Each time we see an invoke with more dispatch targets than the threshold, we stop call site
-    // propagation for all these dispatch targets. The motivation for this is that it is expensive
-    // and that we are somewhat unlikely to have precise knowledge about the value of arguments when
-    // there are many (possibly spurious) call graph edges.
-    private final int maxNumberOfDispatchTargetsBeforeAbandoning = 10;
+    private boolean enableMethodStaticizing = true;
 
     public void disableOptimization() {
       enabled = false;
     }
 
-    public int getMaxNumberOfDispatchTargetsBeforeAbandoning() {
-      return maxNumberOfDispatchTargetsBeforeAbandoning;
-    }
-
     public int getMaxNumberOfInParameters() {
       return 10;
     }
@@ -1284,6 +1280,10 @@
       return enabled;
     }
 
+    public boolean isMethodStaticizingEnabled() {
+      return enableMethodStaticizing;
+    }
+
     public CallSiteOptimizationOptions setEnabled(boolean enabled) {
       if (enabled) {
         assert isEnabled();
@@ -1292,6 +1292,11 @@
       }
       return this;
     }
+
+    public CallSiteOptimizationOptions setEnableMethodStaticizing(boolean enableMethodStaticizing) {
+      this.enableMethodStaticizing = enableMethodStaticizing;
+      return this;
+    }
   }
 
   public class ClassInlinerOptions {
diff --git a/src/test/java/com/android/tools/r8/NoMethodStaticizing.java b/src/test/java/com/android/tools/r8/NoMethodStaticizing.java
new file mode 100644
index 0000000..ecfd3af
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/NoMethodStaticizing.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2022, 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 java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+@Target({ElementType.METHOD})
+public @interface NoMethodStaticizing {}
diff --git a/src/test/java/com/android/tools/r8/R8TestBuilder.java b/src/test/java/com/android/tools/r8/R8TestBuilder.java
index 6b68ca2..090ebb5 100644
--- a/src/test/java/com/android/tools/r8/R8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/R8TestBuilder.java
@@ -18,6 +18,7 @@
 import com.android.tools.r8.shaking.CollectingGraphConsumer;
 import com.android.tools.r8.shaking.NoFieldTypeStrengtheningRule;
 import com.android.tools.r8.shaking.NoHorizontalClassMergingRule;
+import com.android.tools.r8.shaking.NoMethodStaticizingRule;
 import com.android.tools.r8.shaking.NoParameterTypeStrengtheningRule;
 import com.android.tools.r8.shaking.NoReturnTypeStrengtheningRule;
 import com.android.tools.r8.shaking.NoUnusedInterfaceRemovalRule;
@@ -509,6 +510,12 @@
             NoFieldTypeStrengtheningRule.RULE_NAME, NoFieldTypeStrengthening.class);
   }
 
+  public T enableNoMethodStaticizingAnnotations() {
+    return addNoMethodStaticizingAnnotation()
+        .addInternalMatchAnnotationOnMethodRule(
+            NoMethodStaticizingRule.RULE_NAME, NoMethodStaticizing.class);
+  }
+
   public T enableNoParameterTypeStrengtheningAnnotations() {
     return addNoParameterTypeStrengtheningAnnotation()
         .addInternalMatchAnnotationOnMethodRule(
diff --git a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
index d4b3ecf..0c3b2ae 100644
--- a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
@@ -466,6 +466,10 @@
     return addTestingAnnotation(NoHorizontalClassMerging.class);
   }
 
+  public final T addNoMethodStaticizingAnnotation() {
+    return addTestingAnnotation(NoMethodStaticizing.class);
+  }
+
   public final T addNoParameterTypeStrengtheningAnnotation() {
     return addTestingAnnotation(NoParameterTypeStrengthening.class);
   }