Fix Record compilation
- Remove irrelevant warnings
- Do not report invalid dependency edges
Bug: b/270927174
Change-Id: Ib9fb2ac55004eccb4e7d62e0b36a4bfd2c464606
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfo.java b/src/main/java/com/android/tools/r8/graph/AppInfo.java
index d48cdca..de49aea 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfo.java
@@ -3,7 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.graph;
-import com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter;
+import com.android.tools.r8.DesugarGraphConsumer;
+import com.android.tools.r8.origin.GlobalSyntheticOrigin;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.MainDexInfo;
@@ -171,11 +172,43 @@
}
DexClass definition = definitionFor(type);
if (definition != null && !definition.isLibraryClass() && !dependent.isLibraryClass()) {
- InterfaceMethodRewriter.reportDependencyEdge(dependent, definition, this);
+ reportDependencyEdge(dependent, definition);
}
return definition;
}
+ public void reportDependencyEdge(DexClass dependent, DexClass dependency) {
+ assert !dependent.isLibraryClass();
+ assert !dependency.isLibraryClass();
+ DesugarGraphConsumer consumer = options().desugarGraphConsumer;
+ if (consumer == null) {
+ return;
+ }
+ Origin dependencyOrigin = dependency.getOrigin();
+ java.util.Collection<DexType> dependents =
+ getSyntheticItems().getSynthesizingContextTypes(dependent.getType());
+ if (dependents.isEmpty()) {
+ reportDependencyEdge(consumer, dependencyOrigin, dependent);
+ } else {
+ for (DexType type : dependents) {
+ reportDependencyEdge(consumer, dependencyOrigin, definitionFor(type));
+ }
+ }
+ }
+
+ private void reportDependencyEdge(
+ DesugarGraphConsumer consumer, Origin dependencyOrigin, DexClass clazz) {
+ Origin dependentOrigin = clazz.getOrigin();
+ if (dependencyOrigin == GlobalSyntheticOrigin.instance()
+ || dependentOrigin == GlobalSyntheticOrigin.instance()) {
+ // D8/R8 does not report edges to synthetic classes that D8/R8 generates.
+ return;
+ }
+ if (dependentOrigin != dependencyOrigin) {
+ consumer.accept(dependentOrigin, dependencyOrigin);
+ }
+ }
+
public DexProgramClass unsafeDirectProgramTypeLookup(DexType type) {
return app.programDefinitionFor(type);
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
index 54837a8..957ab7a 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
@@ -100,13 +100,24 @@
if (appView.options().enableTryWithResourcesDesugaring()) {
desugarings.add(new TwrInstructionDesugaring(appView));
}
+ recordRewriter = RecordDesugaring.create(appView);
+ if (recordRewriter != null) {
+ desugarings.add(recordRewriter);
+ }
+ StringConcatInstructionDesugaring stringConcatDesugaring =
+ new StringConcatInstructionDesugaring(appView);
+ desugarings.add(stringConcatDesugaring);
+ LambdaInstructionDesugaring lambdaDesugaring = new LambdaInstructionDesugaring(appView);
+ desugarings.add(lambdaDesugaring);
interfaceMethodRewriter =
InterfaceMethodRewriter.create(
appView,
SetUtils.newImmutableSetExcludingNullItems(
alwaysThrowingInstructionDesugaring,
backportedMethodRewriter,
- desugaredLibraryRetargeter));
+ desugaredLibraryRetargeter),
+ SetUtils.newImmutableSetExcludingNullItems(
+ lambdaDesugaring, stringConcatDesugaring, recordRewriter));
if (interfaceMethodRewriter != null) {
desugarings.add(interfaceMethodRewriter);
}
@@ -123,7 +134,6 @@
if (desugaredLibraryAPIConverter != null) {
desugarings.add(desugaredLibraryAPIConverter);
}
- desugarings.add(new LambdaInstructionDesugaring(appView));
desugarings.add(new ConstantDynamicInstructionDesugaring(appView));
desugarings.add(new InvokeSpecialToSelfDesugaring(appView));
if (appView.options().isGeneratingClassFiles()) {
@@ -131,7 +141,6 @@
assert nestBasedAccessDesugaring != null;
desugarings.add(new InvokeToPrivateRewriter());
}
- desugarings.add(new StringConcatInstructionDesugaring(appView));
desugarings.add(new BufferCovariantReturnTypeRewriter(appView));
if (backportedMethodRewriter.hasBackports()) {
desugarings.add(backportedMethodRewriter);
@@ -139,10 +148,6 @@
if (nestBasedAccessDesugaring != null) {
desugarings.add(nestBasedAccessDesugaring);
}
- this.recordRewriter = RecordDesugaring.create(appView);
- if (recordRewriter != null) {
- desugarings.add(recordRewriter);
- }
yieldingDesugarings.add(new UnrepresentableInDexInstructionRemover(appView));
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
index e91925d..8d69d58 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
@@ -9,10 +9,10 @@
import static com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper.InterfaceMethodDesugaringMode.NONE;
import static com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper.getInterfaceMethodDesugaringMode;
-import com.android.tools.r8.DesugarGraphConsumer;
import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.cf.code.CfInstruction;
import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.cf.code.CfInvokeDynamic;
import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.Unimplemented;
@@ -44,8 +44,6 @@
import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
import com.android.tools.r8.ir.desugar.icce.AlwaysThrowingInstructionDesugaring;
import com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper.InterfaceMethodDesugaringMode;
-import com.android.tools.r8.ir.desugar.lambda.LambdaInstructionDesugaring;
-import com.android.tools.r8.ir.desugar.stringconcat.StringConcatInstructionDesugaring;
import com.android.tools.r8.ir.synthetic.ForwardMethodBuilder;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.position.MethodPosition;
@@ -106,8 +104,9 @@
// Caches default interface method info for already processed interfaces.
private final Map<DexType, DefaultMethodsHelper.Collection> cache = new ConcurrentHashMap<>();
- // This is used to filter out double desugaring on backported methods.
- private final Set<CfInstructionDesugaring> precedingDesugarings;
+ // This is used to filter out double desugaring.
+ private final Set<CfInstructionDesugaring> precedingDesugaringsForInvoke;
+ private final Set<CfInstructionDesugaring> precedingDesugaringsForInvokeDynamic;
/** Defines a minor variation in desugaring. */
public enum Flavor {
@@ -118,21 +117,29 @@
}
public static InterfaceMethodRewriter create(
- AppView<?> appView, Set<CfInstructionDesugaring> precedingDesugarings) {
+ AppView<?> appView,
+ Set<CfInstructionDesugaring> precedingDesugaringsForInvoke,
+ Set<CfInstructionDesugaring> precedingDesugaringsForInvokeDynamic) {
InterfaceMethodDesugaringMode desugaringMode =
getInterfaceMethodDesugaringMode(appView.options());
if (desugaringMode == NONE) {
return null;
}
- return new InterfaceMethodRewriter(appView, precedingDesugarings, desugaringMode);
+ return new InterfaceMethodRewriter(
+ appView,
+ precedingDesugaringsForInvoke,
+ precedingDesugaringsForInvokeDynamic,
+ desugaringMode);
}
- public InterfaceMethodRewriter(
+ private InterfaceMethodRewriter(
AppView<?> appView,
- Set<CfInstructionDesugaring> precedingDesugarings,
+ Set<CfInstructionDesugaring> precedingDesugaringsForInvoke,
+ Set<CfInstructionDesugaring> precedingDesugaringsForInvokeDynamic,
InterfaceMethodDesugaringMode desugaringMode) {
this.appView = appView;
- this.precedingDesugarings = precedingDesugarings;
+ this.precedingDesugaringsForInvoke = precedingDesugaringsForInvoke;
+ this.precedingDesugaringsForInvokeDynamic = precedingDesugaringsForInvokeDynamic;
this.options = appView.options();
this.factory = appView.dexItemFactory();
assert desugaringMode == EMULATED_INTERFACE_ONLY || desugaringMode == ALL;
@@ -201,7 +208,13 @@
private boolean isAlreadyDesugared(CfInvoke invoke, ProgramMethod context) {
return Iterables.any(
- precedingDesugarings, desugaring -> desugaring.needsDesugaring(invoke, context));
+ precedingDesugaringsForInvoke, desugaring -> desugaring.needsDesugaring(invoke, context));
+ }
+
+ private boolean isAlreadyDesugared(CfInvokeDynamic invoke, ProgramMethod context) {
+ return Iterables.any(
+ precedingDesugaringsForInvokeDynamic,
+ desugaring -> desugaring.needsDesugaring(invoke, context));
}
@Override
@@ -222,9 +235,7 @@
CfCode code = context.getDefinition().getCode().asCfCode();
for (CfInstruction instruction : code.getInstructions()) {
if (instruction.isInvokeDynamic()
- && !LambdaInstructionDesugaring.isLambdaInvoke(instruction, context, appView)
- && !StringConcatInstructionDesugaring.isStringConcatInvoke(
- instruction, appView.dexItemFactory())) {
+ && !isAlreadyDesugared(instruction.asInvokeDynamic(), context)) {
reportInterfaceMethodHandleCallSite(instruction.asInvokeDynamic().getCallSite(), context);
}
computeDescription(instruction, context).scan();
@@ -927,7 +938,7 @@
// At this point we likely have a non-library type that may depend on default method information
// from its interfaces and the dependency should be reported.
if (implementing.isProgramClass() && !definedInterface.isLibraryClass()) {
- reportDependencyEdge(implementing.asProgramClass(), definedInterface, appView.appInfo());
+ appView.appInfo().reportDependencyEdge(implementing.asProgramClass(), definedInterface);
}
// Merge information from all superinterfaces.
@@ -961,31 +972,4 @@
MethodPosition position = new MethodPosition(method.asMethodReference());
options.warningMissingTypeForDesugar(origin, position, missing, method);
}
-
- public static void reportDependencyEdge(
- DexClass dependent, DexClass dependency, AppInfo appInfo) {
- assert !dependent.isLibraryClass();
- assert !dependency.isLibraryClass();
- DesugarGraphConsumer consumer = appInfo.app().options.desugarGraphConsumer;
- if (consumer != null) {
- Origin dependencyOrigin = dependency.getOrigin();
- java.util.Collection<DexType> dependents =
- appInfo.getSyntheticItems().getSynthesizingContextTypes(dependent.getType());
- if (dependents.isEmpty()) {
- reportDependencyEdge(consumer, dependencyOrigin, dependent);
- } else {
- for (DexType type : dependents) {
- reportDependencyEdge(consumer, dependencyOrigin, appInfo.definitionFor(type));
- }
- }
- }
- }
-
- private static void reportDependencyEdge(
- DesugarGraphConsumer consumer, Origin dependencyOrigin, DexClass clazz) {
- Origin dependentOrigin = clazz.getOrigin();
- if (dependentOrigin != dependencyOrigin) {
- consumer.accept(dependentOrigin, dependencyOrigin);
- }
- }
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/nest/D8NestBasedAccessDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/nest/D8NestBasedAccessDesugaring.java
index bde95b8..f3445c2 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/nest/D8NestBasedAccessDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/nest/D8NestBasedAccessDesugaring.java
@@ -4,8 +4,6 @@
package com.android.tools.r8.ir.desugar.nest;
-import static com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter.reportDependencyEdge;
-
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.ClasspathMethod;
import com.android.tools.r8.graph.DexClass;
@@ -45,8 +43,8 @@
DexClass hostClass = nest.getHostClass();
for (DexClass memberClass : nest.getMembers()) {
if (hostClass.isProgramClass() || memberClass.isProgramClass()) {
- reportDependencyEdge(hostClass, memberClass, appView.appInfo());
- reportDependencyEdge(memberClass, hostClass, appView.appInfo());
+ appView.appInfo().reportDependencyEdge(hostClass, memberClass);
+ appView.appInfo().reportDependencyEdge(memberClass, hostClass);
}
}
},
diff --git a/src/main/java/com/android/tools/r8/origin/GlobalSyntheticOrigin.java b/src/main/java/com/android/tools/r8/origin/GlobalSyntheticOrigin.java
new file mode 100644
index 0000000..cec0daf
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/origin/GlobalSyntheticOrigin.java
@@ -0,0 +1,23 @@
+// Copyright (c) 2023, 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.origin;
+
+public class GlobalSyntheticOrigin extends Origin {
+
+ private static final Origin INSTANCE = new GlobalSyntheticOrigin(Origin.root());
+
+ public static Origin instance() {
+ return INSTANCE;
+ }
+
+ protected GlobalSyntheticOrigin(Origin parent) {
+ super(parent);
+ }
+
+ @Override
+ public String part() {
+ return "<synthetic>";
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java b/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java
index 8c2a3bf..91d352f 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
import com.android.tools.r8.graph.ProgramDefinition;
+import com.android.tools.r8.origin.GlobalSyntheticOrigin;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.shaking.MainDexInfo;
import java.util.Comparator;
@@ -55,7 +56,7 @@
static SynthesizingContext fromType(DexType type) {
// This method should only be used for synthesizing from a non-program context!
// Thus we have no origin info and place the context in the "base" feature.
- return new SynthesizingContext(type, type, Origin.unknown(), FeatureSplit.BASE);
+ return new SynthesizingContext(type, type, GlobalSyntheticOrigin.instance(), FeatureSplit.BASE);
}
static SynthesizingContext fromNonSyntheticInputContext(
diff --git a/src/test/examplesJava17/records/RecordInterface.java b/src/test/examplesJava17/records/RecordInterface.java
new file mode 100644
index 0000000..8fc1e56
--- /dev/null
+++ b/src/test/examplesJava17/records/RecordInterface.java
@@ -0,0 +1,21 @@
+// Copyright (c) 2023, 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 records;
+
+public class RecordInterface {
+
+ interface Human {
+ default void printHuman() {
+ System.out.println("Human");
+ }
+ }
+
+ record Person(String name, int age) implements Human {}
+
+ public static void main(String[] args) {
+ Person janeDoe = new Person("Jane Doe", 42);
+ janeDoe.printHuman();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/graph/DesugarGraphTestConsumer.java b/src/test/java/com/android/tools/r8/desugar/graph/DesugarGraphTestConsumer.java
index d80d81e..541bfea 100644
--- a/src/test/java/com/android/tools/r8/desugar/graph/DesugarGraphTestConsumer.java
+++ b/src/test/java/com/android/tools/r8/desugar/graph/DesugarGraphTestConsumer.java
@@ -4,9 +4,11 @@
package com.android.tools.r8.desugar.graph;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.DesugarGraphConsumer;
+import com.android.tools.r8.origin.GlobalSyntheticOrigin;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.StringUtils.BraceType;
@@ -96,6 +98,12 @@
@Override
public synchronized void accept(Origin dependent, Origin dependency) {
+ // D8/R8 should not report edges synthetic origin.
+ assertNotEquals(dependent, GlobalSyntheticOrigin.instance());
+ assertNotEquals(dependency, GlobalSyntheticOrigin.instance());
+ // D8/R8 may report edges to unknown origin, but that is typically *not* what should be done.
+ assertNotEquals(dependency, Origin.unknown());
+ assertNotEquals(dependent, Origin.unknown());
assertFalse(finished);
dependents.computeIfAbsent(dependency, s -> new HashSet<>()).add(dependent);
}
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordInterfaceTest.java b/src/test/java/com/android/tools/r8/desugar/records/RecordInterfaceTest.java
new file mode 100644
index 0000000..5efd3b2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/records/RecordInterfaceTest.java
@@ -0,0 +1,165 @@
+// Copyright (c) 2023, 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.desugar.records;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.GlobalSyntheticsConsumer;
+import com.android.tools.r8.R8FullTestBuilder;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.desugar.graph.DesugarGraphTestConsumer;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.origin.PathOrigin;
+import com.android.tools.r8.synthesis.globals.GlobalSyntheticsTestingConsumer;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class RecordInterfaceTest extends TestBase {
+
+ private static final String RECORD_NAME = "RecordInterface";
+ private static final byte[][] PROGRAM_DATA = RecordTestUtils.getProgramData(RECORD_NAME);
+ private static final String MAIN_TYPE = RecordTestUtils.getMainType(RECORD_NAME);
+ private static final String EXPECTED_RESULT = StringUtils.lines("Human");
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
+ }
+
+ private boolean isCfRuntimeWithNativeRecordSupport() {
+ return parameters.isCfRuntime()
+ && parameters.asCfRuntime().isNewerThanOrEqual(CfVm.JDK14)
+ && parameters.getApiLevel().equals(AndroidApiLevel.B);
+ }
+
+ @Test
+ public void testReference() throws Exception {
+ assumeTrue(isCfRuntimeWithNativeRecordSupport());
+ testForJvm(parameters)
+ .addProgramClassFileData(PROGRAM_DATA)
+ .run(parameters.getRuntime(), MAIN_TYPE)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ testForD8(parameters.getBackend())
+ .addProgramClassFileData(PROGRAM_DATA)
+ .setMinApi(parameters)
+ .run(parameters.getRuntime(), MAIN_TYPE)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ }
+
+ @Test
+ public void testD8Intermediate() throws Exception {
+ assumeTrue(parameters.isDexRuntime());
+ DesugarGraphTestConsumer consumer = new DesugarGraphTestConsumer();
+ GlobalSyntheticsTestingConsumer globals = new GlobalSyntheticsTestingConsumer();
+ Path path = compileIntermediate(globals);
+ testForD8()
+ .addProgramFiles(path)
+ .apply(
+ b ->
+ b.getBuilder()
+ .addGlobalSyntheticsResourceProviders(globals.getIndexedModeProvider()))
+ .apply(b -> b.getBuilder().setDesugarGraphConsumer(consumer))
+ .setMinApi(parameters)
+ .setIncludeClassesChecksum(true)
+ .compile()
+ .assertNoMessages()
+ .run(parameters.getRuntime(), MAIN_TYPE)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ assertNoEdgeToRecord(consumer);
+ }
+
+ @Test
+ public void testD8IntermediateNoDesugaringInStep2() throws Exception {
+ assumeTrue(parameters.isDexRuntime());
+ DesugarGraphTestConsumer consumer = new DesugarGraphTestConsumer();
+ GlobalSyntheticsTestingConsumer globals = new GlobalSyntheticsTestingConsumer();
+ Path path = compileIntermediate(globals);
+ testForD8()
+ .addProgramFiles(path)
+ .apply(
+ b ->
+ b.getBuilder()
+ .addGlobalSyntheticsResourceProviders(globals.getIndexedModeProvider()))
+ .apply(b -> b.getBuilder().setDesugarGraphConsumer(consumer))
+ .setMinApi(parameters)
+ .setIncludeClassesChecksum(true)
+ // In Android Studio they disable desugaring at this point to improve build speed.
+ .disableDesugaring()
+ .compile()
+ .assertNoMessages()
+ .run(parameters.getRuntime(), MAIN_TYPE)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ assertNoEdgeToRecord(consumer);
+ }
+
+ private Path compileIntermediate(GlobalSyntheticsConsumer globalSyntheticsConsumer)
+ throws Exception {
+ Origin fake = new PathOrigin(Paths.get("origin"));
+ DesugarGraphTestConsumer consumer = new DesugarGraphTestConsumer();
+ Path intermediate =
+ testForD8(Backend.DEX)
+ .apply(
+ b -> {
+ // We avoid unknown origin here since they are not allowed when using a Graph
+ // consumer.
+ for (byte[] programDatum : PROGRAM_DATA) {
+ b.getBuilder().addClassProgramData(programDatum, fake);
+ }
+ })
+ .setMinApi(parameters)
+ .setIntermediate(true)
+ .setIncludeClassesChecksum(true)
+ .apply(b -> b.getBuilder().setGlobalSyntheticsConsumer(globalSyntheticsConsumer))
+ .apply(b -> b.getBuilder().setDesugarGraphConsumer(consumer))
+ .compile()
+ .assertNoMessages()
+ .writeToZip();
+ assertNoEdgeToRecord(consumer);
+ return intermediate;
+ }
+
+ private void assertNoEdgeToRecord(DesugarGraphTestConsumer consumer) {
+ assertEquals(0, consumer.totalEdgeCount());
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ assumeTrue(parameters.isDexRuntime() || isCfRuntimeWithNativeRecordSupport());
+ R8FullTestBuilder builder =
+ testForR8(parameters.getBackend())
+ .addProgramClassFileData(PROGRAM_DATA)
+ .setMinApi(parameters)
+ .addKeepMainRule(MAIN_TYPE);
+ if (parameters.isCfRuntime()) {
+ builder
+ .addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp))
+ .compile()
+ .inspect(RecordTestUtils::assertRecordsAreRecords)
+ .run(parameters.getRuntime(), MAIN_TYPE)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ return;
+ }
+ builder.run(parameters.getRuntime(), MAIN_TYPE).assertSuccessWithOutput(EXPECTED_RESULT);
+ }
+}