Mark hazzer and one-of proto fields as read from dynamicMethod() if they are written in the app
Bug: 144142878
Change-Id: I533dff931e1b47a0a6b8bd2ab3fe90e9ad41c603
diff --git a/src/main/java/com/android/tools/r8/graph/FieldAccessInfo.java b/src/main/java/com/android/tools/r8/graph/FieldAccessInfo.java
index 079b8bb..f573b76 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldAccessInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldAccessInfo.java
@@ -7,6 +7,7 @@
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
+import java.util.function.Predicate;
/** Provides immutable access to {@link FieldAccessInfoImpl}. */
public interface FieldAccessInfo {
@@ -29,5 +30,7 @@
boolean isWritten();
+ boolean isWrittenInMethodSatisfying(Predicate<DexEncodedMethod> predicate);
+
boolean isWrittenOutside(DexEncodedMethod method);
}
diff --git a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java
index a263cb6..5924435 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java
@@ -147,6 +147,23 @@
}
/**
+ * Returns true if this field is written by a method for which {@param predicate} returns true.
+ */
+ @Override
+ public boolean isWrittenInMethodSatisfying(Predicate<DexEncodedMethod> predicate) {
+ if (writesWithContexts != null) {
+ for (Set<DexEncodedMethod> encodedWriteContexts : writesWithContexts.values()) {
+ for (DexEncodedMethod encodedWriteContext : encodedWriteContexts) {
+ if (predicate.test(encodedWriteContext)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
* Returns true if this field is written by a method in the program other than {@param method}.
*/
@Override
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 fce7b09..92954e4 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
@@ -30,6 +30,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.function.Predicate;
// TODO(b/112437944): Handle cycles in the graph + add a test that fails with the current
// implementation. The current caching mechanism is unsafe, because we may mark a message as not
@@ -222,6 +223,19 @@
}
if (newlyLiveField != null) {
+ // Mark hazzer and one-of proto fields as read from dynamicMethod() if they are written in
+ // the app. This is needed to ensure that field writes are not removed from the app.
+ DexEncodedMethod defaultInitializer = clazz.getDefaultInitializer();
+ assert defaultInitializer != null;
+ Predicate<DexEncodedMethod> neitherDefaultConstructorNorDynamicMethod =
+ writer -> writer != defaultInitializer && writer != dynamicMethod;
+ if (enqueuer.isFieldWrittenInMethodSatisfying(
+ newlyLiveField, neitherDefaultConstructorNorDynamicMethod)) {
+ enqueuer.registerFieldRead(newlyLiveField.field, dynamicMethod);
+ }
+
+ // Unconditionally register the hazzer and one-of proto fields as written from
+ // dynamicMethod().
if (enqueuer.registerFieldWrite(newlyLiveField.field, dynamicMethod)) {
worklist.enqueueMarkReachableFieldAction(
clazz, newlyLiveField, KeepReason.reflectiveUseIn(dynamicMethod));
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index 245f3c6..a9f60ec 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -1907,6 +1907,12 @@
return info != null && info.isRead();
}
+ public boolean isFieldWrittenInMethodSatisfying(
+ DexEncodedField field, Predicate<DexEncodedMethod> predicate) {
+ FieldAccessInfoImpl info = fieldAccessInfoCollection.get(field.field);
+ return info != null && info.isWrittenInMethodSatisfying(predicate);
+ }
+
public boolean isFieldWrittenOutsideDefaultConstructor(DexEncodedField field) {
FieldAccessInfoImpl info = fieldAccessInfoCollection.get(field.field);
if (info == null) {