Class inlining of extendable proto builders

Change-Id: I88ff67f879b4a7dbcc9e9a9addb4042f5ca241f8
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteBuilderShrinker.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteBuilderShrinker.java
index 552fa07..e2881ad 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteBuilderShrinker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteBuilderShrinker.java
@@ -8,7 +8,6 @@
 import com.android.tools.r8.graph.AppInfoWithSubtyping;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
@@ -60,11 +59,17 @@
   public static void addInliningHeuristicsForBuilderInlining(
       AppView<? extends AppInfoWithSubtyping> appView,
       PredicateSet<DexType> alwaysClassInline,
+      Set<DexType> neverMerge,
       Set<DexMethod> alwaysInline,
       Set<DexMethod> neverInline,
       Set<DexMethod> bypassClinitforInlining) {
     new RootSetExtension(
-            appView, alwaysClassInline, alwaysInline, neverInline, bypassClinitforInlining)
+            appView,
+            alwaysClassInline,
+            neverMerge,
+            alwaysInline,
+            neverInline,
+            bypassClinitforInlining)
         .extend();
   }
 
@@ -181,6 +186,8 @@
     private final ProtoReferences references;
 
     private final PredicateSet<DexType> alwaysClassInline;
+    private final Set<DexType> neverMerge;
+
     private final Set<DexMethod> alwaysInline;
     private final Set<DexMethod> neverInline;
     private final Set<DexMethod> bypassClinitforInlining;
@@ -188,12 +195,14 @@
     RootSetExtension(
         AppView<? extends AppInfoWithSubtyping> appView,
         PredicateSet<DexType> alwaysClassInline,
+        Set<DexType> neverMerge,
         Set<DexMethod> alwaysInline,
         Set<DexMethod> neverInline,
         Set<DexMethod> bypassClinitforInlining) {
       this.appView = appView;
       this.references = appView.protoShrinker().references;
       this.alwaysClassInline = alwaysClassInline;
+      this.neverMerge = neverMerge;
       this.alwaysInline = alwaysInline;
       this.neverInline = neverInline;
       this.bypassClinitforInlining = bypassClinitforInlining;
@@ -208,10 +217,10 @@
 
       // * extends GeneratedMessageLite heuristics.
       bypassClinitforInliningNewBuilderMethods();
-      alwaysInlineDynamicMethodFromGeneratedMessageLiteImplementations();
 
       // GeneratedMessageLite$Builder heuristics.
-      alwaysInlineBuildPartialFromGeneratedMessageLiteBuilder();
+      alwaysInlineBuildPartialFromGeneratedMessageLiteExtendableBuilder();
+      neverMergeGeneratedMessageLiteBuilder();
     }
 
     private void alwaysClassInlineGeneratedMessageLiteBuilders() {
@@ -236,29 +245,20 @@
       }
     }
 
