Fix record rewriting with dontobfuscate dontshrink
Change-Id: I6678ecb4830465c1a232fcfafc79f4bf1809be9d
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index da49f2e..1083cb8 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -20,6 +20,7 @@
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppServices;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexApplicationReadFlags;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexProgramClass;
@@ -678,9 +679,11 @@
timing.begin("Run postlude");
performFinalMainDexTracing(appView, executorService);
- if (appView.appInfo().hasLiveness()) {
+ // If there was a reference to java.lang.Record in the input, we rewrite the record values.
+ DexApplicationReadFlags flags = appView.appInfo().app().getFlags();
+ if (flags.hasReadRecordReferenceFromProgramClass()) {
RecordFieldValuesRewriter recordFieldArrayRemover =
- RecordFieldValuesRewriter.create(appView.withLiveness());
+ RecordFieldValuesRewriter.create(appView);
if (recordFieldArrayRemover != null) {
recordFieldArrayRemover.rewriteRecordFieldValues();
}
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index aaa4dad..b742d92 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -951,6 +951,11 @@
return (AppView<AppInfo>) this;
}
+ @SuppressWarnings("unchecked")
+ public AppView<AppInfo> unsafeWithoutClassHierarchy() {
+ return (AppView<AppInfo>) this;
+ }
+
public boolean hasLiveness() {
return appInfo().hasLiveness();
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index dcc3abf..13b059d 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -222,8 +222,7 @@
&& appView.options().canHaveVerifyErrorForUnknownUnusedReturnValue())
? new RemoveVerificationErrorForUnknownReturnedValues(appView)
: null;
- if (appView.enableWholeProgramOptimizations()) {
- assert appView.appInfo().hasLiveness();
+ if (appView.enableWholeProgramOptimizations() && appView.appInfo().hasLiveness()) {
assert appView.rootSet() != null;
AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
assumeInserter = new AssumeInserter(appViewWithLiveness);
@@ -254,7 +253,10 @@
options.enableDevirtualization ? new Devirtualizer(appViewWithLiveness) : null;
this.typeChecker = new TypeChecker(appViewWithLiveness, VerifyTypesHelper.create(appView));
} else {
- AppView<AppInfo> appViewWithoutClassHierarchy = appView.withoutClassHierarchy();
+ AppView<AppInfo> appViewWithoutClassHierarchy =
+ appView.enableWholeProgramOptimizations()
+ ? appView.unsafeWithoutClassHierarchy()
+ : appView.withoutClassHierarchy();
this.assumeInserter = null;
this.classInliner = null;
this.dynamicTypeOptimization = null;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordFieldValuesRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordFieldValuesRewriter.java
index 71e0780..839fc6a 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordFieldValuesRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordFieldValuesRewriter.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.FieldResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.code.ArrayPut;
@@ -23,7 +24,6 @@
import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.ir.conversion.MethodConversionOptions;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackIgnore;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.Timing;
import java.util.ArrayList;
import java.util.List;
@@ -32,17 +32,17 @@
/** Used to shrink record field arrays in dex compilations */
public class RecordFieldValuesRewriter {
- private final AppView<AppInfoWithLiveness> appView;
+ private final AppView<?> appView;
private final IRConverter irConverter;
- public static RecordFieldValuesRewriter create(AppView<AppInfoWithLiveness> appView) {
+ public static RecordFieldValuesRewriter create(AppView<?> appView) {
if (appView.enableWholeProgramOptimizations() && appView.options().isGeneratingDex()) {
return new RecordFieldValuesRewriter(appView);
}
return null;
}
- private RecordFieldValuesRewriter(AppView<AppInfoWithLiveness> appView) {
+ private RecordFieldValuesRewriter(AppView<?> appView) {
this.appView = appView;
irConverter = new IRConverter(appView);
}
@@ -50,18 +50,32 @@
// Called after final tree shaking, prune and minify field names and field values.
// At least one instruction is a newRecordFieldArray.
public void rewriteRecordFieldValues() {
- for (DexMethod recordFieldValuesReference : appView.appInfo().recordFieldValuesReferences) {
- DexClass dexClass =
- appView.contextIndependentDefinitionFor(recordFieldValuesReference.getHolderType());
- assert dexClass.isProgramClass();
- ProgramMethod programMethod =
- dexClass.asProgramClass().lookupProgramMethod(recordFieldValuesReference);
- assert programMethod != null;
- rewriteRecordFieldValues(programMethod);
+ if (appView.hasLiveness()) {
+ for (DexMethod recordFieldValuesReference :
+ appView.appInfo().withLiveness().recordFieldValuesReferences) {
+ DexClass dexClass =
+ appView.contextIndependentDefinitionFor(recordFieldValuesReference.getHolderType());
+ assert dexClass.isProgramClass();
+ ProgramMethod programMethod =
+ dexClass.asProgramClass().lookupProgramMethod(recordFieldValuesReference);
+ assert programMethod != null;
+ rewriteRecordFieldValues(programMethod, false);
+ }
+ } else {
+ // Without liveness, i.e., R8 called with -dontshrink, we take a slow path and exhaustively
+ // process all methods.
+ for (DexProgramClass clazz : appView.appInfo().classes()) {
+ clazz.forEachProgramMethod(
+ method -> {
+ if (method.getDefinition().hasCode()) {
+ rewriteRecordFieldValues(method, true);
+ }
+ });
+ }
}
}
- public void rewriteRecordFieldValues(ProgramMethod programMethod) {
+ public void rewriteRecordFieldValues(ProgramMethod programMethod, boolean wide) {
IRCode irCode =
programMethod
.getDefinition()
@@ -84,7 +98,7 @@
}
}
}
- assert done;
+ assert wide || done;
irConverter.removeDeadCodeAndFinalizeIR(
irCode, OptimizationFeedbackIgnore.getInstance(), Timing.empty());
}
diff --git a/src/test/examplesJava17/records/RecordHashCodeTest.java b/src/test/examplesJava17/records/RecordHashCodeTest.java
index 2f2609d..b0b0b0e 100644
--- a/src/test/examplesJava17/records/RecordHashCodeTest.java
+++ b/src/test/examplesJava17/records/RecordHashCodeTest.java
@@ -83,6 +83,28 @@
builder.run(parameters.getRuntime(), TestClass.class).assertSuccessWithOutput(EXPECTED_RESULT);
}
+ @Test
+ public void testR8DontShrinkDontObfuscate() throws Exception {
+ parameters.assumeR8TestParameters();
+ assumeTrue(parameters.isDexRuntime() || isCfRuntimeWithNativeRecordSupport());
+ R8FullTestBuilder builder =
+ testForR8(parameters.getBackend())
+ .addInnerClassesAndStrippedOuter(getClass())
+ .setMinApi(parameters)
+ .addDontShrink()
+ .addDontObfuscate()
+ .addInliningAnnotations()
+ .addKeepMainRule(TestClass.class);
+ if (parameters.isCfRuntime()) {
+ builder
+ .addLibraryProvider(JdkClassFileProvider.fromSystemJdk())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ return;
+ }
+ builder.run(parameters.getRuntime(), TestClass.class).assertSuccessWithOutput(EXPECTED_RESULT);
+ }
+
public static class TestClass {
record Person(String name, int age) {}