Update global synthetics generator to support split CF/DEX desugaring
This change is not binary compatible with the previous generator tool.
It is not yet in use anywhere so that should not be an issue.
Bug: b/332866206
Change-Id: I2cbb596cd2c18b3920d8b8a45a105217f31242ed
diff --git a/src/main/java/com/android/tools/r8/GlobalSyntheticsConsumer.java b/src/main/java/com/android/tools/r8/GlobalSyntheticsConsumer.java
index 88e9f55..28125f6 100644
--- a/src/main/java/com/android/tools/r8/GlobalSyntheticsConsumer.java
+++ b/src/main/java/com/android/tools/r8/GlobalSyntheticsConsumer.java
@@ -31,7 +31,7 @@
* <p>The context class is the class for which the global synthetic data is needed. If compiling
* in DexIndexed mode, the context class will be null.
*
- * <p>The accept method will be called at most once for a given context class (any only once at
+ * <p>The accept method will be called at most once for a given context class (and only once at
* all for a DexIndexed mode compilation). The global data for that class may be the same as for
* other context classes, but it will be provided for each context.
*
diff --git a/src/main/java/com/android/tools/r8/GlobalSyntheticsGenerator.java b/src/main/java/com/android/tools/r8/GlobalSyntheticsGenerator.java
index c9590ea..ccfe752 100644
--- a/src/main/java/com/android/tools/r8/GlobalSyntheticsGenerator.java
+++ b/src/main/java/com/android/tools/r8/GlobalSyntheticsGenerator.java
@@ -34,11 +34,12 @@
import com.android.tools.r8.graph.ProgramDefinition;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.ThrowExceptionCode;
-import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.ir.conversion.PrimaryD8L8IRConverter;
import com.android.tools.r8.ir.desugar.TypeRewriter;
import com.android.tools.r8.ir.desugar.records.RecordDesugaring;
import com.android.tools.r8.ir.desugar.varhandle.VarHandleDesugaring;
import com.android.tools.r8.ir.desugar.varhandle.VarHandleDesugaringEventConsumer;
+import com.android.tools.r8.jar.CfApplicationWriter;
import com.android.tools.r8.keepanno.annotations.KeepForApi;
import com.android.tools.r8.naming.RecordRewritingNamingLens;
import com.android.tools.r8.naming.VarHandleDesugaringRewritingNamingLens;
@@ -72,15 +73,14 @@
@KeepForApi
public class GlobalSyntheticsGenerator {
- @SuppressWarnings("ReferenceEquality")
private static boolean ensureAllGlobalSyntheticsModeled(SyntheticNaming naming) {
for (SyntheticKind kind : naming.kinds()) {
assert !kind.isGlobal()
|| !kind.isMayOverridesNonProgramType()
- || kind == naming.RECORD_TAG
- || kind == naming.API_MODEL_STUB
- || kind == naming.METHOD_HANDLES_LOOKUP
- || kind == naming.VAR_HANDLE;
+ || kind.equals(naming.RECORD_TAG)
+ || kind.equals(naming.API_MODEL_STUB)
+ || kind.equals(naming.METHOD_HANDLES_LOOKUP)
+ || kind.equals(naming.VAR_HANDLE);
}
return true;
}
@@ -129,8 +129,13 @@
timing.end();
assert GlobalSyntheticsGeneratorVerifier.verifyExpectedClassesArePresent(appView);
-
- ApplicationWriter.create(appView, options.getMarker()).write(executorService, app);
+ if (options.isGeneratingDex()) {
+ ApplicationWriter.create(appView, options.getMarker()).write(executorService, app);
+ } else {
+ assert options.isGeneratingClassFiles();
+ new CfApplicationWriter(appView, options.getMarker())
+ .write(options.getClassFileConsumer(), app);
+ }
} catch (ExecutionException e) {
throw unwrapExecutionException(e);
} catch (IOException e) {
@@ -202,11 +207,18 @@
VarHandleDesugaring.ensureMethodHandlesLookupClass(
appView, varHandleEventConsumer, synthesizingContext);
- IRConverter converter = new IRConverter(appView);
- converter.processSimpleSynthesizeMethods(methodsToProcess, executorService);
+ // Commit all the synthetics to the program and then convert as per D8.
+ // We must run proper D8 conversion as the global synthetics may give rise to additional
+ // synthetics as part of their implementation.
+ assert appView.getSyntheticItems().hasPendingSyntheticClasses();
+ appView.setAppInfo(
+ new AppInfo(
+ appView.appInfo().getSyntheticItems().commit(appView.app()),
+ appView.appInfo().getMainDexInfo()));
+
+ new PrimaryD8L8IRConverter(appView, Timing.empty()).convert(appView, executorService);
appView
- .withoutClassHierarchy()
.setAppInfo(
new AppInfo(
appView.appInfo().getSyntheticItems().commit(appView.app()),
@@ -221,11 +233,12 @@
VarHandleDesugaringRewritingNamingLens.createVarHandleDesugaringRewritingNamingLens(
appView));
- // Add global synthetic classes for api stubs.
- createAllApiStubs(appView, synthesizingContext, executorService);
+ if (appView.options().isGeneratingDex()) {
+ // Add global synthetic classes for api stubs.
+ createAllApiStubs(appView, synthesizingContext, executorService);
+ }
appView
- .withoutClassHierarchy()
.setAppInfo(
new AppInfo(
appView.appInfo().getSyntheticItems().commit(appView.app()),
diff --git a/src/main/java/com/android/tools/r8/GlobalSyntheticsGeneratorCommand.java b/src/main/java/com/android/tools/r8/GlobalSyntheticsGeneratorCommand.java
index 44f54ba..5cc177b 100644
--- a/src/main/java/com/android/tools/r8/GlobalSyntheticsGeneratorCommand.java
+++ b/src/main/java/com/android/tools/r8/GlobalSyntheticsGeneratorCommand.java
@@ -7,21 +7,26 @@
import com.android.tools.r8.dex.Marker.Tool;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.DexFileOverflowDiagnostic;
+import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.keepanno.annotations.KeepForApi;
import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.utils.AbortException;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.ExceptionDiagnostic;
-import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.InternalOptions.DesugarState;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.StringDiagnostic;
+import java.io.IOException;
+import java.nio.file.Files;
import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Set;
/**
* Immutable command structure for an invocation of the {@link GlobalSyntheticsGenerator} compiler.
@@ -29,9 +34,10 @@
@KeepForApi
public final class GlobalSyntheticsGeneratorCommand {
- private final ProgramConsumer programConsumer;
+ private final GlobalSyntheticsConsumer globalsConsumer;
private final Reporter reporter;
private final int minApiLevel;
+ private final boolean classfileDesugaringOnly;
private final boolean printHelp;
private final boolean printVersion;
@@ -41,10 +47,15 @@
private final DexItemFactory factory = new DexItemFactory();
private GlobalSyntheticsGeneratorCommand(
- AndroidApp inputApp, ProgramConsumer programConsumer, Reporter reporter, int minApiLevel) {
+ AndroidApp inputApp,
+ GlobalSyntheticsConsumer globalsConsumer,
+ Reporter reporter,
+ int minApiLevel,
+ boolean classfileDesugaringOnly) {
this.inputApp = inputApp;
- this.programConsumer = programConsumer;
+ this.globalsConsumer = globalsConsumer;
this.minApiLevel = minApiLevel;
+ this.classfileDesugaringOnly = classfileDesugaringOnly;
this.reporter = reporter;
this.printHelp = false;
this.printVersion = false;
@@ -55,8 +66,9 @@
this.printVersion = printVersion;
this.inputApp = null;
- this.programConsumer = null;
+ this.globalsConsumer = null;
this.minApiLevel = AndroidApiLevel.B.getLevel();
+ this.classfileDesugaringOnly = false;
reporter = new Reporter();
}
@@ -130,9 +142,11 @@
assert !internal.debug;
assert !internal.minimalMainDex;
internal.setMinApiLevel(AndroidApiLevel.getAndroidApiLevel(minApiLevel));
- assert !internal.intermediate;
assert internal.retainCompileTimeAnnotations;
- internal.programConsumer = programConsumer;
+ internal.intermediate = true;
+ internal.programConsumer =
+ classfileDesugaringOnly ? new ThrowingCfConsumer() : new ThrowingDexConsumer();
+ internal.setGlobalSyntheticsConsumer(globalsConsumer);
// Assert and fixup defaults.
assert !internal.isShrinking();
@@ -148,6 +162,33 @@
return internal;
}
+ private static class ThrowingCfConsumer implements ClassFileConsumer {
+
+ @Override
+ public void accept(ByteDataView data, String descriptor, DiagnosticsHandler handler) {
+ throw new Unreachable("Unexpected attempt to write a non-global artifact");
+ }
+
+ @Override
+ public void finished(DiagnosticsHandler handler) {
+ // Nothing to do.
+ }
+ }
+
+ private static class ThrowingDexConsumer implements DexIndexedConsumer {
+
+ @Override
+ public void accept(
+ int fileIndex, ByteDataView data, Set<String> descriptors, DiagnosticsHandler handler) {
+ throw new Unreachable("Unexpected attempt to write a non-global artifact");
+ }
+
+ @Override
+ public void finished(DiagnosticsHandler handler) {
+ // Nothing to do.
+ }
+ }
+
/**
* Builder for constructing a GlobalSyntheticsGeneratorCommand.
*
@@ -156,9 +197,10 @@
@KeepForApi
public static class Builder {
- private ProgramConsumer programConsumer = null;
+ private GlobalSyntheticsConsumer globalsConsumer = null;
private final Reporter reporter;
private int minApiLevel = AndroidApiLevel.B.getLevel();
+ private boolean classfileDesugaringOnly = false;
private boolean printHelp = false;
private boolean printVersion = false;
private final AndroidApp.Builder appBuilder = AndroidApp.builder();
@@ -177,6 +219,11 @@
return this;
}
+ public Builder setClassfileDesugaringOnly(boolean value) {
+ this.classfileDesugaringOnly = value;
+ return this;
+ }
+
/** Set the value of the print-help flag. */
public Builder setPrintHelp(boolean printHelp) {
this.printHelp = printHelp;
@@ -210,17 +257,32 @@
return this;
}
- /** Set an output path to consume the resulting program. */
- public Builder setProgramConsumerOutput(Path path) {
- return setProgramConsumer(
- FileUtils.isArchive(path)
- ? new DexIndexedConsumer.ArchiveConsumer(path, false)
- : new DexIndexedConsumer.DirectoryConsumer(path, false));
+ /** Set a destination to write the resulting global synthetics output file. */
+ public Builder setGlobalSyntheticsOutput(Path path) {
+ return setGlobalSyntheticsConsumer(
+ new GlobalSyntheticsConsumer() {
+
+ private boolean written = false;
+
+ @Override
+ public synchronized void accept(
+ ByteDataView data, ClassReference context, DiagnosticsHandler handler) {
+ if (written) {
+ throw new Unreachable("Unexpected attempt to repeatedly write global synthetics");
+ }
+ written = true;
+ try {
+ Files.write(path, data.copyByteData(), StandardOpenOption.TRUNCATE_EXISTING);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ });
}
- /** Set a consumer for obtaining the resulting program. */
- public Builder setProgramConsumer(ProgramConsumer programConsumer) {
- this.programConsumer = programConsumer;
+ /** Set a consumer for obtaining the resulting global synthetics output. */
+ public Builder setGlobalSyntheticsConsumer(GlobalSyntheticsConsumer globalsConsumer) {
+ this.globalsConsumer = globalsConsumer;
return this;
}
@@ -230,7 +292,7 @@
return new GlobalSyntheticsGeneratorCommand(printHelp, printVersion);
}
return new GlobalSyntheticsGeneratorCommand(
- appBuilder.build(), programConsumer, reporter, minApiLevel);
+ appBuilder.build(), globalsConsumer, reporter, minApiLevel, classfileDesugaringOnly);
}
private boolean isPrintHelpOrPrintVersion() {
@@ -241,9 +303,8 @@
if (isPrintHelpOrPrintVersion()) {
return;
}
- if (!(programConsumer instanceof DexIndexedConsumer)) {
- reporter.error(
- "GlobalSyntheticsGenerator does not support compiling to dex per class or class files");
+ if (globalsConsumer == null) {
+ reporter.error("GlobalSyntheticsGenerator does not support compiling without output");
}
}
diff --git a/src/main/java/com/android/tools/r8/GlobalSyntheticsGeneratorCommandParser.java b/src/main/java/com/android/tools/r8/GlobalSyntheticsGeneratorCommandParser.java
index 9022e28..de61833 100644
--- a/src/main/java/com/android/tools/r8/GlobalSyntheticsGeneratorCommandParser.java
+++ b/src/main/java/com/android/tools/r8/GlobalSyntheticsGeneratorCommandParser.java
@@ -21,6 +21,7 @@
private static final String LOWER_CASE_NAME = "globalsyntheticsgenerator";
private static final String MIN_API_FLAG = "--min-api";
+ private static final String CLASSFILE_DESUGARING_MODE = "--classfile";
private static final String USAGE_MESSAGE =
StringUtils.lines("Usage: " + LOWER_CASE_NAME + " [options] " + "where options are:");
@@ -29,7 +30,14 @@
return ImmutableList.<ParseFlagInfo>builder()
.add(ParseFlagInfoImpl.getMinApi())
.add(ParseFlagInfoImpl.getLib())
- .add(ParseFlagInfoImpl.flag1("--output", "<dex-file>", "Output result in <dex-file>."))
+ .add(
+ ParseFlagInfoImpl.flag1(
+ "--output", "<globals-file>", "Output result in <globals-file>."))
+ .add(
+ ParseFlagInfoImpl.flag0(
+ "--classfile",
+ "Generate globals for only classfile to classfile desugaring.",
+ "(By default globals for both classfile and dex desugaring are generated)."))
.add(ParseFlagInfoImpl.getVersion(LOWER_CASE_NAME))
.add(ParseFlagInfoImpl.getHelp())
.build();
@@ -98,6 +106,8 @@
}
} else if (arg.equals("--lib")) {
builder.addLibraryFiles(Paths.get(nextArg));
+ } else if (arg.equals(CLASSFILE_DESUGARING_MODE)) {
+ builder.setClassfileDesugaringOnly(true);
} else if (arg.startsWith("--")) {
builder.error(new StringDiagnostic("Unknown option: " + arg, origin));
}
@@ -105,6 +115,6 @@
if (outputPath == null) {
outputPath = Paths.get(".");
}
- return builder.setProgramConsumerOutput(outputPath);
+ return builder.setGlobalSyntheticsOutput(outputPath);
}
}
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
index 6694cb0..4fdbb35 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -89,6 +89,7 @@
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
+import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@@ -216,7 +217,12 @@
Collection<DexProgramClass> allClasses = classes;
classes = new ArrayList<>(allClasses.size());
for (DexProgramClass clazz : allClasses) {
- if (appView.getSyntheticItems().isGlobalSyntheticClass(clazz)) {
+ if (appView.getSyntheticItems().isGlobalSyntheticClassTransitive(clazz)) {
+ Consumer<DexProgramClass> globalSyntheticCreatedCallback =
+ appView.options().testing.globalSyntheticCreatedCallback;
+ if (globalSyntheticCreatedCallback != null) {
+ globalSyntheticCreatedCallback.accept(clazz);
+ }
globalSynthetics.add(clazz);
} else {
classes.add(clazz);
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 9d6a8b6..82f47fe 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
@@ -65,7 +65,6 @@
import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfoCollector;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackDelayed;
-import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackIgnore;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoCollection;
import com.android.tools.r8.ir.optimize.membervaluepropagation.D8MemberValuePropagation;
@@ -330,22 +329,6 @@
return onWaveDoneActions != null;
}
- public void processSimpleSynthesizeMethods(
- List<ProgramMethod> methods, ExecutorService executorService) throws ExecutionException {
- ThreadUtils.processItems(
- methods,
- this::processAndFinalizeSimpleSynthesizedMethod,
- options.getThreadingModule(),
- executorService);
- }
-
- private void processAndFinalizeSimpleSynthesizedMethod(ProgramMethod method) {
- IRCode code = method.buildIR(appView);
- assert code != null;
- new MoveResultRewriter(appView).run(code, Timing.empty());
- removeDeadCodeAndFinalizeIR(code, OptimizationFeedbackIgnore.getInstance(), Timing.empty());
- }
-
/**
* This will replace the Dex code in the method with the Dex code generated from the provided IR.
*
diff --git a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
index ee0ce51..b5b5299 100644
--- a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
@@ -65,6 +65,7 @@
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
+import java.util.function.Consumer;
import java.util.function.Predicate;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassReader;
@@ -161,8 +162,13 @@
Collection<DexProgramClass> allClasses = classes;
classes = new ArrayList<>(allClasses.size());
for (DexProgramClass clazz : allClasses) {
- if (appView.getSyntheticItems().isGlobalSyntheticClass(clazz)) {
+ if (appView.getSyntheticItems().isGlobalSyntheticClassTransitive(clazz)) {
globalSyntheticClasses.add(clazz);
+ Consumer<DexProgramClass> globalSyntheticCreatedCallback =
+ appView.options().testing.globalSyntheticCreatedCallback;
+ if (globalSyntheticCreatedCallback != null) {
+ globalSyntheticCreatedCallback.accept(clazz);
+ }
} else {
classes.add(clazz);
}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
index 61fd940..7f56b25 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -427,6 +427,26 @@
return isGlobalSyntheticClass(clazz.getType());
}
+ public boolean isGlobalSyntheticClassTransitive(DexProgramClass clazz) {
+ // Fast path the common case where the class is not synthetic at all.
+ if (!isSynthetic(clazz)) {
+ return false;
+ }
+ if (isGlobalSyntheticClass(clazz)) {
+ return true;
+ }
+ DexType type = clazz.getType();
+ for (SyntheticReference<?, ?, ?> reference : committed.getItems(type)) {
+ // Only a single context should exist for a globally derived synthetic, so return early.
+ return isGlobalSyntheticClass(reference.getContext().getSynthesizingContextType());
+ }
+ SyntheticDefinition<?, ?, ?> definition = pending.definitions.get(type);
+ if (definition != null) {
+ return isGlobalSyntheticClass(definition.getContext().getSynthesizingContextType());
+ }
+ return false;
+ }
+
private static boolean isGlobalReferences(List<SyntheticProgramClassReference> references) {
if (references == null) {
return false;
@@ -1017,6 +1037,8 @@
Consumer<DexProgramClass> globalSyntheticCreatedCallback =
appView.options().testing.globalSyntheticCreatedCallback;
if (globalSyntheticCreatedCallback != null) {
+ // These are also reported in the writer to ensure transitive classes are reported too.
+ // However, we keep the test reporting here too to fail fast on direct globals.
globalSyntheticCreatedCallback.accept(globalSynthetic);
}
addGlobalContexts(globalSynthetic.getType(), contexts);
@@ -1046,6 +1068,7 @@
SyntheticKind kind = kindSelector.select(naming);
DexType type =
SyntheticNaming.createInternalType(kind, outerContext, syntheticIdSupplier.get(), appView);
+
SyntheticProgramClassBuilder classBuilder =
new SyntheticProgramClassBuilder(type, kind, outerContext, appView.dexItemFactory());
DexProgramClass clazz =
diff --git a/src/main/java/com/android/tools/r8/utils/InternalGlobalSyntheticsProgramConsumer.java b/src/main/java/com/android/tools/r8/utils/InternalGlobalSyntheticsProgramConsumer.java
index 1428459..4fd254a 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalGlobalSyntheticsProgramConsumer.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalGlobalSyntheticsProgramConsumer.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.ProgramConsumer;
import com.android.tools.r8.ProgramResource.Kind;
import com.android.tools.r8.Version;
+import com.android.tools.r8.dex.Marker.Tool;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexType;
@@ -203,12 +204,28 @@
public void finished(AppView<?> appView) {
Map<DexType, Set<DexType>> globalsToContexts =
appView.getSyntheticItems().getFinalGlobalSyntheticContexts(appView);
+ // The global synthetics generator is generating the world of globals, thus no contexts exist.
+ if (appView.options().tool.equals(Tool.GlobalSyntheticsGenerator)) {
+ assert globalsToContexts.isEmpty();
+ GlobalsFileBuilder builder = new GlobalsFileBuilder(getKind());
+ globalToBytes.forEach(
+ (globalType, globalBytes) -> {
+ builder.addGlobalSynthetic(globalType.toDescriptorString(), globalBytes);
+ });
+ try {
+ clientConsumer.accept(ByteDataView.of(builder.build()), null, appView.reporter());
+ } catch (IOException e) {
+ appView.reporter().error(new ExceptionDiagnostic(e));
+ }
+ clientConsumer.finished(appView.reporter());
+ return;
+ }
+ // Otherwise, there must be at least one context for any global.
Map<DexType, Set<DexType>> contextToGlobals = new IdentityHashMap<>();
for (DexType globalType : globalToBytes.keySet()) {
// It would be good to assert that the global is a synthetic type, but the naming-lens
// is not applied to SyntheticItems in AppView.
Set<DexType> contexts = globalsToContexts.get(globalType);
- // TODO(b/231598779): Contexts should never be null once fixed for records.
assert contexts != null;
assert !contexts.isEmpty();
for (DexType contextType : contexts) {
diff --git a/src/test/java/com/android/tools/r8/compilerapi/globalsyntheticsgenerator/GlobalSyntheticsGeneratorTest.java b/src/test/java/com/android/tools/r8/compilerapi/globalsyntheticsgenerator/GlobalSyntheticsGeneratorTest.java
index 6cab4d7..c3e49a5 100644
--- a/src/test/java/com/android/tools/r8/compilerapi/globalsyntheticsgenerator/GlobalSyntheticsGeneratorTest.java
+++ b/src/test/java/com/android/tools/r8/compilerapi/globalsyntheticsgenerator/GlobalSyntheticsGeneratorTest.java
@@ -3,13 +3,19 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.compilerapi.globalsyntheticsgenerator;
-import com.android.tools.r8.DexIndexedConsumer;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.ByteDataView;
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.GlobalSyntheticsConsumer;
import com.android.tools.r8.GlobalSyntheticsGenerator;
import com.android.tools.r8.GlobalSyntheticsGeneratorCommand;
-import com.android.tools.r8.ProgramConsumer;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.compilerapi.CompilerApiTest;
import com.android.tools.r8.compilerapi.CompilerApiTestRunner;
+import com.android.tools.r8.references.ClassReference;
import org.junit.Test;
public class GlobalSyntheticsGeneratorTest extends CompilerApiTestRunner {
@@ -27,8 +33,23 @@
public void testGlobalSynthetics() throws Exception {
new ApiTest(ApiTest.PARAMETERS)
.run(
- new DexIndexedConsumer.ArchiveConsumer(
- temp.newFolder().toPath().resolve("output.zip")));
+ new GlobalSyntheticsConsumer() {
+ boolean hasOutput = false;
+
+ @Override
+ public void accept(
+ ByteDataView data, ClassReference context, DiagnosticsHandler handler) {
+ assertNull(context);
+ assertFalse(hasOutput);
+ assertTrue(data.getLength() > 0);
+ hasOutput = true;
+ }
+
+ @Override
+ public void finished(DiagnosticsHandler handler) {
+ assertTrue(hasOutput);
+ }
+ });
}
public static class ApiTest extends CompilerApiTest {
@@ -37,18 +58,25 @@
super(parameters);
}
- public void run(ProgramConsumer programConsumer) throws Exception {
+ public void run(GlobalSyntheticsConsumer consumer) throws Exception {
GlobalSyntheticsGenerator.run(
GlobalSyntheticsGeneratorCommand.builder()
.addLibraryFiles(getAndroidJar())
.setMinApiLevel(33)
- .setProgramConsumer(programConsumer)
+ .setGlobalSyntheticsConsumer(consumer)
.build());
}
@Test
public void testGlobalSynthetics() throws Exception {
- run(DexIndexedConsumer.emptyConsumer());
+ run(
+ new GlobalSyntheticsConsumer() {
+ @Override
+ public void accept(
+ ByteDataView data, ClassReference context, DiagnosticsHandler handler) {
+ // Ignoring output in API test.
+ }
+ });
}
}
}
diff --git a/src/test/java/com/android/tools/r8/globalsynthetics/GlobalSyntheticsEnsureClassesOutputTest.java b/src/test/java/com/android/tools/r8/globalsynthetics/GlobalSyntheticsEnsureClassesOutputTest.java
index ff2afbe..9ec7c0f 100644
--- a/src/test/java/com/android/tools/r8/globalsynthetics/GlobalSyntheticsEnsureClassesOutputTest.java
+++ b/src/test/java/com/android/tools/r8/globalsynthetics/GlobalSyntheticsEnsureClassesOutputTest.java
@@ -5,22 +5,19 @@
package com.android.tools.r8.globalsynthetics;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
-import com.android.tools.r8.DexIndexedConsumer;
import com.android.tools.r8.GlobalSyntheticsGenerator;
import com.android.tools.r8.GlobalSyntheticsGeneratorCommand;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.synthesis.globals.GlobalSyntheticsTestingConsumer;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.SetUtils;
-import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
-import java.nio.file.Path;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.junit.Test;
@@ -31,91 +28,109 @@
@RunWith(Parameterized.class)
public class GlobalSyntheticsEnsureClassesOutputTest extends TestBase {
- @Parameters(name = "{0}")
- public static TestParametersCollection data() {
- return getTestParameters().withNoneRuntime().build();
+ private final Backend backend;
+
+ @Parameters(name = "{0}, backend:{1}")
+ public static List<Object[]> data() {
+ return buildParameters(getTestParameters().withNoneRuntime().build(), Backend.values());
}
- public GlobalSyntheticsEnsureClassesOutputTest(TestParameters parameters) {
+ public GlobalSyntheticsEnsureClassesOutputTest(TestParameters parameters, Backend backend) {
parameters.assertNoneRuntime();
+ this.backend = backend;
}
@Test
public void testNumberOfClassesOnK() throws Exception {
- Path output = temp.newFolder().toPath().resolve("output.zip");
+ GlobalSyntheticsTestingConsumer globalsConsumer = new GlobalSyntheticsTestingConsumer();
GlobalSyntheticsGenerator.run(
GlobalSyntheticsGeneratorCommand.builder()
.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.API_DATABASE_LEVEL))
.setMinApiLevel(AndroidApiLevel.K.getLevel())
- .setProgramConsumer(new DexIndexedConsumer.ArchiveConsumer(output))
+ .setGlobalSyntheticsConsumer(globalsConsumer)
+ .setClassfileDesugaringOnly(backend.isCf())
.build());
- CodeInspector inspector = new CodeInspector(output);
- assertEquals(1044, inspector.allClasses().size());
+ assertTrue(globalsConsumer.isSingleGlobal());
+ testForD8(backend)
+ .apply(
+ b ->
+ b.getBuilder().addGlobalSyntheticsResourceProviders(globalsConsumer.getProviders()))
+ .setMinApi(AndroidApiLevel.K)
+ .compile()
+ .inspect(
+ inspector -> assertEquals(backend.isDex() ? 1045 : 4, inspector.allClasses().size()));
}
@Test
public void testNumberOfClassesOnLatest() throws Exception {
- Path output = temp.newFolder().toPath().resolve("output.zip");
+ GlobalSyntheticsTestingConsumer globalsConsumer = new GlobalSyntheticsTestingConsumer();
GlobalSyntheticsGenerator.run(
GlobalSyntheticsGeneratorCommand.builder()
.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.API_DATABASE_LEVEL))
.setMinApiLevel(AndroidApiLevel.LATEST.getLevel())
- .setProgramConsumer(new DexIndexedConsumer.ArchiveConsumer(output))
+ .setGlobalSyntheticsConsumer(globalsConsumer)
+ .setClassfileDesugaringOnly(backend.isCf())
.build());
+ assertTrue(globalsConsumer.isSingleGlobal());
Set<String> expectedInOutput = new HashSet<>();
// The output contains a RecordTag type that is mapped back to the original java.lang.Record by
// our codeinspector.
expectedInOutput.add("Ljava/lang/Record;");
expectedInOutput.add("Ljava/lang/invoke/VarHandle;");
expectedInOutput.add("Ljava/lang/invoke/MethodHandles$Lookup;");
- assertEquals(
- expectedInOutput,
- new CodeInspector(output)
- .allClasses().stream()
- .map(FoundClassSubject::getFinalDescriptor)
- .collect(Collectors.toSet()));
+ testForD8(backend)
+ .apply(
+ b ->
+ b.getBuilder().addGlobalSyntheticsResourceProviders(globalsConsumer.getProviders()))
+ .setMinApi(AndroidApiLevel.LATEST)
+ .compile()
+ .inspect(
+ inspector ->
+ assertEquals(
+ expectedInOutput,
+ inspector.allClasses().stream()
+ .map(FoundClassSubject::getFinalDescriptor)
+ .collect(Collectors.toSet())));
}
@Test
public void testClassFileListOutput() throws Exception {
Set<String> generatedGlobalSynthetics = SetUtils.newConcurrentHashSet();
- Path output = temp.newFolder().toPath().resolve("output.zip");
+ GlobalSyntheticsTestingConsumer globalsConsumer = new GlobalSyntheticsTestingConsumer();
runGlobalSyntheticsGenerator(
GlobalSyntheticsGeneratorCommand.builder()
.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.API_DATABASE_LEVEL))
.setMinApiLevel(AndroidApiLevel.K.getLevel())
- .setProgramConsumer(new DexIndexedConsumer.ArchiveConsumer(output))
+ .setGlobalSyntheticsConsumer(globalsConsumer)
+ .setClassfileDesugaringOnly(backend.isCf())
.build(),
options ->
options.testing.globalSyntheticCreatedCallback =
programClass -> {
- if (programClass
- .getClassReference()
- .getDescriptor()
- .equals(DexItemFactory.varHandleDescriptorString)) {
- // We emit a desugared var handle. Rewrite it here to allow checking for final
- // type names.
- generatedGlobalSynthetics.add(
- Reference.classFromDescriptor(
- DexItemFactory.desugarVarHandleDescriptorString)
- .getTypeName());
- } else if (programClass
- .getClassReference()
- .getDescriptor()
- .equals(DexItemFactory.methodHandlesLookupDescriptorString)) {
- // We emit a desugared var handle. Rewrite it here to allow checking for final
- // type names.
- generatedGlobalSynthetics.add(
- Reference.classFromDescriptor(
- DexItemFactory.desugarMethodHandlesLookupDescriptorString)
- .getTypeName());
- } else {
- generatedGlobalSynthetics.add(programClass.getTypeName());
- }
+ String descriptor = programClass.getClassReference().getDescriptor();
+ generatedGlobalSynthetics.add(
+ descriptor
+ .replace(
+ "Ljava/lang/invoke/VarHandle",
+ "Lcom/android/tools/r8/DesugarVarHandle")
+ .replace(
+ "Ljava/lang/invoke/MethodHandles$Lookup",
+ "Lcom/android/tools/r8/DesugarMethodHandlesLookup"));
});
- Set<String> readGlobalSynthetics =
- new CodeInspector(output)
- .allClasses().stream().map(FoundClassSubject::getFinalName).collect(Collectors.toSet());
- assertEquals(generatedGlobalSynthetics, readGlobalSynthetics);
+ assertTrue(globalsConsumer.isSingleGlobal());
+ testForD8()
+ .apply(
+ b ->
+ b.getBuilder().addGlobalSyntheticsResourceProviders(globalsConsumer.getProviders()))
+ .setMinApi(AndroidApiLevel.K)
+ .compile()
+ .inspect(
+ inspector -> {
+ Set<String> readGlobalSynthetics =
+ inspector.allClasses().stream()
+ .map(FoundClassSubject::getFinalDescriptor)
+ .collect(Collectors.toSet());
+ assertEquals(generatedGlobalSynthetics, readGlobalSynthetics);
+ });
}
}
diff --git a/src/test/testbase/java/com/android/tools/r8/TestCompilerBuilder.java b/src/test/testbase/java/com/android/tools/r8/TestCompilerBuilder.java
index 3993296..adfdeaf 100644
--- a/src/test/testbase/java/com/android/tools/r8/TestCompilerBuilder.java
+++ b/src/test/testbase/java/com/android/tools/r8/TestCompilerBuilder.java
@@ -619,7 +619,10 @@
GlobalSyntheticsGeneratorCommand.builder()
.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.API_DATABASE_LEVEL))
.setMinApiLevel(minApiLevel)
- .setProgramConsumer(DexIndexedConsumer.emptyConsumer())
+ .setGlobalSyntheticsConsumer(
+ (data, context, handler) -> {
+ // Ignore the data and context, callback is hit below.
+ })
.build();
InternalOptions internalOptions = command.getInternalOptions();
internalOptions.testing.globalSyntheticCreatedCallback =
diff --git a/third_party/binary_compatibility_tests/compiler_api_tests.tar.gz.sha1 b/third_party/binary_compatibility_tests/compiler_api_tests.tar.gz.sha1
index 5f24411..32f7368 100644
--- a/third_party/binary_compatibility_tests/compiler_api_tests.tar.gz.sha1
+++ b/third_party/binary_compatibility_tests/compiler_api_tests.tar.gz.sha1
@@ -1 +1 @@
-37caffb18e8b47c65512ff667a3d6e91c23e6734
\ No newline at end of file
+34436ff2aee451c7d8e5c0f28193134822238d4b
\ No newline at end of file