-    private void alwaysInlineBuildPartialFromGeneratedMessageLiteBuilder() {
-      alwaysInline.add(references.generatedMessageLiteBuilderMethods.buildPartialMethod);
+    private void alwaysInlineBuildPartialFromGeneratedMessageLiteExtendableBuilder() {
+      alwaysInline.add(references.generatedMessageLiteExtendableBuilderMethods.buildPartialMethod);
     }
 
     private void alwaysInlineCreateBuilderFromGeneratedMessageLite() {
       alwaysInline.add(references.generatedMessageLiteMethods.createBuilderMethod);
     }
 
-    private void alwaysInlineDynamicMethodFromGeneratedMessageLiteImplementations() {
-      // TODO(b/132600418): We should be able to determine that dynamicMethod() becomes 'SIMPLE'
-      //  when the MethodToInvoke argument is MethodToInvoke.NEW_BUILDER.
-      DexItemFactory dexItemFactory = appView.dexItemFactory();
-      for (DexType type : appView.appInfo().subtypes(references.generatedMessageLiteType)) {
-        alwaysInline.add(
-            dexItemFactory.createMethod(
-                type,
-                dexItemFactory.createProto(
-                    dexItemFactory.objectType,
-                    references.methodToInvokeType,
-                    dexItemFactory.objectType,
-                    dexItemFactory.objectType),
-                "dynamicMethod"));
-      }
+    private void neverMergeGeneratedMessageLiteBuilder() {
+      // For consistency, never merge the GeneratedMessageLite builders. These will only have a
+      // unique subtype if the application has a single proto message, which mostly happens during
+      // testing.
+      neverMerge.add(references.generatedMessageLiteBuilderType);
+      neverMerge.add(references.generatedMessageLiteExtendableBuilderType);
     }
 
     /**
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoInliningReasonStrategy.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoInliningReasonStrategy.java
index 56fd8e9..de37f38 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoInliningReasonStrategy.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoInliningReasonStrategy.java
@@ -7,6 +7,7 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.ir.analysis.proto.ProtoReferences.MethodToInvokeMembers;
 import com.android.tools.r8.ir.code.Instruction;
 import com.android.tools.r8.ir.code.InvokeMethod;
@@ -22,19 +23,30 @@
 
   private static final int METHOD_TO_INVOKE_ARGUMENT_POSITION_IN_DYNAMIC_METHOD = 1;
 
+  private final AppView<?> appView;
   private final InliningReasonStrategy parent;
   private final ProtoReferences references;
 
   public ProtoInliningReasonStrategy(AppView<?> appView, InliningReasonStrategy parent) {
+    this.appView = appView;
     this.parent = parent;
     this.references = appView.protoShrinker().references;
   }
 
   @Override
-  public Reason computeInliningReason(InvokeMethod invoke, DexEncodedMethod target) {
+  public Reason computeInliningReason(
+      InvokeMethod invoke, DexEncodedMethod target, DexEncodedMethod context) {
+    DexProgramClass enclosingClass = appView.definitionFor(context.method.holder).asProgramClass();
+    if (references.isAbstractGeneratedMessageLiteBuilder(enclosingClass)
+        && invoke.isInvokeSuper()) {
+      // Aggressively inline invoke-super calls inside the GeneratedMessageLite builders. Such
+      // instructions prohibit inlining of the enclosing method into other contexts, and therefore
+      // block class inlining of proto builders.
+      return Reason.ALWAYS;
+    }
     return references.isDynamicMethod(target) || references.isDynamicMethodBridge(target)
         ? computeInliningReasonForDynamicMethod(invoke)
-        : parent.computeInliningReason(invoke, target);
+        : parent.computeInliningReason(invoke, target, context);
   }
 
   private Reason computeInliningReasonForDynamicMethod(InvokeMethod invoke) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoReferences.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoReferences.java
index aaf3483..aacb2a0 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoReferences.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoReferences.java
@@ -21,12 +21,15 @@
   public final DexType generatedExtensionType;
   public final DexType generatedMessageLiteType;
   public final DexType generatedMessageLiteBuilderType;
+  public final DexType generatedMessageLiteExtendableBuilderType;
   public final DexType rawMessageInfoType;
   public final DexType messageLiteType;
   public final DexType methodToInvokeType;
 
   public final GeneratedMessageLiteMethods generatedMessageLiteMethods;
   public final GeneratedMessageLiteBuilderMethods generatedMessageLiteBuilderMethods;
+  public final GeneratedMessageLiteExtendableBuilderMethods
+      generatedMessageLiteExtendableBuilderMethods;
   public final MethodToInvokeMembers methodToInvokeMembers;
 
   public final DexString dynamicMethodName;
@@ -54,6 +57,9 @@
     generatedMessageLiteBuilderType =
         factory.createType(
             factory.createString("Lcom/google/protobuf/GeneratedMessageLite$Builder;"));
+    generatedMessageLiteExtendableBuilderType =
+        factory.createType(
+            factory.createString("Lcom/google/protobuf/GeneratedMessageLite$ExtendableBuilder;"));
     rawMessageInfoType =
         factory.createType(factory.createString("Lcom/google/protobuf/RawMessageInfo;"));
     messageLiteType = factory.createType(factory.createString("Lcom/google/protobuf/MessageLite;"));
@@ -89,9 +95,16 @@
 
     generatedMessageLiteMethods = new GeneratedMessageLiteMethods(factory);
     generatedMessageLiteBuilderMethods = new GeneratedMessageLiteBuilderMethods(factory);
+    generatedMessageLiteExtendableBuilderMethods =
+        new GeneratedMessageLiteExtendableBuilderMethods(factory);
     methodToInvokeMembers = new MethodToInvokeMembers(factory);
   }
 
+  public boolean isAbstractGeneratedMessageLiteBuilder(DexProgramClass clazz) {
+    return clazz.type == generatedMessageLiteBuilderType
+        || clazz.type == generatedMessageLiteExtendableBuilderType;
+  }
+
   public boolean isDynamicMethod(DexMethod method) {
     return method.name == dynamicMethodName && method.proto == dynamicMethodProto;
   }
@@ -114,7 +127,9 @@
   }
 
   public boolean isGeneratedMessageLiteBuilder(DexProgramClass clazz) {
-    return clazz.superType == generatedMessageLiteBuilderType;
+    return (clazz.superType == generatedMessageLiteBuilderType
+            || clazz.superType == generatedMessageLiteExtendableBuilderType)
+        && !isAbstractGeneratedMessageLiteBuilder(clazz);
   }
 
   public boolean isMessageInfoConstructionMethod(DexMethod method) {
@@ -165,6 +180,19 @@
     }
   }
 
+  class GeneratedMessageLiteExtendableBuilderMethods {
+
+    public final DexMethod buildPartialMethod;
+
+    private GeneratedMessageLiteExtendableBuilderMethods(DexItemFactory dexItemFactory) {
+      buildPartialMethod =
+          dexItemFactory.createMethod(
+              generatedMessageLiteExtendableBuilderType,
+              dexItemFactory.createProto(extendableMessageType),
+              "buildPartial");
+    }
+  }
+
   public class MethodToInvokeMembers {
 
     public final DexField buildMessageInfoField;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
index 145bb62..8370b69 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
@@ -325,6 +325,7 @@
   public InlineAction computeInlining(
       InvokeMethod invoke,
       DexEncodedMethod singleTarget,
+      DexEncodedMethod context,
       ClassInitializationAnalysis classInitializationAnalysis,
       WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) {
     if (isSingleTargetInvalid(invoke, singleTarget, whyAreYouNotInliningReporter)) {
@@ -335,7 +336,7 @@
       return null;
     }
 
-    Reason reason = reasonStrategy.computeInliningReason(invoke, singleTarget);
+    Reason reason = reasonStrategy.computeInliningReason(invoke, singleTarget, context);
     if (reason == Reason.NEVER) {
       return null;
     }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ForcedInliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/ForcedInliningOracle.java
index 15281a5..26c28d7 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ForcedInliningOracle.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ForcedInliningOracle.java
@@ -61,6 +61,7 @@
   public InlineAction computeInlining(
       InvokeMethod invoke,
       DexEncodedMethod singleTarget,
+      DexEncodedMethod context,
       ClassInitializationAnalysis classInitializationAnalysis,
       WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) {
     return computeForInvoke(invoke, whyAreYouNotInliningReporter);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
index eeff808..5bbc662 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -948,7 +948,11 @@
                   : WhyAreYouNotInliningReporter.createFor(singleTarget, appView, context);
           InlineAction action =
               oracle.computeInlining(
-                  invoke, singleTarget, classInitializationAnalysis, whyAreYouNotInliningReporter);
+                  invoke,
+                  singleTarget,
+                  context,
+                  classInitializationAnalysis,
+                  whyAreYouNotInliningReporter);
           if (action == null) {
             assert whyAreYouNotInliningReporter.unsetReasonHasBeenReportedFlag();
             continue;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/InliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/InliningOracle.java
index 3fbb76f..6f4570f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/InliningOracle.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/InliningOracle.java
@@ -31,6 +31,7 @@
   InlineAction computeInlining(
       InvokeMethod invoke,
       DexEncodedMethod singleTarget,
+      DexEncodedMethod context,
       ClassInitializationAnalysis classInitializationAnalysis,
       WhyAreYouNotInliningReporter whyAreYouNotInliningReporter);
 }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
index 50c0d30..a5b8d52 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
@@ -1112,6 +1112,7 @@
           oracle.computeInlining(
               invoke,
               singleTarget,
+              method,
               ClassInitializationAnalysis.trivial(),
               NopWhyAreYouNotInliningReporter.getInstance());
       if (inlineAction == null) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/inliner/DefaultInliningReasonStrategy.java b/src/main/java/com/android/tools/r8/ir/optimize/inliner/DefaultInliningReasonStrategy.java
index f28afbe..5c45a37 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/inliner/DefaultInliningReasonStrategy.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/inliner/DefaultInliningReasonStrategy.java
@@ -28,7 +28,8 @@
   }
 
   @Override
-  public Reason computeInliningReason(InvokeMethod invoke, DexEncodedMethod target) {
+  public Reason computeInliningReason(
+      InvokeMethod invoke, DexEncodedMethod target, DexEncodedMethod context) {
     if (target.getOptimizationInfo().forceInline()
         || (appView.appInfo().hasLiveness()
             && appView.withLiveness().appInfo().forceInline.contains(target.method))) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/inliner/FixedInliningReasonStrategy.java b/src/main/java/com/android/tools/r8/ir/optimize/inliner/FixedInliningReasonStrategy.java
index 90fc4f0..2b4c072 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/inliner/FixedInliningReasonStrategy.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/inliner/FixedInliningReasonStrategy.java
@@ -17,7 +17,8 @@
   }
 
   @Override
-  public Reason computeInliningReason(InvokeMethod invoke, DexEncodedMethod target) {
+  public Reason computeInliningReason(
+      InvokeMethod invoke, DexEncodedMethod target, DexEncodedMethod context) {
     return reason;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/inliner/InliningReasonStrategy.java b/src/main/java/com/android/tools/r8/ir/optimize/inliner/InliningReasonStrategy.java
index 5450988..6e8e1aa 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/inliner/InliningReasonStrategy.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/inliner/InliningReasonStrategy.java
@@ -10,5 +10,6 @@
 
 public interface InliningReasonStrategy {
 
-  Reason computeInliningReason(InvokeMethod invoke, DexEncodedMethod target);
+  Reason computeInliningReason(
+      InvokeMethod invoke, DexEncodedMethod target, DexEncodedMethod context);
 }
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 62a0553..f27bd26 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -296,7 +296,12 @@
     }
     if (appView.options().protoShrinking().enableGeneratedMessageLiteBuilderShrinking) {
       GeneratedMessageLiteBuilderShrinker.addInliningHeuristicsForBuilderInlining(
-          appView, alwaysClassInline, alwaysInline, neverInline, bypassClinitforInlining);
+          appView,
+          alwaysClassInline,
+          neverMerge,
+          alwaysInline,
+          neverInline,
+          bypassClinitforInlining);
     }
     assert Sets.intersection(neverInline, alwaysInline).isEmpty()
             && Sets.intersection(neverInline, forceInline).isEmpty()
diff --git a/src/test/examplesProto/proto2/HasFlaggedOffExtensionBuilderTestClass.java b/src/test/examplesProto/proto2/HasFlaggedOffExtensionBuilderTestClass.java
new file mode 100644
index 0000000..9cc9e84
--- /dev/null
+++ b/src/test/examplesProto/proto2/HasFlaggedOffExtensionBuilderTestClass.java
@@ -0,0 +1,45 @@
+// Copyright (c) 2019, 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 proto2;
+
+import com.android.tools.r8.proto2.Shrinking.HasFlaggedOffExtension;
+import com.google.protobuf.ExtensionRegistryLite;
+import com.google.protobuf.InvalidProtocolBufferException;
+import com.google.protobuf.WireFormat;
+import java.nio.ByteBuffer;
+
+public class HasFlaggedOffExtensionBuilderTestClass {
+
+  // A protobuf payload indicating that varint field 1 is set to 42.
+  // See https://developers.google.com/protocol-buffers/docs/encoding
+  //
+  // Since serialization and deserialization use the same schema (which we're modifying), testing
+  // against wire-format data is preferred.
+  private static final byte[] FIELD1_SET_TO_42 =
+      new byte[] {(1 << 3) | WireFormat.WIRETYPE_VARINT, 42};
+
+  // A protobuf payload indicating that field 10 is a message whose field 1 is set to 42.
+  private static final byte[] MESSAGE10_WITH_FIELD1_SET_TO_42 =
+      ByteBuffer.allocate(4)
+          .put(
+              new byte[] {
+                (10 << 3) | WireFormat.WIRETYPE_LENGTH_DELIMITED, (byte) FIELD1_SET_TO_42.length
+              })
+          .put(FIELD1_SET_TO_42)
+          .array();
+
+  public static void main(String[] args) {
+    HasFlaggedOffExtension msg;
+    try {
+      msg =
+          HasFlaggedOffExtension.parseFrom(
+              MESSAGE10_WITH_FIELD1_SET_TO_42, ExtensionRegistryLite.getGeneratedRegistry());
+    } catch (InvalidProtocolBufferException e) {
+      System.out.println("Unexpected exception: " + e);
+      throw new RuntimeException(e);
+    }
+    Printer.print(HasFlaggedOffExtension.newBuilder(msg).build());
+  }
+}
diff --git a/src/test/examplesProto/proto2/Printer.java b/src/test/examplesProto/proto2/Printer.java
index fd87ef0..0d6f506 100644
--- a/src/test/examplesProto/proto2/Printer.java
+++ b/src/test/examplesProto/proto2/Printer.java
@@ -4,20 +4,25 @@
 
 package proto2;
 
+import com.android.tools.r8.proto2.Shrinking.HasFlaggedOffExtension;
 import com.android.tools.r8.proto2.TestProto.Primitives;
 
 public class Printer {
 
-  static void print(Primitives primitives) {
-    System.out.println(primitives.hasFooInt32());
-    System.out.println(primitives.getFooInt32());
-    System.out.println(primitives.hasOneofString());
-    System.out.println(primitives.getOneofString());
-    System.out.println(primitives.hasOneofUint32());
-    System.out.println(primitives.getOneofUint32());
-    System.out.println(primitives.hasBarInt64());
-    System.out.println(primitives.getBarInt64());
-    System.out.println(primitives.hasQuxString());
-    System.out.println(primitives.getQuxString());
+  static void print(HasFlaggedOffExtension msg) {
+    System.out.println(msg.getSerializedSize());
+  }
+
+  static void print(Primitives msg) {
+    System.out.println(msg.hasFooInt32());
+    System.out.println(msg.getFooInt32());
+    System.out.println(msg.hasOneofString());
+    System.out.println(msg.getOneofString());
+    System.out.println(msg.hasOneofUint32());
+    System.out.println(msg.getOneofUint32());
+    System.out.println(msg.hasBarInt64());
+    System.out.println(msg.getBarInt64());
+    System.out.println(msg.hasQuxString());
+    System.out.println(msg.getQuxString());
   }
 }
diff --git a/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderShrinkingTest.java b/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderShrinkingTest.java
index b17cae5..6bf5d8f 100644
--- a/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderShrinkingTest.java
+++ b/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderShrinkingTest.java
@@ -47,12 +47,14 @@
             ImmutableList.of("proto2.BuilderWithProtoBuilderSetterTestClass"),
             ImmutableList.of("proto2.BuilderWithProtoSetterTestClass"),
             ImmutableList.of("proto2.BuilderWithReusedSettersTestClass"),
+            ImmutableList.of("proto2.HasFlaggedOffExtensionBuilderTestClass"),
             ImmutableList.of(
                 "proto2.BuilderWithOneofSetterTestClass",
                 "proto2.BuilderWithPrimitiveSettersTestClass",
                 "proto2.BuilderWithProtoBuilderSetterTestClass",
                 "proto2.BuilderWithProtoSetterTestClass",
-                "proto2.BuilderWithReusedSettersTestClass")),
+                "proto2.BuilderWithReusedSettersTestClass",
+                "proto2.HasFlaggedOffExtensionBuilderTestClass")),
         getTestParameters().withAllRuntimesAndApiLevels().build());
   }
 
@@ -70,11 +72,22 @@
             .addKeepRuleFiles(PROTOBUF_LITE_PROGUARD_RULES)
             .addOptionsModification(
                 options -> {
+                  assert !options.applyInliningToInlinee;
                   options.applyInliningToInlinee = true;
+
+                  assert !options.enableFieldBitAccessAnalysis;
                   options.enableFieldBitAccessAnalysis = true;
+
+                  assert !options.protoShrinking().enableGeneratedExtensionRegistryShrinking;
                   options.protoShrinking().enableGeneratedExtensionRegistryShrinking = true;
+
+                  assert !options.protoShrinking().enableGeneratedMessageLiteShrinking;
                   options.protoShrinking().enableGeneratedMessageLiteShrinking = true;
+
+                  assert !options.protoShrinking().enableGeneratedMessageLiteBuilderShrinking;
                   options.protoShrinking().enableGeneratedMessageLiteBuilderShrinking = true;
+
+                  assert !options.enableStringSwitchConversion;
                   options.enableStringSwitchConversion = true;
                 })
             .allowAccessModification()
@@ -154,6 +167,8 @@
             "0",
             "true",
             "qux");
+      case "proto2.HasFlaggedOffExtensionBuilderTestClass":
+        return StringUtils.lines("4");
       default:
         throw new Unreachable();
     }
@@ -166,6 +181,12 @@
 
   private void verifyBuildersAreAbsent(CodeInspector outputInspector) {
     assertThat(
+        outputInspector.clazz(
+            "com.android.tools.r8.proto2.Shrinking$HasFlaggedOffExtension$Builder"),
+        mains.equals(ImmutableList.of("proto2.HasFlaggedOffExtensionBuilderTestClass"))
+            ? isPresent()
+            : not(isPresent()));
+    assertThat(
         outputInspector.clazz("com.android.tools.r8.proto2.TestProto$Primitives$Builder"),
         not(isPresent()));
     assertThat(