Initial support for removing unused proto fields
Bug: 112437944
Change-Id: Ic17e1d0ec05489a69a319666290d639ee959bed3
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 7b34833..be25c67 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -693,12 +693,27 @@
}
}
- {
- MainDexClasses finalMainDexClasses = mainDexClasses;
+ if (appView.options().isProtoShrinkingEnabled()) {
+ // TODO(b/112437944): IRConverter.<init>() asserts that liveness information is available in
+ // AppView. This is not the case if both shrinking and minification is disabled, because we
+ // skip the second round of tree shaking in that case.
+ IRConverter converter = new IRConverter(appView, timing, null, mainDexClasses);
+
+ // If proto shrinking is enabled, we need to reprocess every dynamicMethod(). This ensures
+ // that proto fields that have been removed by the second round of tree shaking are also
+ // removed from the proto schemas in the bytecode.
+ // TODO(b/112437944): Avoid iterating the entire application to post-process every
+ // dynamicMethod() method.
+ appView.withGeneratedMessageLiteShrinker(
+ shrinker -> shrinker.postOptimizeDynamicMethods(converter));
+
+ // If proto shrinking is enabled, we need to post-process every findLiteExtensionByNumber()
+ // method. This ensures that there are no references to dead extensions that have been
+ // removed by the second round of tree shaking.
+ // TODO(b/112437944): Avoid iterating the entire application to post-process every
+ // findLiteExtensionByNumber() method.
appView.withGeneratedExtensionRegistryShrinker(
- shrinker ->
- shrinker.postOptimizeGeneratedExtensionRegistry(
- new IRConverter(appView, timing, null, finalMainDexClasses)));
+ shrinker -> shrinker.postOptimizeGeneratedExtensionRegistry(converter));
}
// Add automatic main dex classes to an eventual manual list of classes.
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 d0cbbbe..00602a7 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -526,17 +526,6 @@
return true;
}
- public DexEncodedField[] allFieldsSorted() {
- int iLen = instanceFields.length;
- int sLen = staticFields.length;
- DexEncodedField[] result = new DexEncodedField[iLen + sLen];
- System.arraycopy(instanceFields, 0, result, 0, iLen);
- System.arraycopy(staticFields, 0, result, iLen, sLen);
- Arrays.sort(result,
- (DexEncodedField a, DexEncodedField b) -> a.field.slowCompareTo(b.field));
- return result;
- }
-
/** Find static field in this class matching {@param field}. */
public DexEncodedField lookupStaticField(DexField field) {
return lookupTarget(staticFields, field);
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java
index f0f35bb..c3cd006 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java
@@ -4,9 +4,15 @@
package com.android.tools.r8.ir.analysis.proto;
+import static com.android.tools.r8.ir.analysis.proto.ProtoUtils.getInfoValueFromMessageInfoConstructionInvoke;
+import static com.android.tools.r8.ir.analysis.proto.ProtoUtils.getObjectsValueFromMessageInfoConstructionInvoke;
+import static com.android.tools.r8.ir.analysis.proto.ProtoUtils.setObjectsValueForMessageInfoConstructionInvoke;
+import static com.google.common.base.Predicates.alwaysFalse;
+
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.ir.analysis.proto.schema.ProtoMessageInfo;
import com.android.tools.r8.ir.analysis.proto.schema.ProtoObject;
import com.android.tools.r8.ir.analysis.type.Nullability;
@@ -22,9 +28,13 @@
import com.android.tools.r8.ir.code.MemberType;
import com.android.tools.r8.ir.code.NewArrayEmpty;
import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.conversion.CallSiteInformation;
+import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.ir.conversion.OptimizationFeedbackIgnore;
+import com.android.tools.r8.ir.optimize.Outliner;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.utils.BooleanUtils;
import java.util.List;
+import java.util.function.Consumer;
public class GeneratedMessageLiteShrinker {
@@ -55,11 +65,31 @@
}
public void run(DexEncodedMethod method, IRCode code) {
- if (appView.options().isMinifying() && references.isDynamicMethod(method.method)) {
+ if (references.isDynamicMethod(method.method)) {
rewriteDynamicMethod(method, code);
}
}
+ public void postOptimizeDynamicMethods(IRConverter converter) {
+ forEachDynamicMethod(
+ method ->
+ converter.processMethod(
+ method,
+ OptimizationFeedbackIgnore.getInstance(),
+ alwaysFalse(),
+ CallSiteInformation.empty(),
+ Outliner::noProcessing));
+ }
+
+ private void forEachDynamicMethod(Consumer<DexEncodedMethod> consumer) {
+ for (DexProgramClass clazz : appView.appInfo().classes()) {
+ DexEncodedMethod dynamicMethod = clazz.lookupVirtualMethod(references::isDynamicMethod);
+ if (dynamicMethod != null) {
+ consumer.accept(dynamicMethod);
+ }
+ }
+ }
+
/**
* Finds all const-string instructions in the code that flows into GeneratedMessageLite.
* newMessageInfo(), and rewrites them into a dex-item-based-const-string if the string value
@@ -76,16 +106,10 @@
InvokeMethod newMessageInfoInvoke = getNewMessageInfoInvoke(code, references);
if (newMessageInfoInvoke != null) {
- // If this invoke is targeting RawMessageInfo.<init>(...) then `info` and `objects` is at
- // positions 2 and 3, respectively, and not position 1 and 2 as when calling the static method
- // GeneratedMessageLite.newMessageInfo().
- int adjustment = BooleanUtils.intValue(newMessageInfoInvoke.isInvokeDirect());
- assert adjustment == 0
- ? newMessageInfoInvoke.getInvokedMethod().match(references.newMessageInfoMethod)
- : newMessageInfoInvoke.getInvokedMethod() == references.rawMessageInfoConstructor;
-
- Value infoValue = newMessageInfoInvoke.inValues().get(1 + adjustment).getAliasedValue();
- Value objectsValue = newMessageInfoInvoke.inValues().get(2 + adjustment).getAliasedValue();
+ Value infoValue =
+ getInfoValueFromMessageInfoConstructionInvoke(newMessageInfoInvoke, references);
+ Value objectsValue =
+ getObjectsValueFromMessageInfoConstructionInvoke(newMessageInfoInvoke, references);
// Decode the arguments passed to newMessageInfo().
ProtoMessageInfo protoMessageInfo = decoder.run(context, infoValue, objectsValue);
@@ -158,8 +182,8 @@
// Pass the newly created `objects` array to RawMessageInfo.<init>(...) or
// GeneratedMessageLite.newMessageInfo().
- int adjustment = BooleanUtils.intValue(newMessageInfoInvoke.isInvokeDirect());
- newMessageInfoInvoke.replaceValue(2 + adjustment, newObjectsValue);
+ setObjectsValueForMessageInfoConstructionInvoke(
+ newMessageInfoInvoke, newObjectsValue, references);
if (hasIntroducedIdentifierNameString) {
method.getMutableOptimizationInfo().markUseIdentifierNameString();
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/RawMessageInfoDecoder.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/RawMessageInfoDecoder.java
index f9b1d62..dd627f8 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/RawMessageInfoDecoder.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/RawMessageInfoDecoder.java
@@ -8,9 +8,11 @@
import static com.android.tools.r8.ir.analysis.proto.ProtoUtils.getObjectsValueFromMessageInfoConstructionInvoke;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.ir.analysis.proto.schema.DeadProtoFieldObject;
import com.android.tools.r8.ir.analysis.proto.schema.ProtoFieldInfo;
import com.android.tools.r8.ir.analysis.proto.schema.ProtoFieldObject;
import com.android.tools.r8.ir.analysis.proto.schema.ProtoFieldType;
@@ -119,24 +121,18 @@
ThrowingIterator<Value, InvalidRawMessageInfoException> objectIterator =
createObjectIterator(objectsValue);
- if (numberOfOneOfObjects > 0) {
- builder.setNumberOfOneOfObjects(numberOfOneOfObjects);
- for (int i = 0; i < numberOfOneOfObjects; i++) {
- builder.addOneOfObject(
- createProtoObject(
- objectIterator.computeNextIfAbsent(this::invalidObjectsFailure), context),
- createProtoObject(
- objectIterator.computeNextIfAbsent(this::invalidObjectsFailure), context));
- }
+ for (int i = 0; i < numberOfOneOfObjects; i++) {
+ builder.addOneOfObject(
+ createProtoObject(
+ objectIterator.computeNextIfAbsent(this::invalidObjectsFailure), context),
+ createProtoObject(
+ objectIterator.computeNextIfAbsent(this::invalidObjectsFailure), context));
}
- if (numberOfHasBitsObjects > 0) {
- builder.setNumberOfHasBitsObjects(numberOfHasBitsObjects);
- for (int i = 0; i < numberOfHasBitsObjects; i++) {
- builder.addHasBitsObject(
- createProtoObject(
- objectIterator.computeNextIfAbsent(this::invalidObjectsFailure), context));
- }
+ for (int i = 0; i < numberOfHasBitsObjects; i++) {
+ builder.addHasBitsObject(
+ createProtoObject(
+ objectIterator.computeNextIfAbsent(this::invalidObjectsFailure), context));
}
boolean isProto2 = (flags & IS_PROTO_2_MASK) != 0;
@@ -193,6 +189,9 @@
if (field != null) {
return new ProtoFieldObject(field);
}
+ // This const-string refers to a field that no longer exists. In this case, we create a
+ // special dead-object instead of failing with an InvalidRawMessageInfoException below.
+ return new DeadProtoFieldObject(context.type, constString.getValue());
} else if (definition.isDexItemBasedConstString()) {
DexItemBasedConstString constString = definition.asDexItemBasedConstString();
DexReference reference = constString.getItem();
@@ -200,7 +199,14 @@
if (reference.isDexField()
&& nameComputationInfo.isFieldNameComputationInfo()
&& nameComputationInfo.asFieldNameComputationInfo().isForFieldName()) {
- return new ProtoFieldObject(reference.asDexField());
+ DexField field = reference.asDexField();
+ DexEncodedField encodedField = context.lookupInstanceField(field);
+ if (encodedField != null) {
+ return new ProtoFieldObject(field);
+ }
+ // This const-string refers to a field that no longer exists. In this case, we create a
+ // special dead-object instead of failing with an InvalidRawMessageInfoException below.
+ return new DeadProtoFieldObject(context.type, field.name);
}
} else if (definition.isInvokeStatic()) {
InvokeStatic invoke = definition.asInvokeStatic();
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/DeadProtoFieldObject.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/DeadProtoFieldObject.java
new file mode 100644
index 0000000..3d2b3bc
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/DeadProtoFieldObject.java
@@ -0,0 +1,39 @@
+// 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 com.android.tools.r8.ir.analysis.proto.schema;
+
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+
+public class DeadProtoFieldObject extends ProtoObject {
+
+ // For debugging purposes only.
+ private final DexType holder;
+ private final DexString name;
+
+ public DeadProtoFieldObject(DexType holder, DexString name) {
+ this.holder = holder;
+ this.name = name;
+ }
+
+ @Override
+ public Instruction buildIR(AppView<?> appView, IRCode code) {
+ throw new Unreachable();
+ }
+
+ @Override
+ public boolean isDeadProtoFieldObject() {
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "DeadProtoFieldObject(" + holder.toSourceString() + "." + name.toSourceString() + ")";
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
index 1d77c62..15d5487 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.graph.analysis.EnqueuerAnalysis;
import com.android.tools.r8.ir.analysis.proto.GeneratedMessageLiteShrinker;
import com.android.tools.r8.ir.analysis.proto.ProtoReferences;
+import com.android.tools.r8.ir.analysis.proto.ProtoShrinker;
import com.android.tools.r8.ir.analysis.proto.RawMessageInfoDecoder;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.InvokeMethod;
@@ -55,10 +56,11 @@
new IdentityHashMap<>();
public ProtoEnqueuerExtension(AppView<?> appView) {
+ ProtoShrinker protoShrinker = appView.protoShrinker();
this.appView = appView;
- this.decoder = appView.protoShrinker().decoder;
- this.factory = appView.protoShrinker().factory;
- this.references = appView.protoShrinker().references;
+ this.decoder = protoShrinker.decoder;
+ this.factory = protoShrinker.factory;
+ this.references = protoShrinker.references;
}
/**
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoFieldInfo.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoFieldInfo.java
index d8af9b6..d9ef276 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoFieldInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoFieldInfo.java
@@ -16,7 +16,17 @@
private final int number;
private final ProtoFieldType type;
+ /**
+ * Index into {@link ProtoMessageInfo#oneOfObjects} or {@link ProtoMessageInfo#hasBitsObjects}.
+ * Only used for oneof and proto2 singular fields.
+ */
private final OptionalInt auxData;
+
+ /**
+ * For any non-oneof field, the first entry will be a reference to a java.lang.String literal. For
+ * repeated message fields, the second entry will be a reference to java.lang.Class for the
+ * message.
+ */
private final List<ProtoObject> objects;
public ProtoFieldInfo(
@@ -163,4 +173,23 @@
assert object.isProtoFieldObject();
return object.asProtoFieldObject().getField();
}
+
+ @Override
+ public String toString() {
+ StringBuilder builder =
+ new StringBuilder("ProtoFieldInfo(number=")
+ .append(number)
+ .append(", type=")
+ .append(type)
+ .append(", aux data=")
+ .append(auxData)
+ .append(", objects=[");
+ if (objects.size() > 0) {
+ builder.append(objects.get(0));
+ for (int i = 1; i < objects.size(); i++) {
+ builder.append(", ").append(objects.get(i));
+ }
+ }
+ return builder.append("])").toString();
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoFieldObject.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoFieldObject.java
index dbd3c7f..b7e708b 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoFieldObject.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoFieldObject.java
@@ -50,4 +50,9 @@
public ProtoFieldObject asProtoFieldObject() {
return this;
}
+
+ @Override
+ public String toString() {
+ return "ProtoFieldObject(" + field.toSourceString() + ")";
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoMessageInfo.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoMessageInfo.java
index d0871df..aaea56f 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoMessageInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoMessageInfo.java
@@ -7,8 +7,12 @@
import static com.android.tools.r8.ir.analysis.proto.RawMessageInfoDecoder.IS_PROTO_2_MASK;
import com.android.tools.r8.utils.Pair;
-import java.util.ArrayList;
+import it.unimi.dsi.fastutil.ints.IntArrayList;
+import it.unimi.dsi.fastutil.ints.IntList;
+import java.util.Iterator;
+import java.util.LinkedList;
import java.util.List;
+import java.util.function.Predicate;
public class ProtoMessageInfo {
@@ -18,9 +22,9 @@
private int flags;
- private List<ProtoFieldInfo> fields;
- private List<ProtoObject> hasBitsObjects;
- private List<Pair<ProtoObject, ProtoObject>> oneOfObjects;
+ private LinkedList<ProtoFieldInfo> fields;
+ private LinkedList<ProtoObject> hasBitsObjects;
+ private LinkedList<Pair<ProtoObject, ProtoObject>> oneOfObjects;
public void setFlags(int value) {
this.flags = value;
@@ -28,53 +32,98 @@
public void addField(ProtoFieldInfo field) {
if (fields == null) {
- fields = new ArrayList<>();
+ fields = new LinkedList<>();
}
fields.add(field);
}
public void addHasBitsObject(ProtoObject hasBitsObject) {
if (hasBitsObjects == null) {
- hasBitsObjects = new ArrayList<>();
+ hasBitsObjects = new LinkedList<>();
}
hasBitsObjects.add(hasBitsObject);
}
- public void setNumberOfHasBitsObjects(int number) {
- if (number > 0) {
- hasBitsObjects = new ArrayList<>(number);
- }
- }
-
public void addOneOfObject(ProtoObject first, ProtoObject second) {
if (oneOfObjects == null) {
- oneOfObjects = new ArrayList<>();
+ oneOfObjects = new LinkedList<>();
}
oneOfObjects.add(new Pair<>(first, second));
}
- public void setNumberOfOneOfObjects(int number) {
- if (number > 0) {
- oneOfObjects = new ArrayList<>(number);
+ public ProtoMessageInfo build() {
+ removeDeadFields();
+ removeUnusedSharedData();
+ return new ProtoMessageInfo(flags, fields, hasBitsObjects, oneOfObjects);
+ }
+
+ private void removeDeadFields() {
+ if (fields != null) {
+ Predicate<ProtoFieldInfo> isFieldDead =
+ field -> {
+ ProtoObject object =
+ field.getType().isOneOf()
+ ? oneOfObjects.get(field.getAuxData()).getFirst()
+ : field.getObjects().get(0);
+ return object.isDeadProtoFieldObject();
+ };
+ fields.removeIf(isFieldDead);
}
}
- public ProtoMessageInfo build() {
- return new ProtoMessageInfo(flags, fields, hasBitsObjects, oneOfObjects);
+ private void removeUnusedSharedData() {
+ // Gather used "oneof" and "hasbits" indices.
+ IntList usedOneofIndices = new IntArrayList();
+ IntList usedHasBitsIndices = new IntArrayList();
+ if (fields != null) {
+ for (ProtoFieldInfo field : fields) {
+ if (field.hasAuxData()) {
+ if (field.getType().isOneOf()) {
+ usedOneofIndices.add(field.getAuxData());
+ } else {
+ usedHasBitsIndices.add(field.getAuxData() / BITS_PER_HAS_BITS_WORD);
+ }
+ }
+ }
+ }
+
+ // Remove unused parts of "oneof" vector.
+ if (oneOfObjects != null) {
+ Iterator<Pair<ProtoObject, ProtoObject>> oneOfObjectIterator = oneOfObjects.iterator();
+ for (int i = 0; i < oneOfObjects.size(); i++) {
+ oneOfObjectIterator.next();
+ if (!usedOneofIndices.contains(i)) {
+ oneOfObjectIterator.remove();
+ }
+ }
+ }
+
+ // Remove unused parts of "hasbits" vector.
+ if (hasBitsObjects != null) {
+ Iterator<ProtoObject> hasBitsObjectIterator = hasBitsObjects.iterator();
+ for (int i = 0; i < hasBitsObjects.size(); i++) {
+ hasBitsObjectIterator.next();
+ if (!usedHasBitsIndices.contains(i)) {
+ hasBitsObjectIterator.remove();
+ }
+ }
+ }
+
+ // TODO(b/112437944): Fix up references + add a test that fails when references are not fixed.
}
}
private final int flags;
- private final List<ProtoFieldInfo> fields;
- private final List<ProtoObject> hasBitsObjects;
- private final List<Pair<ProtoObject, ProtoObject>> oneOfObjects;
+ private final LinkedList<ProtoFieldInfo> fields;
+ private final LinkedList<ProtoObject> hasBitsObjects;
+ private final LinkedList<Pair<ProtoObject, ProtoObject>> oneOfObjects;
private ProtoMessageInfo(
int flags,
- List<ProtoFieldInfo> fields,
- List<ProtoObject> hasBitsObjects,
- List<Pair<ProtoObject, ProtoObject>> oneOfObjects) {
+ LinkedList<ProtoFieldInfo> fields,
+ LinkedList<ProtoObject> hasBitsObjects,
+ LinkedList<Pair<ProtoObject, ProtoObject>> oneOfObjects) {
this.flags = flags;
this.fields = fields;
this.hasBitsObjects = hasBitsObjects;
@@ -120,4 +169,17 @@
public int numberOfOneOfObjects() {
return oneOfObjects != null ? oneOfObjects.size() : 0;
}
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder("ProtoMessageInfo(fields=[");
+ if (hasFields()) {
+ Iterator<ProtoFieldInfo> fieldIterator = fields.iterator();
+ builder.append(fieldIterator.next());
+ while (fieldIterator.hasNext()) {
+ builder.append(", ").append(fieldIterator.next());
+ }
+ }
+ return builder.append("])").toString();
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoObject.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoObject.java
index 31ef09f..ca06081 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoObject.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoObject.java
@@ -12,6 +12,10 @@
public abstract Instruction buildIR(AppView<?> appView, IRCode code);
+ public boolean isDeadProtoFieldObject() {
+ return false;
+ }
+
public boolean isProtoFieldObject() {
return false;
}
diff --git a/src/test/java/com/android/tools/r8/internal/proto/Proto2ShrinkingTest.java b/src/test/java/com/android/tools/r8/internal/proto/Proto2ShrinkingTest.java
index 6a79796..c1b3f86 100644
--- a/src/test/java/com/android/tools/r8/internal/proto/Proto2ShrinkingTest.java
+++ b/src/test/java/com/android/tools/r8/internal/proto/Proto2ShrinkingTest.java
@@ -39,11 +39,6 @@
.addProgramFiles(PROTO2_EXAMPLES_JAR, PROTO2_PROTO_JAR, PROTOBUF_LITE_JAR)
.addKeepMainRule("proto2.TestClass")
.addKeepRules(
- // TODO(b/112437944): Do not remove proto fields that are actually used in tree shaking.
- "-keepclassmembers,allowobfuscation class * extends",
- " com.google.protobuf.GeneratedMessageLite {",
- " <fields>;",
- "}",
allowAccessModification ? "-allowaccessmodification" : "")
.addKeepRuleFiles(PROTOBUF_LITE_PROGUARD_RULES)
.addOptionsModification(
diff --git a/src/test/java/com/android/tools/r8/internal/proto/Proto3ShrinkingTest.java b/src/test/java/com/android/tools/r8/internal/proto/Proto3ShrinkingTest.java
index 38bdf2b..7e876c9 100644
--- a/src/test/java/com/android/tools/r8/internal/proto/Proto3ShrinkingTest.java
+++ b/src/test/java/com/android/tools/r8/internal/proto/Proto3ShrinkingTest.java
@@ -39,11 +39,6 @@
.addProgramFiles(PROTO3_EXAMPLES_JAR, PROTO3_PROTO_JAR, PROTOBUF_LITE_JAR)
.addKeepMainRule("proto3.TestClass")
.addKeepRules(
- // TODO(b/112437944): Do not remove proto fields that are actually used in tree shaking.
- "-keepclassmembers,allowobfuscation class * extends",
- " com.google.protobuf.GeneratedMessageLite {",
- " <fields>;",
- "}",
allowAccessModification ? "-allowaccessmodification" : "")
.addKeepRuleFiles(PROTOBUF_LITE_PROGUARD_RULES)
.addOptionsModification(