Don't remove field writes for objects with non default finalizers from library
Previously we always ignored finalizers from library even when explicitly declared.
Bug: b/339371242
Change-Id: I1c70a2d25e2cf909c90f1a5bcc093530f3de8f64
diff --git a/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java b/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
index 0fbadd1..dba1c38 100644
--- a/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.FieldResolutionResult;
import com.android.tools.r8.graph.FieldResolutionResult.SingleFieldResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
@@ -197,15 +198,17 @@
if (clazz == null) {
return true;
}
- if (clazz.superType == null) {
- return false;
- }
DexItemFactory dexItemFactory = appView.dexItemFactory();
DexEncodedMethod resolutionResult =
appInfo
.resolveMethodOnClassLegacy(clazz, dexItemFactory.objectMembers.finalize)
.getSingleTarget();
- return resolutionResult != null && resolutionResult.isProgramMethod(appView);
+ if (resolutionResult == null) {
+ return false;
+ }
+ DexType holderType = resolutionResult.getHolderType();
+ return holderType.isNotIdenticalTo(dexItemFactory.objectType)
+ && holderType.isNotIdenticalTo(dexItemFactory.enumType);
}
return mayHaveFinalizeMethodDirectlyOrIndirectly(appView, baseType.asClassType());
diff --git a/src/main/java/com/android/tools/r8/shaking/ObjectAllocationInfoCollectionUtils.java b/src/main/java/com/android/tools/r8/shaking/ObjectAllocationInfoCollectionUtils.java
index 7eb81ce..30e9735 100644
--- a/src/main/java/com/android/tools/r8/shaking/ObjectAllocationInfoCollectionUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/ObjectAllocationInfoCollectionUtils.java
@@ -67,8 +67,12 @@
.resolveMethodOnLegacy(
clazz, appView.dexItemFactory().objectMembers.finalize)
.asSingleResolution();
- if (resolution != null && resolution.getResolvedHolder().isProgramClass()) {
- return TraversalContinuation.doBreak();
+ if (resolution != null) {
+ DexType resolvedType = resolution.getResolvedHolder().getType();
+ if (resolvedType.isNotIdenticalTo(appView.dexItemFactory().objectType)
+ && resolvedType.isNotIdenticalTo(appView.dexItemFactory().enumType)) {
+ return TraversalContinuation.doBreak();
+ }
}
}
return TraversalContinuation.doContinue();
diff --git a/src/test/java/com/android/tools/r8/regress/Regress339371242.java b/src/test/java/com/android/tools/r8/regress/Regress339371242.java
new file mode 100644
index 0000000..2201a1a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/regress/Regress339371242.java
@@ -0,0 +1,73 @@
+// Copyright (c) 2024, 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.regress;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class Regress339371242 extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withDefaultRuntimes().withAllApiLevels().build();
+ }
+
+ public Regress339371242(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(TestClass.class, WithLibraryFinalizer.class)
+ .addLibraryClasses(LibraryClassWithFinalizer.class)
+ .addDefaultRuntimeLibrary(parameters)
+ .addKeepMainRule(TestClass.class)
+ .setMinApi(parameters)
+ .compile()
+ .inspect(
+ codeInspector -> {
+ ClassSubject clazz = codeInspector.clazz(TestClass.class);
+ assertThat(clazz, isPresent());
+ assertThat(clazz.uniqueFieldWithOriginalName("handler"), isPresent());
+ });
+ }
+
+ public static class LibraryClassWithFinalizer {
+
+ @Override
+ protected void finalize() throws Throwable {
+ super.finalize();
+ }
+ }
+
+ public static class WithLibraryFinalizer extends LibraryClassWithFinalizer {}
+
+ public static class TestClass {
+ private final WithLibraryFinalizer handler;
+
+ public static void main(String[] args) {
+ new TestClass().foo();
+ }
+
+ public TestClass() {
+ handler = new WithLibraryFinalizer();
+ }
+
+ public void foo() {
+ System.out.println("ab");
+ }
+ }
+}