Merge commit '9066ac43ffdd344123a2f1e13979e9df3232b42b' into dev-release
diff --git a/.gitignore b/.gitignore index 45852a0..2e7f798 100644 --- a/.gitignore +++ b/.gitignore
@@ -225,6 +225,8 @@ third_party/r8.tar.gz third_party/r8-releases/2.0.74 third_party/r8-releases/2.0.74.tar.gz +third_party/r8-releases/3.2.54 +third_party/r8-releases/3.2.54.tar.gz third_party/r8mappings third_party/r8mappings.tar.gz third_party/remapper
diff --git a/build.gradle b/build.gradle index 67074c7..e5467f0 100644 --- a/build.gradle +++ b/build.gradle
@@ -354,6 +354,7 @@ "retrace/binary_compatibility", "r8", "r8-releases/2.0.74", + "r8-releases/3.2.54", "r8mappings", "tachiyomi" ],
diff --git a/buildSrc/src/main/java/dx/DexMergerTask.java b/buildSrc/src/main/java/dx/DexMergerTask.java index 735afa7..5dea52b 100644 --- a/buildSrc/src/main/java/dx/DexMergerTask.java +++ b/buildSrc/src/main/java/dx/DexMergerTask.java
@@ -10,17 +10,13 @@ import java.util.List; import java.util.Set; import javax.inject.Inject; -import org.gradle.api.Action; import org.gradle.api.DefaultTask; -import org.gradle.api.UncheckedIOException; import org.gradle.api.file.FileCollection; import org.gradle.api.tasks.InputFile; import org.gradle.api.tasks.InputFiles; import org.gradle.api.tasks.OutputFile; import org.gradle.api.tasks.TaskAction; -import org.gradle.process.ExecSpec; import org.gradle.workers.IsolationMode; -import org.gradle.workers.WorkerConfiguration; import org.gradle.workers.WorkerExecutor; import utils.Utils; @@ -67,19 +63,30 @@ @TaskAction void exec() { - workerExecutor.submit(RunDexMerger.class, config -> { - config.setIsolationMode(IsolationMode.NONE); - config.params(source.getFiles(), destination, dexMergerExecutable); - }); + workerExecutor.submit( + RunDexMerger.class, + config -> { + File executable = + dexMergerExecutable.isPresent() + ? dexMergerExecutable.get() + : config + .getForkOptions() + .getWorkingDir() + .toPath() + .resolve(Utils.dexMergerExecutable()) + .toFile(); + config.setIsolationMode(IsolationMode.NONE); + config.params(source.getFiles(), destination, executable); + }); } public static class RunDexMerger implements Runnable { private final Set<File> sources; private final File destination; - private final Optional<File> dexMergerExecutable; + private final File dexMergerExecutable; @Inject - public RunDexMerger(Set<File> sources, File destination, Optional<File> dexMergerExecutable) { + public RunDexMerger(Set<File> sources, File destination, File dexMergerExecutable) { this.sources = sources; this.destination = destination; this.dexMergerExecutable = dexMergerExecutable; @@ -89,7 +96,7 @@ public void run() { try { List<String> command = new ArrayList<>(); - command.add(dexMergerExecutable.or(Utils::dexMergerExecutable).getCanonicalPath()); + command.add(dexMergerExecutable.getCanonicalPath()); command.add(destination.getCanonicalPath()); for (File source : sources) { command.add(source.getCanonicalPath());
diff --git a/buildSrc/src/main/java/dx/DxTask.java b/buildSrc/src/main/java/dx/DxTask.java index d8975dc..e05e8fb 100644 --- a/buildSrc/src/main/java/dx/DxTask.java +++ b/buildSrc/src/main/java/dx/DxTask.java
@@ -75,20 +75,31 @@ @TaskAction void exec() { - workerExecutor.submit(RunDx.class, config -> { - config.setIsolationMode(IsolationMode.NONE); - config.params(source.getFiles(), destination, dxExecutable, debug); - }); + workerExecutor.submit( + RunDx.class, + config -> { + File executable = + dxExecutable.isPresent() + ? dxExecutable.get() + : config + .getForkOptions() + .getWorkingDir() + .toPath() + .resolve(Utils.dxExecutable()) + .toFile(); + config.setIsolationMode(IsolationMode.NONE); + config.params(source.getFiles(), destination, executable, debug); + }); } public static class RunDx implements Runnable { private final Set<File> sources; private final File destination; - private final Optional<File> dxExecutable; + private final File dxExecutable; private final boolean debug; @Inject - public RunDx(Set<File> sources, File destination, Optional<File> dxExecutable, boolean debug) { + public RunDx(Set<File> sources, File destination, File dxExecutable, boolean debug) { this.sources = sources; this.destination = destination; this.dxExecutable = dxExecutable; @@ -99,7 +110,7 @@ public void run() { try { List<String> command = new ArrayList<>(); - command.add(dxExecutable.or(Utils::dxExecutable).getCanonicalPath()); + command.add(dxExecutable.getCanonicalPath()); command.add("--dex"); command.add("--output"); command.add(destination.getCanonicalPath());
diff --git a/buildSrc/src/main/java/utils/Utils.java b/buildSrc/src/main/java/utils/Utils.java index 7843edd..7f09b81 100644 --- a/buildSrc/src/main/java/utils/Utils.java +++ b/buildSrc/src/main/java/utils/Utils.java
@@ -3,7 +3,8 @@ // BSD-style license that can be found in the LICENSE file. package utils; -import java.io.File; +import java.nio.file.Path; +import java.nio.file.Paths; public class Utils { public static String toolsDir() { @@ -17,13 +18,17 @@ } } - public static File dxExecutable() { - String dxExecutableName = Utils.toolsDir().equals("windows") ? "dx.bat" : "dx"; - return new File("tools/" + Utils.toolsDir() + "/dx/bin/" + dxExecutableName); + public static boolean isWindows() { + return toolsDir().equals("windows"); } - public static File dexMergerExecutable() { - String executableName = Utils.toolsDir().equals("windows") ? "dexmerger.bat" : "dexmerger"; - return new File("tools/" + Utils.toolsDir() + "/dx/bin/" + executableName); + public static Path dxExecutable() { + String dxExecutableName = isWindows() ? "dx.bat" : "dx"; + return Paths.get("tools", toolsDir(), "dx", "bin", dxExecutableName); + } + + public static Path dexMergerExecutable() { + String executableName = isWindows() ? "dexmerger.bat" : "dexmerger"; + return Paths.get("tools", toolsDir(), "dx", "bin", executableName); } }
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs_alternative_3.json b/src/library_desugar/jdk11/desugar_jdk_libs_alternative_3.json index de5d525..57f7c3e 100644 --- a/src/library_desugar/jdk11/desugar_jdk_libs_alternative_3.json +++ b/src/library_desugar/jdk11/desugar_jdk_libs_alternative_3.json
@@ -55,7 +55,6 @@ "api_level_below_or_equal": 23, "rewrite_prefix": { "java.io.DesugarBufferedReader": "j$.io.DesugarBufferedReader", - "java.io.UncheckedIOException": "j$.io.UncheckedIOException", "java.util.DoubleSummaryStatistics": "j$.util.DoubleSummaryStatistics", "java.util.IntSummaryStatistics": "j$.util.IntSummaryStatistics", "java.util.LongSummaryStatistics": "j$.util.LongSummaryStatistics", @@ -70,6 +69,9 @@ "java.util.function.": "j$.util.function.", "java.util.stream.": "j$.util.stream." }, + "maintain_prefix": [ + "java.io.UncheckedIOException" + ], "emulate_interface": { "java.lang.Iterable": "j$.lang.Iterable", "java.util.Collection": "j$.util.Collection",
diff --git a/src/main/java/com/android/tools/r8/CompatProguardCommandBuilder.java b/src/main/java/com/android/tools/r8/CompatProguardCommandBuilder.java index 90544cc..b638235 100644 --- a/src/main/java/com/android/tools/r8/CompatProguardCommandBuilder.java +++ b/src/main/java/com/android/tools/r8/CompatProguardCommandBuilder.java
@@ -14,9 +14,7 @@ public CompatProguardCommandBuilder( boolean forceProguardCompatibility, DiagnosticsHandler diagnosticsHandler) { super(diagnosticsHandler); - if (forceProguardCompatibility) { - internalForceProguardCompatibility(); - } + setProguardCompatibility(forceProguardCompatibility); setIgnoreDexInArchive(true); } @@ -26,9 +24,7 @@ public CompatProguardCommandBuilder( boolean forceProguardCompatibility, boolean disableVerticalClassMerging) { - if (forceProguardCompatibility) { - internalForceProguardCompatibility(); - } + setProguardCompatibility(forceProguardCompatibility); setDisableVerticalClassMerging(disableVerticalClassMerging); setIgnoreDexInArchive(true); }
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java index 7f74668..c953853 100644 --- a/src/main/java/com/android/tools/r8/D8Command.java +++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -539,7 +539,6 @@ assert !internal.isMinifying(); assert !internal.passthroughDexCode; internal.passthroughDexCode = true; - assert internal.neverMergePrefixes.contains("j$."); // Assert some of R8 optimizations are disabled. assert !internal.inlinerOptions().enableInlining; @@ -580,7 +579,9 @@ HorizontalClassMergerOptions horizontalClassMergerOptions = internal.horizontalClassMergerOptions(); if (internal.isGeneratingDex()) { - horizontalClassMergerOptions.setRestrictToSynthetics(); + // TODO(b/227791663): Disable until fixed. + horizontalClassMergerOptions.disable(); + // horizontalClassMergerOptions.setRestrictToSynthetics(); } else { assert internal.isGeneratingClassFiles(); horizontalClassMergerOptions.disable();
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java index 05f03d2..f259f26 100644 --- a/src/main/java/com/android/tools/r8/R8Command.java +++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -138,10 +138,6 @@ // Internal - void internalForceProguardCompatibility() { - this.forceProguardCompatibility = true; - } - void setDisableVerticalClassMerging(boolean disableVerticalClassMerging) { this.disableVerticalClassMerging = disableVerticalClassMerging; } @@ -203,6 +199,23 @@ return self(); } + /** + * Set Proguard compatibility mode. + * + * <p>If true, R8 will attempt to retain more compatibility with Proguard. Most notably, R8 will + * introduce rules for keeping more default constructors as well as various attributes. Note + * that setting R8 in compatibility mode will result in larger residual programs. + */ + public Builder setProguardCompatibility(boolean value) { + this.forceProguardCompatibility = value; + return self(); + } + + /** Get the current value of Proguard compatibility mode. */ + public boolean getProguardCompatibility() { + return forceProguardCompatibility; + } + /** Add proguard configuration-file resources. */ public Builder addProguardConfigurationFiles(Path... paths) { guard(() -> { @@ -851,6 +864,11 @@ return enableMinification; } + /** Get the Proguard compatibility state. */ + public boolean getProguardCompatibility() { + return forceProguardCompatibility; + } + @Override InternalOptions getInternalOptions() { InternalOptions internal = new InternalOptions(getMode(), proguardConfiguration, getReporter());
diff --git a/src/main/java/com/android/tools/r8/R8CommandParser.java b/src/main/java/com/android/tools/r8/R8CommandParser.java index 5e7cf59..56e2b97 100644 --- a/src/main/java/com/android/tools/r8/R8CommandParser.java +++ b/src/main/java/com/android/tools/r8/R8CommandParser.java
@@ -83,6 +83,7 @@ + "# Minimum Android API level compatibility, default: " + AndroidApiLevel.getDefault().getLevel() + ".", + " --pg-compat # Compile with R8 in Proguard compatibility mode.", " --pg-conf <file> # Proguard configuration <file>.", " --pg-map-output <file> # Output the resulting name and line mapping to" + " <file>.", @@ -195,6 +196,8 @@ "Cannot compile in both --debug and --release mode.", argsOrigin)); } state.mode = CompilationMode.RELEASE; + } else if (arg.equals("--pg-compat")) { + builder.setProguardCompatibility(true); } else if (arg.equals("--dex")) { if (state.outputMode == OutputMode.ClassFile) { builder.error(
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java b/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java index 29dc73f..ce94d5d 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
@@ -93,6 +93,12 @@ return opcode == Opcodes.GETSTATIC; } + public boolean isStaticFieldPut() { + return opcode == Opcodes.PUTSTATIC; + } + + public abstract CfFieldInstruction createWithField(DexField field); + @Override public CfFieldInstruction asFieldInstruction() { return this;
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInstanceFieldRead.java b/src/main/java/com/android/tools/r8/cf/code/CfInstanceFieldRead.java index 145bc94..2684c0d 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfInstanceFieldRead.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfInstanceFieldRead.java
@@ -22,6 +22,11 @@ } @Override + public CfFieldInstruction createWithField(DexField otherField) { + return new CfInstanceFieldRead(otherField); + } + + @Override void internalRegisterUse( UseRegistry<?> registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) { registry.registerInstanceFieldReadInstruction(this);
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInstanceFieldWrite.java b/src/main/java/com/android/tools/r8/cf/code/CfInstanceFieldWrite.java index 6d01f49..c9138bb 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfInstanceFieldWrite.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfInstanceFieldWrite.java
@@ -21,6 +21,11 @@ } @Override + public CfFieldInstruction createWithField(DexField otherField) { + return new CfInstanceFieldWrite(otherField); + } + + @Override void internalRegisterUse( UseRegistry<?> registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) { registry.registerInstanceFieldWrite(getField());
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfStaticFieldRead.java b/src/main/java/com/android/tools/r8/cf/code/CfStaticFieldRead.java index 66fc48e..30715e1 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfStaticFieldRead.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfStaticFieldRead.java
@@ -22,6 +22,11 @@ } @Override + public CfFieldInstruction createWithField(DexField otherField) { + return new CfStaticFieldRead(otherField); + } + + @Override void internalRegisterUse( UseRegistry<?> registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) { registry.registerStaticFieldReadInstruction(this);
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfStaticFieldWrite.java b/src/main/java/com/android/tools/r8/cf/code/CfStaticFieldWrite.java index 2cd0f26..24b5e6d 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfStaticFieldWrite.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfStaticFieldWrite.java
@@ -21,6 +21,11 @@ } @Override + public CfFieldInstruction createWithField(DexField otherField) { + return new CfStaticFieldWrite(otherField); + } + + @Override void internalRegisterUse( UseRegistry<?> registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) { registry.registerStaticFieldWrite(getField());
diff --git a/src/main/java/com/android/tools/r8/dex/CodeToKeep.java b/src/main/java/com/android/tools/r8/dex/CodeToKeep.java index 3e33ca8..7bfd947 100644 --- a/src/main/java/com/android/tools/r8/dex/CodeToKeep.java +++ b/src/main/java/com/android/tools/r8/dex/CodeToKeep.java
@@ -25,6 +25,7 @@ static CodeToKeep createCodeToKeep(InternalOptions options, NamingLens namingLens) { if ((!namingLens.hasPrefixRewritingLogic() + && options.machineDesugaredLibrarySpecification.getMaintainType().isEmpty() && !options.machineDesugaredLibrarySpecification.hasEmulatedInterfaces()) || options.isDesugaredLibraryCompilation() || options.testing.enableExperimentalDesugaredLibraryKeepRuleGenerator) { @@ -67,6 +68,7 @@ private boolean shouldKeep(DexType type) { return namingLens.prefixRewrittenType(type) != null + || options.machineDesugaredLibrarySpecification.getMaintainType().contains(type) || options.machineDesugaredLibrarySpecification.isCustomConversionRewrittenType(type) || options.machineDesugaredLibrarySpecification.isEmulatedInterfaceRewrittenType(type) // TODO(b/158632510): This should prefix match on DexString.
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotation.java b/src/main/java/com/android/tools/r8/graph/DexAnnotation.java index 5a2bffc..d26278f 100644 --- a/src/main/java/com/android/tools/r8/graph/DexAnnotation.java +++ b/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
@@ -390,9 +390,13 @@ public static DexAnnotation createAnnotationSynthesizedClass( SyntheticKind kind, DexItemFactory dexItemFactory) { + DexString versionHash = + dexItemFactory.createString(dexItemFactory.getSyntheticNaming().getVersionHash()); DexAnnotationElement kindElement = new DexAnnotationElement(dexItemFactory.kindString, DexValueInt.create(kind.getId())); - DexAnnotationElement[] elements = new DexAnnotationElement[] {kindElement}; + DexAnnotationElement versionHashElement = + new DexAnnotationElement(dexItemFactory.versionHashString, new DexValueString(versionHash)); + DexAnnotationElement[] elements = new DexAnnotationElement[] {kindElement, versionHashElement}; return new DexAnnotation( VISIBILITY_BUILD, new DexEncodedAnnotation(dexItemFactory.annotationSynthesizedClass, elements)); @@ -413,17 +417,29 @@ return null; } int length = annotation.annotation.elements.length; - if (length != 1) { + if (length != 2) { return null; } - assert factory.kindString.isLessThan(factory.valueString); + assert factory.kindString.isLessThan(factory.versionHashString); DexAnnotationElement kindElement = annotation.annotation.elements[0]; + DexAnnotationElement versionHashElement = annotation.annotation.elements[1]; if (kindElement.name != factory.kindString) { return null; } if (!kindElement.value.isDexValueInt()) { return null; } + if (versionHashElement.name != factory.versionHashString) { + return null; + } + if (!versionHashElement.value.isDexValueString()) { + return null; + } + String currentVersionHash = synthetics.getNaming().getVersionHash(); + String syntheticVersionHash = versionHashElement.value.asDexValueString().getValue().toString(); + if (!currentVersionHash.equals(syntheticVersionHash)) { + return null; + } SyntheticKind kind = synthetics.getNaming().fromId(kindElement.value.asDexValueInt().getValue()); return kind;
diff --git a/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java b/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java index 765fef3..aeab5fe 100644 --- a/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java +++ b/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java
@@ -6,10 +6,9 @@ import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfo; import com.android.tools.r8.references.MethodReference; -import java.util.function.Consumer; public abstract class DexClassAndMethod extends DexClassAndMember<DexEncodedMethod, DexMethod> - implements LookupTarget { + implements LookupMethodTarget { DexClassAndMethod(DexClass holder, DexEncodedMethod method) { super(holder, method); @@ -88,16 +87,6 @@ } @Override - public boolean isMethodTarget() { - return true; - } - - @Override - public DexClassAndMethod asMethodTarget() { - return this; - } - - @Override public boolean isMethod() { return true; } @@ -113,8 +102,7 @@ } @Override - public void accept( - Consumer<DexClassAndMethod> methodConsumer, Consumer<LookupLambdaTarget> lambdaConsumer) { - methodConsumer.accept(this); + public DexClassAndMethod getTarget() { + return this; } }
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java index 3e5150c..98dac6d 100644 --- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java +++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -338,6 +338,7 @@ public final DexString valueString = createString("value"); public final DexString kindString = createString("kind"); + public final DexString versionHashString = createString("versionHash"); // Prefix for runtime affecting yet potential class-retained annotations. public final DexString dalvikAnnotationOptimizationPrefix = @@ -643,7 +644,7 @@ createStaticallyKnownType("Ldalvik/annotation/SourceDebugExtension;"); public final DexType annotationThrows = createStaticallyKnownType("Ldalvik/annotation/Throws;"); public final DexType annotationSynthesizedClass = - createStaticallyKnownType("Lcom/android/tools/r8/annotations/SynthesizedClass;"); + createStaticallyKnownType("Lcom/android/tools/r8/annotations/SynthesizedClassV2;"); public final DexType annotationCovariantReturnType = createStaticallyKnownType("Ldalvik/annotation/codegen/CovariantReturnType;"); public final DexType annotationCovariantReturnTypes =
diff --git a/src/main/java/com/android/tools/r8/graph/DexType.java b/src/main/java/com/android/tools/r8/graph/DexType.java index 810874b..2e84b7a 100644 --- a/src/main/java/com/android/tools/r8/graph/DexType.java +++ b/src/main/java/com/android/tools/r8/graph/DexType.java
@@ -157,7 +157,8 @@ if (clazz == null) { return false; } - if (clazz.isInterface() && appView.getOpenClosedInterfacesCollection().isMaybeOpen(clazz)) { + // TODO(b/214496607): Allow uninstantiated reasoning for closed interfaces. + if (clazz.isInterface()) { return false; } return !appView.appInfo().isInstantiatedDirectlyOrIndirectly(clazz);
diff --git a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java index 8e5ec1f..4354f06 100644 --- a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java +++ b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
@@ -360,9 +360,6 @@ if (InternalOptions.SUPPORTED_CF_VERSION.isLessThan(version)) { throw new CompilationError("Unsupported class file version: " + version, origin); } - if (version.isGreaterThanOrEqualTo(InternalOptions.EXPERIMENTAL_CF_VERSION)) { - application.options.warningExperimentalClassFileVersion(origin); - } this.deprecated = AsmUtils.isDeprecated(access); accessFlags = ClassAccessFlags.fromCfAccessFlags(cleanAccessFlags(access)); type = application.getTypeFromName(name);
diff --git a/src/main/java/com/android/tools/r8/graph/LookupCompletenessHelper.java b/src/main/java/com/android/tools/r8/graph/LookupCompletenessHelper.java index 4a99071..504ca65 100644 --- a/src/main/java/com/android/tools/r8/graph/LookupCompletenessHelper.java +++ b/src/main/java/com/android/tools/r8/graph/LookupCompletenessHelper.java
@@ -38,9 +38,9 @@ } } - void checkDexClassAndMethod(DexClassAndMethod classAndMethod) { - checkClass(classAndMethod.getHolder()); - checkMethod(classAndMethod.getDefinition()); + void checkDexClassAndMethod(LookupMethodTarget methodTarget) { + checkClass(methodTarget.getHolder()); + checkMethod(methodTarget.getDefinition()); } LookupResultCollectionState computeCollectionState(
diff --git a/src/main/java/com/android/tools/r8/graph/LookupLambdaTarget.java b/src/main/java/com/android/tools/r8/graph/LookupLambdaTarget.java index 12eeddc..e1209e9 100644 --- a/src/main/java/com/android/tools/r8/graph/LookupLambdaTarget.java +++ b/src/main/java/com/android/tools/r8/graph/LookupLambdaTarget.java
@@ -30,7 +30,7 @@ @Override public void accept( - Consumer<DexClassAndMethod> methodConsumer, Consumer<LookupLambdaTarget> lambdaConsumer) { + Consumer<LookupMethodTarget> methodConsumer, Consumer<LookupLambdaTarget> lambdaConsumer) { lambdaConsumer.accept(this); }
diff --git a/src/main/java/com/android/tools/r8/graph/LookupMethodTarget.java b/src/main/java/com/android/tools/r8/graph/LookupMethodTarget.java new file mode 100644 index 0000000..b7a6aa0 --- /dev/null +++ b/src/main/java/com/android/tools/r8/graph/LookupMethodTarget.java
@@ -0,0 +1,33 @@ +// Copyright (c) 2022, 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.graph; + +import java.util.function.Consumer; + +public interface LookupMethodTarget extends LookupTarget { + + @Override + default boolean isMethodTarget() { + return true; + } + + @Override + default LookupMethodTarget asMethodTarget() { + return this; + } + + @Override + default void accept( + Consumer<LookupMethodTarget> methodConsumer, Consumer<LookupLambdaTarget> lambdaConsumer) { + methodConsumer.accept(this); + } + + DexClass getHolder(); + + DexMethod getReference(); + + DexEncodedMethod getDefinition(); + + DexClassAndMethod getTarget(); +}
diff --git a/src/main/java/com/android/tools/r8/graph/LookupMethodTargetWithAccessOverride.java b/src/main/java/com/android/tools/r8/graph/LookupMethodTargetWithAccessOverride.java new file mode 100644 index 0000000..d69c5b3 --- /dev/null +++ b/src/main/java/com/android/tools/r8/graph/LookupMethodTargetWithAccessOverride.java
@@ -0,0 +1,41 @@ +// Copyright (c) 2022, 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.graph; + +public class LookupMethodTargetWithAccessOverride implements LookupMethodTarget { + + private final DexClassAndMethod target; + private final DexClassAndMethod accessOverride; + + public LookupMethodTargetWithAccessOverride( + DexClassAndMethod target, DexClassAndMethod accessOverride) { + this.target = target; + this.accessOverride = accessOverride; + } + + @Override + public DexClassAndMethod getAccessOverride() { + return accessOverride; + } + + @Override + public DexClass getHolder() { + return target.getHolder(); + } + + @Override + public DexMethod getReference() { + return target.getReference(); + } + + @Override + public DexEncodedMethod getDefinition() { + return target.getDefinition(); + } + + @Override + public DexClassAndMethod getTarget() { + return target; + } +}
diff --git a/src/main/java/com/android/tools/r8/graph/LookupResult.java b/src/main/java/com/android/tools/r8/graph/LookupResult.java index bc85814..b8bc3c8 100644 --- a/src/main/java/com/android/tools/r8/graph/LookupResult.java +++ b/src/main/java/com/android/tools/r8/graph/LookupResult.java
@@ -5,10 +5,11 @@ package com.android.tools.r8.graph; import com.android.tools.r8.graph.LookupResult.LookupResultSuccess.LookupResultCollectionState; -import com.android.tools.r8.utils.collections.DexClassAndMethodSet; import java.util.ArrayList; import java.util.Collections; +import java.util.IdentityHashMap; import java.util.List; +import java.util.Map; import java.util.function.Consumer; public abstract class LookupResult { @@ -34,14 +35,14 @@ } public abstract void forEach( - Consumer<? super DexClassAndMethod> onMethodTarget, + Consumer<? super LookupMethodTarget> onMethodTarget, Consumer<? super LookupLambdaTarget> onLambdaTarget); public abstract void forEachFailureDependency( Consumer<? super DexEncodedMethod> methodCausingFailureConsumer); public static LookupResultSuccess createResult( - DexClassAndMethodSet methodTargets, + Map<DexMethod, LookupMethodTarget> methodTargets, List<LookupLambdaTarget> lambdaTargets, List<DexEncodedMethod> methodsCausingFailure, LookupResultCollectionState state) { @@ -60,18 +61,18 @@ private static final LookupResultSuccess EMPTY_INSTANCE = new LookupResultSuccess( - DexClassAndMethodSet.empty(), + new IdentityHashMap<>(), Collections.emptyList(), Collections.emptyList(), LookupResultCollectionState.Incomplete); - private final DexClassAndMethodSet methodTargets; + private final Map<DexMethod, LookupMethodTarget> methodTargets; private final List<LookupLambdaTarget> lambdaTargets; private final List<DexEncodedMethod> methodsCausingFailure; private LookupResultCollectionState state; private LookupResultSuccess( - DexClassAndMethodSet methodTargets, + Map<DexMethod, LookupMethodTarget> methodTargets, List<LookupLambdaTarget> lambdaTargets, List<DexEncodedMethod> methodsCausingFailure, LookupResultCollectionState state) { @@ -99,9 +100,9 @@ @Override public void forEach( - Consumer<? super DexClassAndMethod> onMethodTarget, + Consumer<? super LookupMethodTarget> onMethodTarget, Consumer<? super LookupLambdaTarget> onLambdaTarget) { - methodTargets.forEach(onMethodTarget); + methodTargets.forEach((key, value) -> onMethodTarget.accept(value)); lambdaTargets.forEach(onLambdaTarget); } @@ -113,7 +114,7 @@ public boolean contains(DexEncodedMethod method) { // Containment of a method in the lookup results only pertains to the method targets. - return methodTargets.contains(method); + return methodTargets.containsKey(method.getReference()); } @Override @@ -145,7 +146,7 @@ } // TODO(b/150932978): Check lambda targets implementation methods. if (methodTargets.size() == 1) { - return methodTargets.iterator().next(); + return methodTargets.values().iterator().next(); } else if (lambdaTargets.size() == 1) { return lambdaTargets.get(0); } @@ -159,13 +160,14 @@ public static class Builder { - private final DexClassAndMethodSet methodTargets = DexClassAndMethodSet.create(); + private final Map<DexMethod, LookupMethodTarget> methodTargets = new IdentityHashMap<>(); private final List<LookupLambdaTarget> lambdaTargets = new ArrayList<>(); private final List<DexEncodedMethod> methodsCausingFailure = new ArrayList<>(); private LookupResultCollectionState state; - public Builder addMethodTarget(DexClassAndMethod methodTarget) { - methodTargets.add(methodTarget); + public Builder addMethodTarget(LookupMethodTarget methodTarget) { + assert methodTarget.isMethodTarget(); + methodTargets.putIfAbsent(methodTarget.asMethodTarget().getReference(), methodTarget); return this; } @@ -210,7 +212,7 @@ @Override public void forEach( - Consumer<? super DexClassAndMethod> onMethodTarget, + Consumer<? super LookupMethodTarget> onMethodTarget, Consumer<? super LookupLambdaTarget> onLambdaTarget) { // Nothing to iterate for a failed lookup. }
diff --git a/src/main/java/com/android/tools/r8/graph/LookupTarget.java b/src/main/java/com/android/tools/r8/graph/LookupTarget.java index cc0d574..4530c0f 100644 --- a/src/main/java/com/android/tools/r8/graph/LookupTarget.java +++ b/src/main/java/com/android/tools/r8/graph/LookupTarget.java
@@ -14,7 +14,7 @@ return false; } - default DexClassAndMethod asMethodTarget() { + default LookupMethodTarget asMethodTarget() { return null; } @@ -22,6 +22,10 @@ return null; } + default DexClassAndMethod getAccessOverride() { + return null; + } + void accept( - Consumer<DexClassAndMethod> methodConsumer, Consumer<LookupLambdaTarget> lambdaConsumer); + Consumer<LookupMethodTarget> methodConsumer, Consumer<LookupLambdaTarget> lambdaConsumer); }
diff --git a/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java b/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java index 113c31c..49860a7 100644 --- a/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java +++ b/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java
@@ -13,7 +13,6 @@ import com.android.tools.r8.utils.BooleanBox; import com.android.tools.r8.utils.Box; import com.android.tools.r8.utils.OptionalBool; -import com.android.tools.r8.utils.collections.DexClassAndMethodSet; import java.util.Collection; import java.util.Collections; import java.util.function.BiPredicate; @@ -152,7 +151,7 @@ public abstract LookupTarget lookupVirtualDispatchTarget( InstantiatedObject instance, AppInfoWithClassHierarchy appInfo); - public abstract DexClassAndMethod lookupVirtualDispatchTarget( + public abstract LookupMethodTarget lookupVirtualDispatchTarget( DexClass dynamicInstance, AppInfoWithClassHierarchy appInfo); public abstract LookupTarget lookupVirtualDispatchTarget( @@ -451,8 +450,9 @@ // Only include if the target has code or is native. boolean isIncomplete = pinnedPredicate.isPinned(resolvedHolder) && pinnedPredicate.isPinned(resolvedMethod); + DexClassAndMethod resolutionPair = getResolutionPair(); return LookupResult.createResult( - DexClassAndMethodSet.create(getResolutionPair()), + Collections.singletonMap(resolutionPair.getReference(), resolutionPair), Collections.emptyList(), Collections.emptyList(), isIncomplete @@ -466,13 +466,12 @@ initialResolutionHolder.type, subClass -> { incompleteness.checkClass(subClass); - DexClassAndMethod dexClassAndMethod = + LookupMethodTarget lookupTarget = lookupVirtualDispatchTarget( subClass, appInfo, resolvedHolder.type, resultBuilder::addMethodCausingFailure); - if (dexClassAndMethod != null) { - incompleteness.checkDexClassAndMethod(dexClassAndMethod); - addVirtualDispatchTarget( - dexClassAndMethod, resolvedHolder.isInterface(), resultBuilder); + if (lookupTarget != null) { + incompleteness.checkDexClassAndMethod(lookupTarget); + addVirtualDispatchTarget(lookupTarget, resolvedHolder.isInterface(), resultBuilder); } }, lambda -> { @@ -552,10 +551,11 @@ } private static void addVirtualDispatchTarget( - DexClassAndMethod target, + LookupMethodTarget target, boolean holderIsInterface, LookupResultSuccess.Builder resultBuilder) { - DexEncodedMethod targetMethod = target.getDefinition(); + assert target.isMethodTarget(); + DexEncodedMethod targetMethod = target.asMethodTarget().getDefinition(); assert !targetMethod.isPrivateMethod(); if (holderIsInterface) { // Add default interface methods to the list of targets. @@ -609,7 +609,7 @@ } @Override - public DexClassAndMethod lookupVirtualDispatchTarget( + public LookupMethodTarget lookupVirtualDispatchTarget( DexClass dynamicInstance, AppInfoWithClassHierarchy appInfo) { return lookupVirtualDispatchTarget( dynamicInstance, appInfo, initialResolutionHolder.type, emptyConsumer()); @@ -634,7 +634,7 @@ lambdaInstance, appInfo, methodCausingFailureConsumer); } - private DexClassAndMethod lookupVirtualDispatchTarget( + private LookupMethodTarget lookupVirtualDispatchTarget( DexClass dynamicInstance, AppInfoWithClassHierarchy appInfo, DexType resolutionHolder, @@ -644,18 +644,20 @@ // TODO(b/148591377): Enable this assertion. // The dynamic type cannot be an interface. // assert !dynamicInstance.isInterface(); + DexClassAndMethod initialResolutionPair = getResolutionPair(); if (resolvedMethod.isPrivateMethod()) { // If the resolved reference is private there is no dispatch. // This is assuming that the method is accessible, which implies self/nest access. - return getResolutionPair(); + return initialResolutionPair; } boolean allowPackageBlocked = resolvedMethod.accessFlags.isPackagePrivate(); DexClass current = dynamicInstance; - DexEncodedMethod overrideTarget = resolvedMethod; + DexClassAndMethod overrideTarget = initialResolutionPair; while (current != null) { - DexEncodedMethod candidate = lookupOverrideCandidate(overrideTarget, current); + DexEncodedMethod candidate = + lookupOverrideCandidate(overrideTarget.getDefinition(), current); if (candidate == DexEncodedMethod.SENTINEL && allowPackageBlocked) { - overrideTarget = findWideningOverride(resolvedMethod, current, appInfo); + overrideTarget = findWideningOverride(initialResolutionPair, current, appInfo); allowPackageBlocked = false; continue; } @@ -667,7 +669,10 @@ current = current.superType == null ? null : appInfo.definitionFor(current.superType); continue; } - return DexClassAndMethod.create(current, candidate); + DexClassAndMethod target = DexClassAndMethod.create(current, candidate); + return overrideTarget != initialResolutionPair + ? new LookupMethodTargetWithAccessOverride(target, overrideTarget) + : target; } // If we have not found a candidate and the holder is not an interface it must be because the // class is missing. @@ -732,10 +737,10 @@ return null; } - private static DexEncodedMethod findWideningOverride( - DexEncodedMethod resolvedMethod, DexClass clazz, AppInfoWithClassHierarchy appView) { + private static DexClassAndMethod findWideningOverride( + DexClassAndMethod resolvedMethod, DexClass clazz, AppInfoWithClassHierarchy appView) { // Otherwise, lookup to first override that is distinct from resolvedMethod. - assert resolvedMethod.accessFlags.isPackagePrivate(); + assert resolvedMethod.getDefinition().accessFlags.isPackagePrivate(); while (clazz.superType != null) { clazz = appView.definitionFor(clazz.superType); if (clazz == null) { @@ -743,10 +748,10 @@ } DexEncodedMethod otherOverride = clazz.lookupVirtualMethod(resolvedMethod.getReference()); if (otherOverride != null - && isOverriding(resolvedMethod, otherOverride) + && isOverriding(resolvedMethod.getDefinition(), otherOverride) && (otherOverride.accessFlags.isPublic() || otherOverride.accessFlags.isProtected())) { - assert resolvedMethod != otherOverride; - return otherOverride; + assert resolvedMethod.getDefinition() != otherOverride; + return DexClassAndMethod.create(clazz, otherOverride); } } return resolvedMethod; @@ -818,19 +823,19 @@ } @Override - public DexClassAndMethod lookupVirtualDispatchTarget( + public LookupTarget lookupVirtualDispatchTarget( InstantiatedObject instance, AppInfoWithClassHierarchy appInfo) { return null; } @Override - public DexClassAndMethod lookupVirtualDispatchTarget( + public LookupMethodTarget lookupVirtualDispatchTarget( DexClass dynamicInstance, AppInfoWithClassHierarchy appInfo) { return null; } @Override - public DexClassAndMethod lookupVirtualDispatchTarget( + public LookupTarget lookupVirtualDispatchTarget( LambdaDescriptor lambdaInstance, AppInfoWithClassHierarchy appInfo, Consumer<? super DexEncodedMethod> methodCausingFailureConsumer) { @@ -1032,4 +1037,5 @@ return invalidSymbolicReference.get(); } } + }
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/ApiModelAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/ApiModelAnalysis.java index 1321169..aebbc12 100644 --- a/src/main/java/com/android/tools/r8/graph/analysis/ApiModelAnalysis.java +++ b/src/main/java/com/android/tools/r8/graph/analysis/ApiModelAnalysis.java
@@ -73,7 +73,7 @@ public void notifyMarkVirtualDispatchTargetAsLive( LookupTarget target, EnqueuerWorklist worklist) { target.accept( - this::computeAndSetApiLevelForDefinition, + lookupMethodTarget -> computeAndSetApiLevelForDefinition(lookupMethodTarget.getTarget()), lookupLambdaTarget -> { // The implementation method will be assigned an api level when visited. });
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java index 1d259f6..4e77680 100644 --- a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java +++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
@@ -170,7 +170,8 @@ } ProgramMethodSet result = ProgramMethodSet.create(); lookupResult.forEach( - methodTarget -> { + target -> { + DexClassAndMethod methodTarget = target.getTarget(); if (methodTarget.isProgramMethod()) { result.add(methodTarget.asProgramMethod()); }
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 acadf11..9ccaec9 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
@@ -83,7 +83,6 @@ import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagator; import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagatorIROptimizer; import com.android.tools.r8.optimize.interfaces.analysis.OpenClosedInterfacesAnalysis; -import com.android.tools.r8.optimize.interfaces.analysis.OpenClosedInterfacesAnalysisImpl; import com.android.tools.r8.position.MethodPosition; import com.android.tools.r8.shaking.AppInfoWithLiveness; import com.android.tools.r8.shaking.KeepMethodInfo; @@ -93,6 +92,7 @@ import com.android.tools.r8.utils.DescriptorUtils; import com.android.tools.r8.utils.ExceptionUtils; import com.android.tools.r8.utils.InternalOptions; +import com.android.tools.r8.utils.InternalOptions.NeverMergeGroup; import com.android.tools.r8.utils.ListUtils; import com.android.tools.r8.utils.StringDiagnostic; import com.android.tools.r8.utils.ThreadUtils; @@ -107,7 +107,6 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.stream.Collectors; public class IRConverter { @@ -158,7 +157,7 @@ private List<Action> onWaveDoneActions = null; private final Set<DexMethod> prunedMethodsInWave = Sets.newIdentityHashSet(); - private final List<DexString> neverMergePrefixes; + private final NeverMergeGroup<DexString> neverMerge; // Use AtomicBoolean to satisfy TSAN checking (see b/153714743). AtomicBoolean seenNotNeverMergePrefix = new AtomicBoolean(); AtomicBoolean seenNeverMergePrefix = new AtomicBoolean(); @@ -184,11 +183,11 @@ this.deadCodeRemover = new DeadCodeRemover(appView, codeRewriter); this.assertionsRewriter = new AssertionsRewriter(appView); this.idempotentFunctionCallCanonicalizer = new IdempotentFunctionCallCanonicalizer(appView); - this.neverMergePrefixes = - options.neverMergePrefixes.stream() - .map(prefix -> "L" + DescriptorUtils.getPackageBinaryNameFromJavaType(prefix)) - .map(options.itemFactory::createString) - .collect(Collectors.toList()); + this.neverMerge = + options.neverMerge.map( + prefix -> + options.itemFactory.createString( + "L" + DescriptorUtils.getPackageBinaryNameFromJavaType(prefix))); if (options.isDesugaredLibraryCompilation()) { // Specific L8 Settings, performs all desugaring including L8 specific desugaring. // @@ -259,7 +258,8 @@ this.memberValuePropagation = new MemberValuePropagation(appViewWithLiveness); this.methodOptimizationInfoCollector = new MethodOptimizationInfoCollector(appViewWithLiveness, this); - this.openClosedInterfacesAnalysis = new OpenClosedInterfacesAnalysisImpl(appViewWithLiveness); + // TODO(b/214496607): Enable open/closed interfaces analysis. + this.openClosedInterfacesAnalysis = OpenClosedInterfacesAnalysis.empty(); if (options.isMinifying()) { this.identifierNameStringMarker = new IdentifierNameStringMarker(appViewWithLiveness); } else { @@ -550,11 +550,17 @@ if (!appView.options().enableNeverMergePrefixes) { return; } - for (DexString neverMergePrefix : neverMergePrefixes) { - if (method.getHolderType().descriptor.startsWith(neverMergePrefix)) { + DexString descriptor = method.getHolderType().descriptor; + for (DexString neverMergePrefix : neverMerge.getPrefixes()) { + if (descriptor.startsWith(neverMergePrefix)) { seenNeverMergePrefix.getAndSet(true); } else { - seenNotNeverMergePrefix.getAndSet(true); + for (DexString exceptionPrefix : neverMerge.getExceptionPrefixes()) { + if (!descriptor.startsWith(exceptionPrefix)) { + seenNotNeverMergePrefix.getAndSet(true); + break; + } + } } // Don't mix. // TODO(b/168001352): Consider requiring that no 'never merge' prefix is ever seen as a @@ -562,16 +568,37 @@ if (seenNeverMergePrefix.get() && seenNotNeverMergePrefix.get()) { StringBuilder message = new StringBuilder(); message - .append("Merging dex file containing classes with prefix") - .append(neverMergePrefixes.size() > 1 ? "es " : " "); - for (int i = 0; i < neverMergePrefixes.size(); i++) { + .append("Merging DEX file containing classes with prefix") + .append(neverMerge.getPrefixes().size() > 1 ? "es " : " "); + for (int i = 0; i < neverMerge.getPrefixes().size(); i++) { message .append("'") - .append(neverMergePrefixes.get(0).toString().substring(1).replace('/', '.')) + .append(neverMerge.getPrefixes().get(i).toString().substring(1).replace('/', '.')) .append("'") - .append(i < neverMergePrefixes.size() - 1 ? ", " : ""); + .append(i < neverMerge.getPrefixes().size() - 1 ? ", " : ""); } - message.append(" with classes with any other prefixes is not allowed: "); + if (!neverMerge.getExceptionPrefixes().isEmpty()) { + message + .append(" with other classes, except classes with prefix") + .append(neverMerge.getExceptionPrefixes().size() > 1 ? "es " : " "); + for (int i = 0; i < neverMerge.getExceptionPrefixes().size(); i++) { + message + .append("'") + .append( + neverMerge + .getExceptionPrefixes() + .get(i) + .toString() + .substring(1) + .replace('/', '.')) + .append("'") + .append(i < neverMerge.getExceptionPrefixes().size() - 1 ? ", " : ""); + } + message.append(","); + } else { + message.append(" with classes with any other prefixes"); + } + message.append(" is not allowed: "); boolean first = true; int limit = 11; for (DexProgramClass clazz : appView.appInfo().classesWithDeterministicOrder()) {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/callgraph/InvokeExtractor.java b/src/main/java/com/android/tools/r8/ir/conversion/callgraph/InvokeExtractor.java index 4b15bbc..edd4f14 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/callgraph/InvokeExtractor.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/callgraph/InvokeExtractor.java
@@ -124,7 +124,8 @@ lookupResult .asLookupResultSuccess() .forEach( - methodTarget -> { + lookupMethodTarget -> { + DexClassAndMethod methodTarget = lookupMethodTarget.getTarget(); if (methodTarget.isProgramMethod()) { targets.add(methodTarget.asProgramMethod()); }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAmender.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAmender.java index c943028..92bd91e 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAmender.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAmender.java
@@ -8,8 +8,13 @@ import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexClass; import com.android.tools.r8.graph.DexDefinitionSupplier; +import com.android.tools.r8.graph.DexEncodedField; import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.DexField; +import com.android.tools.r8.graph.DexLibraryClass; import com.android.tools.r8.graph.DexMethod; +import com.android.tools.r8.graph.DexReference; +import com.android.tools.r8.graph.FieldAccessFlags; import com.android.tools.r8.graph.MethodAccessFlags; import com.android.tools.r8.utils.Reporter; import java.util.Map; @@ -28,20 +33,23 @@ public static void run(AppView<?> appView) { run( appView.options().machineDesugaredLibrarySpecification.getAmendLibraryMethods(), + appView.options().machineDesugaredLibrarySpecification.getAmendLibraryFields(), appView, appView.options().reporter, appView.computedMinApiLevel()); } public static void run( - Map<DexMethod, MethodAccessFlags> amendLibrary, + Map<DexMethod, MethodAccessFlags> amendLibraryMethod, + Map<DexField, FieldAccessFlags> amendLibraryField, DexDefinitionSupplier definitions, Reporter reporter, ComputedApiLevel minAPILevel) { - if (amendLibrary.isEmpty()) { + if (amendLibraryMethod.isEmpty() && amendLibraryField.isEmpty()) { return; } - new DesugaredLibraryAmender(definitions, reporter, minAPILevel).run(amendLibrary); + new DesugaredLibraryAmender(definitions, reporter, minAPILevel) + .run(amendLibraryMethod, amendLibraryField); } private DesugaredLibraryAmender( @@ -51,22 +59,40 @@ this.minAPILevel = minAPILevel; } - private void run(Map<DexMethod, MethodAccessFlags> amendLibrary) { - amendLibrary.forEach(this::amendLibraryMethod); + private void run( + Map<DexMethod, MethodAccessFlags> amendLibraryMethod, + Map<DexField, FieldAccessFlags> amendLibraryField) { + amendLibraryMethod.forEach(this::amendLibraryMethod); + amendLibraryField.forEach(this::amendLibraryField); + } + + private void amendLibraryField(DexField field, FieldAccessFlags fieldAccessFlags) { + DexLibraryClass libClass = getLibraryClass(field); + if (libClass == null) { + return; + } + if (libClass.lookupField(field) != null) { + return; + } + DexEncodedField encodedField = + DexEncodedField.syntheticBuilder() + .setField(field) + .setAccessFlags(fieldAccessFlags) + .setApiLevel(minAPILevel) + .build(); + if (fieldAccessFlags.isStatic()) { + libClass.appendStaticField(encodedField); + } else { + libClass.appendInstanceField(encodedField); + } } private void amendLibraryMethod(DexMethod method, MethodAccessFlags methodAccessFlags) { - DexClass dexClass = definitions.contextIndependentDefinitionFor(method.getHolderType()); - if (dexClass == null || !dexClass.isLibraryClass()) { - // Consider just throwing an error. - reporter.warning( - "Desugared library: Cannot amend library method " - + method - + " because the holder is not a library class" - + (dexClass == null ? "(null)." : ".")); + DexLibraryClass libClass = getLibraryClass(method); + if (libClass == null) { return; } - if (dexClass.lookupMethod(method) != null) { + if (libClass.lookupMethod(method) != null) { return; } DexEncodedMethod encodedMethod = @@ -76,6 +102,20 @@ .setCode(null) .setApiLevelForDefinition(minAPILevel) .build(); - dexClass.getMethodCollection().addMethod(encodedMethod); + libClass.getMethodCollection().addMethod(encodedMethod); + } + + private DexLibraryClass getLibraryClass(DexReference reference) { + DexClass dexClass = definitions.contextIndependentDefinitionFor(reference.getContextType()); + if (dexClass == null || !dexClass.isLibraryClass()) { + // Consider just throwing an error. + reporter.warning( + "Desugared library: Cannot amend library reference " + + reference + + " because the holder is not a library class" + + (dexClass == null ? "(null)." : ".")); + return null; + } + return dexClass.asLibraryClass(); } }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/AbstractMethodParser.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/AbstractMethodParser.java deleted file mode 100644 index d63efb6..0000000 --- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/AbstractMethodParser.java +++ /dev/null
@@ -1,81 +0,0 @@ -// Copyright (c) 2022, 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.ir.desugar.desugaredlibrary.humanspecification; - -import com.android.tools.r8.dex.Constants; -import com.android.tools.r8.errors.CompilationError; -import com.android.tools.r8.graph.DexItemFactory; -import com.android.tools.r8.graph.DexString; -import com.android.tools.r8.graph.DexType; -import com.android.tools.r8.utils.DescriptorUtils; -import com.google.common.collect.ImmutableMap; -import java.util.Map; - -/** Parse methods of the form: modifiers* returnType holder#name(arg0, ..., argN) */ -public abstract class AbstractMethodParser { - - private static final String SEPARATORS = "\\s+|,\\s+|#|\\(|\\)"; - - private static final Map<String, Integer> modifiers = - ImmutableMap.<String, Integer>builder() - .put("public", Constants.ACC_PUBLIC) - .put("private", Constants.ACC_PRIVATE) - .put("protected", Constants.ACC_PROTECTED) - .put("final", Constants.ACC_FINAL) - .put("abstract", Constants.ACC_ABSTRACT) - .put("static", Constants.ACC_STATIC) - .build(); - - final DexItemFactory factory; - - protected AbstractMethodParser(DexItemFactory factory) { - this.factory = factory; - } - - // TODO(b/218755060): It would be nice to avoid the split regexp and use a nextToken() - // method instead, then add a TraversalContinuation. - public void parseMethod(String signature) { - String[] tokens = signature.split(SEPARATORS); - if (tokens.length < 3) { - throw new CompilationError("Desugared library: cannot parse method " + signature); - } - methodStart(); - int first = parseModifiers(tokens); - returnType(stringTypeToDexType(tokens[first])); - holderType(stringTypeToDexType(tokens[first + 1])); - methodName(factory.createString(tokens[first + 1 + 1])); - for (int i = first + 3; i < tokens.length; i++) { - argType(stringTypeToDexType(tokens[i])); - } - methodEnd(); - } - - private DexType stringTypeToDexType(String stringType) { - return factory.createType(DescriptorUtils.javaTypeToDescriptor(stringType)); - } - - private int parseModifiers(String[] split) { - int index = 0; - while (modifiers.containsKey(split[index])) { - modifier(modifiers.get(split[index])); - index++; - } - return index; - } - - protected abstract void methodStart(); - - protected abstract void methodEnd(); - - protected abstract void returnType(DexType type); - - protected abstract void argType(DexType type); - - protected abstract void modifier(int access); - - protected abstract void holderType(DexType type); - - protected abstract void methodName(DexString name); -}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecificationParser.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecificationParser.java index 2eb550b..5936d8c 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecificationParser.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecificationParser.java
@@ -8,10 +8,13 @@ import static com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecificationParser.isHumanSpecification; import com.android.tools.r8.StringResource; +import com.android.tools.r8.graph.DexField; import com.android.tools.r8.graph.DexItemFactory; import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.DexType; import com.android.tools.r8.ir.desugar.desugaredlibrary.TopLevelFlagsBuilder; +import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.memberparser.HumanFieldParser; +import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.memberparser.HumanMethodParser; import com.android.tools.r8.origin.Origin; import com.android.tools.r8.utils.AndroidApiLevel; import com.android.tools.r8.utils.DescriptorUtils; @@ -48,6 +51,8 @@ static final String WRAPPER_CONVERSION_EXCLUDING_KEY = "wrapper_conversion_excluding"; static final String CUSTOM_CONVERSION_KEY = "custom_conversion"; static final String REWRITE_PREFIX_KEY = "rewrite_prefix"; + static final String MAINTAIN_PREFIX_KEY = "maintain_prefix"; + static final String RETARGET_STATIC_FIELD_KEY = "retarget_static_field"; static final String RETARGET_METHOD_KEY = "retarget_method"; static final String RETARGET_METHOD_EMULATED_DISPATCH_KEY = "retarget_method_with_emulated_dispatch"; @@ -57,11 +62,13 @@ static final String DONT_RETARGET_KEY = "dont_retarget"; static final String BACKPORT_KEY = "backport"; static final String AMEND_LIBRARY_METHOD_KEY = "amend_library_method"; + static final String AMEND_LIBRARY_FIELD_KEY = "amend_library_field"; static final String SHRINKER_CONFIG_KEY = "shrinker_config"; static final String SUPPORT_ALL_CALLBACKS_FROM_LIBRARY_KEY = "support_all_callbacks_from_library"; private final DexItemFactory dexItemFactory; private final HumanMethodParser methodParser; + private final HumanFieldParser fieldParser; private final Reporter reporter; private final boolean libraryCompilation; private final int minAPILevel; @@ -76,6 +83,7 @@ int minAPILevel) { this.dexItemFactory = dexItemFactory; this.methodParser = new HumanMethodParser(dexItemFactory); + this.fieldParser = new HumanFieldParser(dexItemFactory); this.reporter = reporter; this.minAPILevel = minAPILevel; this.libraryCompilation = libraryCompilation; @@ -235,6 +243,11 @@ builder.putRewritePrefix(rewritePrefix.getKey(), rewritePrefix.getValue().getAsString()); } } + if (jsonFlagSet.has(MAINTAIN_PREFIX_KEY)) { + for (JsonElement maintainPrefix : jsonFlagSet.get(MAINTAIN_PREFIX_KEY).getAsJsonArray()) { + builder.putMaintainPrefix(maintainPrefix.getAsString()); + } + } if (jsonFlagSet.has(REWRITE_DERIVED_PREFIX_KEY)) { for (Map.Entry<String, JsonElement> prefixToMatch : jsonFlagSet.get(REWRITE_DERIVED_PREFIX_KEY).getAsJsonObject().entrySet()) { @@ -245,6 +258,14 @@ } } } + if (jsonFlagSet.has(RETARGET_STATIC_FIELD_KEY)) { + for (Map.Entry<String, JsonElement> retarget : + jsonFlagSet.get(RETARGET_STATIC_FIELD_KEY).getAsJsonObject().entrySet()) { + builder.retargetStaticField( + parseField(retarget.getKey()), + stringDescriptorToDexType(retarget.getValue().getAsString())); + } + } if (jsonFlagSet.has(RETARGET_METHOD_KEY)) { for (Map.Entry<String, JsonElement> retarget : jsonFlagSet.get(RETARGET_METHOD_KEY).getAsJsonObject().entrySet()) { @@ -317,6 +338,13 @@ builder.amendLibraryMethod(methodParser.getMethod(), methodParser.getFlags()); } } + if (jsonFlagSet.has(AMEND_LIBRARY_FIELD_KEY)) { + JsonArray amendLibraryMember = jsonFlagSet.get(AMEND_LIBRARY_FIELD_KEY).getAsJsonArray(); + for (JsonElement amend : amendLibraryMember) { + fieldParser.parseField(amend.getAsString()); + builder.amendLibraryField(fieldParser.getField(), fieldParser.getFlags()); + } + } } private Set<DexMethod> parseMethods(JsonArray array) { @@ -332,6 +360,11 @@ return methodParser.getMethod(); } + private DexField parseField(String signature) { + fieldParser.parseField(signature); + return fieldParser.getField(); + } + private DexType stringDescriptorToDexType(String stringClass) { return dexItemFactory.createType(DescriptorUtils.javaTypeToDescriptor(stringClass)); }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanRewritingFlags.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanRewritingFlags.java index 9eb3ed7..b0c7920 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanRewritingFlags.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanRewritingFlags.java
@@ -4,8 +4,10 @@ package com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification; +import com.android.tools.r8.graph.DexField; import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.graph.FieldAccessFlags; import com.android.tools.r8.graph.MethodAccessFlags; import com.android.tools.r8.origin.Origin; import com.android.tools.r8.utils.Reporter; @@ -24,8 +26,10 @@ public class HumanRewritingFlags { private final Map<String, String> rewritePrefix; + private final Set<String> maintainPrefix; private final Map<String, Map<String, String>> rewriteDerivedPrefix; private final Map<DexType, DexType> emulatedInterfaces; + private final Map<DexField, DexType> retargetStaticField; private final Map<DexMethod, DexType> retargetMethod; private final Map<DexMethod, DexType> retargetMethodEmulatedDispatch; private final Map<DexType, DexType> legacyBackport; @@ -34,11 +38,14 @@ private final Set<DexType> dontRetarget; private final Map<DexType, Set<DexMethod>> wrapperConversions; private final Map<DexMethod, MethodAccessFlags> amendLibraryMethod; + private final Map<DexField, FieldAccessFlags> amendLibraryField; HumanRewritingFlags( Map<String, String> rewritePrefix, + Set<String> maintainPrefix, Map<String, Map<String, String>> rewriteDerivedPrefix, Map<DexType, DexType> emulateLibraryInterface, + Map<DexField, DexType> retargetStaticField, Map<DexMethod, DexType> retargetMethod, Map<DexMethod, DexType> retargetMethodEmulatedDispatch, Map<DexType, DexType> legacyBackport, @@ -46,10 +53,13 @@ Set<DexMethod> dontRewriteInvocation, Set<DexType> dontRetarget, Map<DexType, Set<DexMethod>> wrapperConversion, - Map<DexMethod, MethodAccessFlags> amendLibraryMethod) { + Map<DexMethod, MethodAccessFlags> amendLibraryMethod, + Map<DexField, FieldAccessFlags> amendLibraryField) { this.rewritePrefix = rewritePrefix; + this.maintainPrefix = maintainPrefix; this.rewriteDerivedPrefix = rewriteDerivedPrefix; this.emulatedInterfaces = emulateLibraryInterface; + this.retargetStaticField = retargetStaticField; this.retargetMethod = retargetMethod; this.retargetMethodEmulatedDispatch = retargetMethodEmulatedDispatch; this.legacyBackport = legacyBackport; @@ -58,11 +68,14 @@ this.dontRetarget = dontRetarget; this.wrapperConversions = wrapperConversion; this.amendLibraryMethod = amendLibraryMethod; + this.amendLibraryField = amendLibraryField; } public static HumanRewritingFlags empty() { return new HumanRewritingFlags( ImmutableMap.of(), + ImmutableSet.of(), + ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of(), @@ -72,6 +85,7 @@ ImmutableSet.of(), ImmutableSet.of(), ImmutableMap.of(), + ImmutableMap.of(), ImmutableMap.of()); } @@ -84,8 +98,10 @@ reporter, origin, rewritePrefix, + maintainPrefix, rewriteDerivedPrefix, emulatedInterfaces, + retargetStaticField, retargetMethod, retargetMethodEmulatedDispatch, legacyBackport, @@ -93,13 +109,18 @@ dontRewriteInvocation, dontRetarget, wrapperConversions, - amendLibraryMethod); + amendLibraryMethod, + amendLibraryField); } public Map<String, String> getRewritePrefix() { return rewritePrefix; } + public Set<String> getMaintainPrefix() { + return maintainPrefix; + } + public Map<String, Map<String, String>> getRewriteDerivedPrefix() { return rewriteDerivedPrefix; } @@ -108,6 +129,10 @@ return emulatedInterfaces; } + public Map<DexField, DexType> getRetargetStaticField() { + return retargetStaticField; + } + public Map<DexMethod, DexType> getRetargetMethod() { return retargetMethod; } @@ -140,11 +165,17 @@ return amendLibraryMethod; } + public Map<DexField, FieldAccessFlags> getAmendLibraryField() { + return amendLibraryField; + } + public boolean isEmpty() { return rewritePrefix.isEmpty() && rewriteDerivedPrefix.isEmpty() && emulatedInterfaces.isEmpty() - && retargetMethod.isEmpty(); + && retargetMethod.isEmpty() + && retargetMethodEmulatedDispatch.isEmpty() + && retargetStaticField.isEmpty(); } public static class Builder { @@ -153,8 +184,10 @@ private final Origin origin; private final Map<String, String> rewritePrefix; + private final Set<String> maintainPrefix; private final Map<String, Map<String, String>> rewriteDerivedPrefix; private final Map<DexType, DexType> emulatedInterfaces; + private final Map<DexField, DexType> retargetStaticField; private final Map<DexMethod, DexType> retargetMethod; private final Map<DexMethod, DexType> retargetMethodEmulatedDispatch; private final Map<DexType, DexType> legacyBackport; @@ -163,21 +196,25 @@ private final Set<DexType> dontRetarget; private final Map<DexType, Set<DexMethod>> wrapperConversions; private final Map<DexMethod, MethodAccessFlags> amendLibraryMethod; + private final Map<DexField, FieldAccessFlags> amendLibraryField; Builder(Reporter reporter, Origin origin) { this( reporter, origin, new HashMap<>(), + Sets.newIdentityHashSet(), new HashMap<>(), new IdentityHashMap<>(), new IdentityHashMap<>(), new IdentityHashMap<>(), new IdentityHashMap<>(), new IdentityHashMap<>(), + new IdentityHashMap<>(), Sets.newIdentityHashSet(), Sets.newIdentityHashSet(), new IdentityHashMap<>(), + new IdentityHashMap<>(), new IdentityHashMap<>()); } @@ -185,8 +222,10 @@ Reporter reporter, Origin origin, Map<String, String> rewritePrefix, + Set<String> maintainPrefix, Map<String, Map<String, String>> rewriteDerivedPrefix, Map<DexType, DexType> emulateLibraryInterface, + Map<DexField, DexType> retargetStaticField, Map<DexMethod, DexType> retargetMethod, Map<DexMethod, DexType> retargetMethodEmulatedDispatch, Map<DexType, DexType> backportCoreLibraryMember, @@ -194,12 +233,15 @@ Set<DexMethod> dontRewriteInvocation, Set<DexType> dontRetargetLibMember, Map<DexType, Set<DexMethod>> wrapperConversions, - Map<DexMethod, MethodAccessFlags> amendLibrary) { + Map<DexMethod, MethodAccessFlags> amendLibraryMethod, + Map<DexField, FieldAccessFlags> amendLibraryField) { this.reporter = reporter; this.origin = origin; this.rewritePrefix = new HashMap<>(rewritePrefix); + this.maintainPrefix = Sets.newHashSet(maintainPrefix); this.rewriteDerivedPrefix = new HashMap<>(rewriteDerivedPrefix); this.emulatedInterfaces = new IdentityHashMap<>(emulateLibraryInterface); + this.retargetStaticField = new IdentityHashMap<>(retargetStaticField); this.retargetMethod = new IdentityHashMap<>(retargetMethod); this.retargetMethodEmulatedDispatch = new IdentityHashMap<>(retargetMethodEmulatedDispatch); this.legacyBackport = new IdentityHashMap<>(backportCoreLibraryMember); @@ -209,7 +251,8 @@ this.dontRetarget = Sets.newIdentityHashSet(); this.dontRetarget.addAll(dontRetargetLibMember); this.wrapperConversions = new IdentityHashMap<>(wrapperConversions); - this.amendLibraryMethod = new IdentityHashMap<>(amendLibrary); + this.amendLibraryMethod = new IdentityHashMap<>(amendLibraryMethod); + this.amendLibraryField = new IdentityHashMap<>(amendLibraryField); } // Utility to set values. @@ -237,6 +280,11 @@ return this; } + public Builder putMaintainPrefix(String prefix) { + maintainPrefix.add(prefix); + return this; + } + public Builder putRewriteDerivedPrefix( String prefixToMatch, String prefixToRewrite, String rewrittenPrefix) { Map<String, String> map = @@ -285,6 +333,15 @@ return this; } + public Builder retargetStaticField(DexField key, DexType rewrittenType) { + put( + retargetStaticField, + key, + rewrittenType, + HumanDesugaredLibrarySpecificationParser.RETARGET_STATIC_FIELD_KEY); + return this; + } + public Builder retargetMethodEmulatedDispatch(DexMethod key, DexType rewrittenType) { put( retargetMethodEmulatedDispatch, @@ -318,12 +375,19 @@ return this; } + public Builder amendLibraryField(DexField member, FieldAccessFlags flags) { + amendLibraryField.put(member, flags); + return this; + } + public HumanRewritingFlags build() { validate(); return new HumanRewritingFlags( ImmutableMap.copyOf(rewritePrefix), + ImmutableSet.copyOf(maintainPrefix), ImmutableMap.copyOf(rewriteDerivedPrefix), ImmutableMap.copyOf(emulatedInterfaces), + ImmutableMap.copyOf(retargetStaticField), ImmutableMap.copyOf(retargetMethod), ImmutableMap.copyOf(retargetMethodEmulatedDispatch), ImmutableMap.copyOf(legacyBackport), @@ -331,7 +395,8 @@ ImmutableSet.copyOf(dontRewriteInvocation), ImmutableSet.copyOf(dontRetarget), ImmutableMap.copyOf(wrapperConversions), - ImmutableMap.copyOf(amendLibraryMethod)); + ImmutableMap.copyOf(amendLibraryMethod), + ImmutableMap.copyOf(amendLibraryField)); } private void validate() {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanTopLevelFlags.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanTopLevelFlags.java index 6f1f435..5383b69 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanTopLevelFlags.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanTopLevelFlags.java
@@ -47,7 +47,7 @@ public static HumanTopLevelFlags testing() { return new HumanTopLevelFlags( - AndroidApiLevel.R, "unused", "testing", null, true, ImmutableList.of()); + AndroidApiLevel.O, "unused", "testing", null, true, ImmutableList.of()); } public static Builder builder() {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/memberparser/AbstractFieldParser.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/memberparser/AbstractFieldParser.java new file mode 100644 index 0000000..8062063 --- /dev/null +++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/memberparser/AbstractFieldParser.java
@@ -0,0 +1,43 @@ +// Copyright (c) 2022, 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.ir.desugar.desugaredlibrary.humanspecification.memberparser; + +import com.android.tools.r8.errors.CompilationError; +import com.android.tools.r8.graph.DexItemFactory; +import com.android.tools.r8.graph.DexString; +import com.android.tools.r8.graph.DexType; + +/** Parse fields of the form: modifiers* fieldType holder#fieldName */ +public abstract class AbstractFieldParser extends AbstractMemberParser { + + protected AbstractFieldParser(DexItemFactory factory) { + super(factory); + } + + // TODO(b/218755060): It would be nice to avoid the split regexp and use a nextToken() + // method instead, then add a TraversalContinuation. + public void parseField(String signature) { + String[] tokens = signature.split(SEPARATORS); + if (tokens.length < 3) { + throw new CompilationError("Desugared library: cannot parse field " + signature); + } + fieldStart(); + int first = parseModifiers(tokens); + fieldType(stringTypeToDexType(tokens[first])); + holderType(stringTypeToDexType(tokens[first + 1])); + fieldName(factory.createString(tokens[first + 1 + 1])); + fieldEnd(); + } + + protected abstract void fieldStart(); + + protected abstract void fieldEnd(); + + protected abstract void fieldType(DexType type); + + protected abstract void holderType(DexType type); + + protected abstract void fieldName(DexString name); +}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/memberparser/AbstractMemberParser.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/memberparser/AbstractMemberParser.java new file mode 100644 index 0000000..99475b2 --- /dev/null +++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/memberparser/AbstractMemberParser.java
@@ -0,0 +1,48 @@ +// Copyright (c) 2022, 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.ir.desugar.desugaredlibrary.humanspecification.memberparser; + +import com.android.tools.r8.dex.Constants; +import com.android.tools.r8.graph.DexItemFactory; +import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.utils.DescriptorUtils; +import com.google.common.collect.ImmutableMap; +import java.util.Map; + +public abstract class AbstractMemberParser { + + static final String SEPARATORS = "\\s+|,\\s+|#|\\(|\\)"; + + static final Map<String, Integer> MODIFIERS = + ImmutableMap.<String, Integer>builder() + .put("public", Constants.ACC_PUBLIC) + .put("private", Constants.ACC_PRIVATE) + .put("protected", Constants.ACC_PROTECTED) + .put("final", Constants.ACC_FINAL) + .put("abstract", Constants.ACC_ABSTRACT) + .put("static", Constants.ACC_STATIC) + .build(); + + final DexItemFactory factory; + + protected AbstractMemberParser(DexItemFactory factory) { + this.factory = factory; + } + + DexType stringTypeToDexType(String stringType) { + return factory.createType(DescriptorUtils.javaTypeToDescriptor(stringType)); + } + + int parseModifiers(String[] split) { + int index = 0; + while (MODIFIERS.containsKey(split[index])) { + modifier(MODIFIERS.get(split[index])); + index++; + } + return index; + } + + protected abstract void modifier(int access); +}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/memberparser/AbstractMethodParser.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/memberparser/AbstractMethodParser.java new file mode 100644 index 0000000..1afff88 --- /dev/null +++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/memberparser/AbstractMethodParser.java
@@ -0,0 +1,48 @@ +// Copyright (c) 2022, 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.ir.desugar.desugaredlibrary.humanspecification.memberparser; + +import com.android.tools.r8.errors.CompilationError; +import com.android.tools.r8.graph.DexItemFactory; +import com.android.tools.r8.graph.DexString; +import com.android.tools.r8.graph.DexType; + +/** Parse methods of the form: modifiers* returnType holder#name(arg0, ..., argN) */ +public abstract class AbstractMethodParser extends AbstractMemberParser { + + protected AbstractMethodParser(DexItemFactory factory) { + super(factory); + } + + // TODO(b/218755060): It would be nice to avoid the split regexp and use a nextToken() + // method instead, then add a TraversalContinuation. + public void parseMethod(String signature) { + String[] tokens = signature.split(SEPARATORS); + if (tokens.length < 3) { + throw new CompilationError("Desugared library: cannot parse method " + signature); + } + methodStart(); + int first = parseModifiers(tokens); + returnType(stringTypeToDexType(tokens[first])); + holderType(stringTypeToDexType(tokens[first + 1])); + methodName(factory.createString(tokens[first + 1 + 1])); + for (int i = first + 3; i < tokens.length; i++) { + argType(stringTypeToDexType(tokens[i])); + } + methodEnd(); + } + + protected abstract void methodStart(); + + protected abstract void methodEnd(); + + protected abstract void returnType(DexType type); + + protected abstract void argType(DexType type); + + protected abstract void holderType(DexType type); + + protected abstract void methodName(DexString name); +}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/memberparser/HumanFieldParser.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/memberparser/HumanFieldParser.java new file mode 100644 index 0000000..9a5a44d --- /dev/null +++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/memberparser/HumanFieldParser.java
@@ -0,0 +1,81 @@ +// Copyright (c) 2022, 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.ir.desugar.desugaredlibrary.humanspecification.memberparser; + +import com.android.tools.r8.graph.DexField; +import com.android.tools.r8.graph.DexItemFactory; +import com.android.tools.r8.graph.DexString; +import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.graph.FieldAccessFlags; + +public class HumanFieldParser extends AbstractFieldParser { + + // Values accumulated while parsing. + private FieldAccessFlags.Builder flagBuilder; + private DexType fieldType; + private DexType holder; + private DexString fieldName; + // Resulting values. + private DexField field; + private FieldAccessFlags flags; + + public HumanFieldParser(DexItemFactory factory) { + super(factory); + } + + private boolean parsingFinished() { + return field != null; + } + + public DexField getField() { + assert parsingFinished(); + return field; + } + + public FieldAccessFlags getFlags() { + assert parsingFinished(); + return flags; + } + + @Override + protected void modifier(int access) { + assert !parsingFinished(); + flagBuilder.set(access); + } + + @Override + protected void holderType(DexType type) { + assert !parsingFinished(); + holder = type; + } + + @Override + protected void fieldName(DexString name) { + assert !parsingFinished(); + fieldName = name; + } + + @Override + protected void fieldStart() { + flagBuilder = FieldAccessFlags.builder(); + fieldType = null; + holder = null; + fieldName = null; + field = null; + flags = null; + } + + @Override + protected void fieldEnd() { + field = factory.createField(holder, fieldType, fieldName); + flags = flagBuilder.build(); + } + + @Override + protected void fieldType(DexType type) { + assert !parsingFinished(); + fieldType = type; + } +}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanMethodParser.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/memberparser/HumanMethodParser.java similarity index 96% rename from src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanMethodParser.java rename to src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/memberparser/HumanMethodParser.java index b4bac60..d78267b 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanMethodParser.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/memberparser/HumanMethodParser.java
@@ -2,7 +2,7 @@ // 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.ir.desugar.desugaredlibrary.humanspecification; +package com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.memberparser; import com.android.tools.r8.graph.DexItemFactory; import com.android.tools.r8.graph.DexMethod; @@ -25,7 +25,7 @@ private DexMethod method; private MethodAccessFlags flags; - protected HumanMethodParser(DexItemFactory factory) { + public HumanMethodParser(DexItemFactory factory) { super(factory); }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java index 1f52b3d..d974c56 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java
@@ -4,9 +4,11 @@ package com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification; +import com.android.tools.r8.graph.DexField; import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.DexReference; import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.graph.FieldAccessFlags; import com.android.tools.r8.graph.MethodAccessFlags; import com.android.tools.r8.utils.AndroidApiLevel; import com.android.tools.r8.utils.SemanticVersion; @@ -82,10 +84,18 @@ return rewritingFlags.getRewriteType(); } + public Set<DexType> getMaintainType() { + return rewritingFlags.getMaintainType(); + } + public Map<DexType, DexType> getRewriteDerivedTypeOnly() { return rewritingFlags.getRewriteDerivedTypeOnly(); } + public Map<DexField, DexField> getStaticFieldRetarget() { + return rewritingFlags.getStaticFieldRetarget(); + } + public Map<DexMethod, DexMethod> getStaticRetarget() { return rewritingFlags.getStaticRetarget(); } @@ -143,6 +153,10 @@ return rewritingFlags.getAmendLibraryMethod(); } + public Map<DexField, FieldAccessFlags> getAmendLibraryFields() { + return rewritingFlags.getAmendLibraryField(); + } + public boolean hasRetargeting() { return rewritingFlags.hasRetargeting(); }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineRewritingFlags.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineRewritingFlags.java index 00a4747..dcaa744 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineRewritingFlags.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineRewritingFlags.java
@@ -4,8 +4,10 @@ package com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification; +import com.android.tools.r8.graph.DexField; import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.graph.FieldAccessFlags; import com.android.tools.r8.graph.MethodAccessFlags; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; @@ -25,7 +27,9 @@ MachineRewritingFlags( Map<DexType, DexType> rewriteType, + Set<DexType> maintainType, Map<DexType, DexType> rewriteDerivedTypeOnly, + Map<DexField, DexField> staticFieldRetarget, Map<DexMethod, DexMethod> staticRetarget, Map<DexMethod, DexMethod> nonEmulatedVirtualRetarget, Map<DexMethod, EmulatedDispatchMethodDescriptor> emulatedVirtualRetarget, @@ -35,9 +39,12 @@ Map<DexType, DexType> legacyBackport, Set<DexType> dontRetarget, Map<DexType, CustomConversionDescriptor> customConversions, - Map<DexMethod, MethodAccessFlags> amendLibraryMethods) { + Map<DexMethod, MethodAccessFlags> amendLibraryMethods, + Map<DexField, FieldAccessFlags> amendLibraryFields) { this.rewriteType = rewriteType; + this.maintainType = maintainType; this.rewriteDerivedTypeOnly = rewriteDerivedTypeOnly; + this.staticFieldRetarget = staticFieldRetarget; this.staticRetarget = staticRetarget; this.nonEmulatedVirtualRetarget = nonEmulatedVirtualRetarget; this.emulatedVirtualRetarget = emulatedVirtualRetarget; @@ -49,14 +56,20 @@ this.dontRetarget = dontRetarget; this.customConversions = customConversions; this.amendLibraryMethod = amendLibraryMethods; + this.amendLibraryField = amendLibraryFields; } // Rewrites all the references to the keys as well as synthetic types derived from any key. private final Map<DexType, DexType> rewriteType; + // Maintains the references in the desugared library dex file. + private final Set<DexType> maintainType; // Rewrites only synthetic types derived from any key. private final Map<DexType, DexType> rewriteDerivedTypeOnly; - // Static methods to retarget, duplicated to library boundaries. + // Fields to retarget. + private final Map<DexField, DexField> staticFieldRetarget; + + // Static methods to retarget. private final Map<DexMethod, DexMethod> staticRetarget; // Virtual methods to retarget, which are guaranteed not to require emulated dispatch. @@ -83,15 +96,24 @@ private final Set<DexType> dontRetarget; private final Map<DexType, CustomConversionDescriptor> customConversions; private final Map<DexMethod, MethodAccessFlags> amendLibraryMethod; + private final Map<DexField, FieldAccessFlags> amendLibraryField; public Map<DexType, DexType> getRewriteType() { return rewriteType; } + public Set<DexType> getMaintainType() { + return maintainType; + } + public Map<DexType, DexType> getRewriteDerivedTypeOnly() { return rewriteDerivedTypeOnly; } + public Map<DexField, DexField> getStaticFieldRetarget() { + return staticFieldRetarget; + } + public Map<DexMethod, DexMethod> getStaticRetarget() { return staticRetarget; } @@ -146,10 +168,15 @@ return amendLibraryMethod; } + public Map<DexField, FieldAccessFlags> getAmendLibraryField() { + return amendLibraryField; + } + public boolean hasRetargeting() { return !staticRetarget.isEmpty() || !nonEmulatedVirtualRetarget.isEmpty() - || !emulatedVirtualRetarget.isEmpty(); + || !emulatedVirtualRetarget.isEmpty() + || !staticFieldRetarget.isEmpty(); } public boolean isEmulatedInterfaceRewrittenType(DexType type) { @@ -174,7 +201,10 @@ Builder() {} private final Map<DexType, DexType> rewriteType = new IdentityHashMap<>(); + private final ImmutableSet.Builder<DexType> maintainType = ImmutableSet.builder(); private final Map<DexType, DexType> rewriteDerivedTypeOnly = new IdentityHashMap<>(); + private final ImmutableMap.Builder<DexField, DexField> staticFieldRetarget = + ImmutableMap.builder(); private final ImmutableMap.Builder<DexMethod, DexMethod> staticRetarget = ImmutableMap.builder(); private final ImmutableMap.Builder<DexMethod, DexMethod> nonEmulatedVirtualRetarget = @@ -192,6 +222,8 @@ ImmutableMap.builder(); private final ImmutableMap.Builder<DexMethod, MethodAccessFlags> amendLibraryMethod = ImmutableMap.builder(); + private final ImmutableMap.Builder<DexField, FieldAccessFlags> amendLibraryField = + ImmutableMap.builder(); public void rewriteType(DexType src, DexType target) { assert src != null; @@ -201,10 +233,19 @@ rewriteType.put(src, target); } + public void maintainType(DexType type) { + assert type != null; + maintainType.add(type); + } + public void rewriteDerivedTypeOnly(DexType src, DexType target) { rewriteDerivedTypeOnly.put(src, target); } + public void putStaticFieldRetarget(DexField src, DexField dest) { + staticFieldRetarget.put(src, dest); + } + public void putStaticRetarget(DexMethod src, DexMethod dest) { staticRetarget.put(src, dest); } @@ -245,6 +286,10 @@ amendLibraryMethod.put(missingReference, flags); } + public void amendLibraryField(DexField missingReference, FieldAccessFlags flags) { + amendLibraryField.put(missingReference, flags); + } + public DexType getRewrittenType(DexType type) { return rewriteType.get(type); } @@ -252,7 +297,9 @@ public MachineRewritingFlags build() { return new MachineRewritingFlags( rewriteType, + maintainType.build(), rewriteDerivedTypeOnly, + staticFieldRetarget.build(), staticRetarget.build(), nonEmulatedVirtualRetarget.build(), emulatedVirtualRetarget.build(), @@ -262,7 +309,8 @@ legacyBackport.build(), dontRetarget.build(), customConversions.build(), - amendLibraryMethod.build()); + amendLibraryMethod.build(), + amendLibraryField.build()); } } }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeter.java index a4b984f..a929fb9 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeter.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeter.java
@@ -6,12 +6,15 @@ import static com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.DesugaredLibraryRetargeter.InvokeRetargetingResult.NO_REWRITING; +import com.android.tools.r8.cf.code.CfFieldInstruction; import com.android.tools.r8.cf.code.CfInstruction; import com.android.tools.r8.cf.code.CfInvoke; import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext; import com.android.tools.r8.graph.AppInfoWithClassHierarchy; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexClassAndMethod; +import com.android.tools.r8.graph.DexEncodedField; +import com.android.tools.r8.graph.DexField; import com.android.tools.r8.graph.DexItemFactory; import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.MethodResolutionResult; @@ -26,6 +29,7 @@ import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.DesugaredLibraryRetargeterSynthesizerEventConsumer.DesugaredLibraryRetargeterInstructionEventConsumer; import java.util.Collection; import java.util.Collections; +import java.util.List; import java.util.Map; import java.util.function.Consumer; import java.util.function.Function; @@ -36,6 +40,7 @@ private final AppView<?> appView; private final DesugaredLibraryRetargeterSyntheticHelper syntheticHelper; + private final Map<DexField, DexField> staticFieldRetarget; private final Map<DexMethod, DexMethod> staticRetarget; private final Map<DexMethod, DexMethod> nonEmulatedVirtualRetarget; private final Map<DexMethod, EmulatedDispatchMethodDescriptor> emulatedVirtualRetarget; @@ -45,6 +50,7 @@ this.syntheticHelper = new DesugaredLibraryRetargeterSyntheticHelper(appView); MachineDesugaredLibrarySpecification specification = appView.options().machineDesugaredLibrarySpecification; + staticFieldRetarget = specification.getStaticFieldRetarget(); staticRetarget = specification.getStaticRetarget(); nonEmulatedVirtualRetarget = specification.getNonEmulatedVirtualRetarget(); emulatedVirtualRetarget = specification.getEmulatedVirtualRetarget(); @@ -67,20 +73,53 @@ MethodProcessingContext methodProcessingContext, CfInstructionDesugaringCollection desugaringCollection, DexItemFactory dexItemFactory) { - InvokeRetargetingResult invokeRetargetingResult = computeNewInvokeTarget(instruction, context); - - if (!invokeRetargetingResult.hasNewInvokeTarget()) { - return null; + if (instruction.isFieldInstruction() && needsDesugaring(instruction, context)) { + return desugarFieldInstruction(instruction.asFieldInstruction(), context); + } else if (instruction.isInvoke() && needsDesugaring(instruction, context)) { + return desugarInvoke(instruction.asInvoke(), eventConsumer, context); } + return null; + } + private Collection<CfInstruction> desugarFieldInstruction( + CfFieldInstruction fieldInstruction, ProgramMethod context) { + DexField fieldRetarget = fieldRetarget(fieldInstruction, context); + assert fieldRetarget != null; + assert fieldInstruction.isStaticFieldGet() || fieldInstruction.isStaticFieldPut(); + return Collections.singletonList(fieldInstruction.createWithField(fieldRetarget)); + } + + private List<CfInstruction> desugarInvoke( + CfInvoke invoke, CfInstructionDesugaringEventConsumer eventConsumer, ProgramMethod context) { + InvokeRetargetingResult invokeRetargetingResult = computeNewInvokeTarget(invoke, context); + assert invokeRetargetingResult.hasNewInvokeTarget(); DexMethod newInvokeTarget = invokeRetargetingResult.getNewInvokeTarget(eventConsumer); return Collections.singletonList( - new CfInvoke(Opcodes.INVOKESTATIC, newInvokeTarget, instruction.asInvoke().isInterface())); + new CfInvoke(Opcodes.INVOKESTATIC, newInvokeTarget, invoke.isInterface())); } @Override public boolean needsDesugaring(CfInstruction instruction, ProgramMethod context) { - return computeNewInvokeTarget(instruction, context).hasNewInvokeTarget(); + if (instruction.isFieldInstruction()) { + return fieldRetarget(instruction.asFieldInstruction(), context) != null; + } else if (instruction.isInvoke()) { + return computeNewInvokeTarget(instruction.asInvoke(), context).hasNewInvokeTarget(); + } + return false; + } + + private DexField fieldRetarget(CfFieldInstruction fieldInstruction, ProgramMethod context) { + DexEncodedField resolvedField = + appView + .appInfoForDesugaring() + .resolveField(fieldInstruction.getField(), context) + .getResolvedField(); + if (resolvedField != null) { + assert resolvedField.isStatic() + || !staticFieldRetarget.containsKey(resolvedField.getReference()); + return staticFieldRetarget.get(resolvedField.getReference()); + } + return null; } InvokeRetargetingResult ensureInvokeRetargetingResult(DexMethod retarget) { @@ -124,10 +163,7 @@ } private InvokeRetargetingResult computeNewInvokeTarget( - CfInstruction instruction, ProgramMethod context) { - if (!instruction.isInvoke()) { - return NO_REWRITING; - } + CfInvoke instruction, ProgramMethod context) { if (appView .options() .machineDesugaredLibrarySpecification
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachinePrefixConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachinePrefixConverter.java index 87c03e3..78fc59f 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachinePrefixConverter.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachinePrefixConverter.java
@@ -13,6 +13,7 @@ import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineRewritingFlags; import com.android.tools.r8.utils.DescriptorUtils; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; import java.util.Map; import java.util.Set; @@ -25,6 +26,7 @@ private final String synthesizedPrefix; private final boolean libraryCompilation; private final Map<DexString, DexString> descriptorPrefix; + private final Set<DexString> descriptorMaintainPrefix; private final Map<DexString, Map<DexString, DexString>> descriptorDifferentPrefix; private final Set<DexString> usedPrefix = Sets.newIdentityHashSet(); @@ -38,6 +40,7 @@ this.synthesizedPrefix = humanSpec.getSynthesizedLibraryClassesPackagePrefix(); this.libraryCompilation = humanSpec.isLibraryCompilation(); this.descriptorPrefix = convertRewritePrefix(rewritingFlags.getRewritePrefix()); + this.descriptorMaintainPrefix = convertMaintainPrefix(rewritingFlags.getMaintainPrefix()); this.descriptorDifferentPrefix = convertRewriteDifferentPrefix(rewritingFlags.getRewriteDerivedPrefix()); } @@ -56,6 +59,7 @@ private void warnIfUnusedPrefix(BiConsumer<String, Set<DexString>> warnConsumer) { Set<DexString> prefixes = Sets.newIdentityHashSet(); prefixes.addAll(descriptorPrefix.keySet()); + prefixes.addAll(descriptorMaintainPrefix); prefixes.addAll(descriptorDifferentPrefix.keySet()); prefixes.removeAll(usedPrefix); warnConsumer.accept("The following prefixes do not match any type: ", prefixes); @@ -99,6 +103,7 @@ private void registerClassType(DexType type) { registerType(type); + registerMaintainType(type); registerDifferentType(type); } @@ -109,6 +114,15 @@ } } + private void registerMaintainType(DexType type) { + DexString prefix = prefixMatching(type, descriptorMaintainPrefix); + if (prefix == null) { + return; + } + builder.maintainType(type); + usedPrefix.add(prefix); + } + private void registerDifferentType(DexType type) { DexString prefix = prefixMatching(type, descriptorDifferentPrefix.keySet()); if (prefix == null) { @@ -160,6 +174,14 @@ return mapBuilder.build(); } + private ImmutableSet<DexString> convertMaintainPrefix(Set<String> maintainPrefix) { + ImmutableSet.Builder<DexString> builder = ImmutableSet.builder(); + for (String prefix : maintainPrefix) { + builder.add(toDescriptorPrefix(prefix)); + } + return builder.build(); + } + private ImmutableMap<DexString, DexString> convertRewritePrefix( Map<String, String> rewritePrefix) { ImmutableMap.Builder<DexString, DexString> mapBuilder = ImmutableMap.builder();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineRetargetConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineRetargetConverter.java index f64a3d0..56af1fc 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineRetargetConverter.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineRetargetConverter.java
@@ -6,7 +6,9 @@ import com.android.tools.r8.graph.AppInfoWithClassHierarchy; import com.android.tools.r8.graph.DexClass; +import com.android.tools.r8.graph.DexEncodedField; import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.DexField; import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.DexProto; import com.android.tools.r8.graph.DexReference; @@ -26,7 +28,7 @@ public class HumanToMachineRetargetConverter { private final AppInfoWithClassHierarchy appInfo; - private final Set<DexMethod> missingMethods = Sets.newIdentityHashSet(); + private final Set<DexReference> missingReferences = Sets.newIdentityHashSet(); public HumanToMachineRetargetConverter(AppInfoWithClassHierarchy appInfo) { this.appInfo = appInfo; @@ -37,6 +39,9 @@ MachineRewritingFlags.Builder builder, BiConsumer<String, Set<? extends DexReference>> warnConsumer) { rewritingFlags + .getRetargetStaticField() + .forEach((field, type) -> convertRetargetField(builder, field, type)); + rewritingFlags .getRetargetMethod() .forEach((method, type) -> convertRetargetMethod(builder, method, type)); rewritingFlags @@ -44,7 +49,19 @@ .forEach( (method, type) -> convertRetargetMethodEmulatedDispatch(builder, rewritingFlags, method, type)); - warnConsumer.accept("Cannot retarget missing methods: ", missingMethods); + warnConsumer.accept("Cannot retarget missing references: ", missingReferences); + } + + private void convertRetargetField( + MachineRewritingFlags.Builder builder, DexField field, DexType type) { + DexClass holder = appInfo.definitionFor(field.holder); + DexEncodedField foundField = holder.lookupField(field); + if (foundField == null) { + missingReferences.add(field); + return; + } + builder.putStaticFieldRetarget( + field, appInfo.dexItemFactory().createField(type, field.type, field.name)); } private void convertRetargetMethodEmulatedDispatch( @@ -55,7 +72,7 @@ DexClass holder = appInfo.definitionFor(method.holder); DexEncodedMethod foundMethod = holder.lookupMethod(method); if (foundMethod == null) { - missingMethods.add(method); + missingReferences.add(method); return; } if (foundMethod.isStatic()) { @@ -83,7 +100,7 @@ DexClass holder = appInfo.definitionFor(method.holder); DexEncodedMethod foundMethod = holder.lookupMethod(method); if (foundMethod == null) { - missingMethods.add(method); + missingReferences.add(method); return; } if (foundMethod.isStatic()) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineSpecificationConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineSpecificationConverter.java index 5ea225d..0cec776 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineSpecificationConverter.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineSpecificationConverter.java
@@ -86,8 +86,13 @@ HumanRewritingFlags rewritingFlags = humanSpec.getRewritingFlags(); MachineRewritingFlags.Builder builder = MachineRewritingFlags.builder(); DesugaredLibraryAmender.run( - rewritingFlags.getAmendLibraryMethod(), appInfo, reporter, ComputedApiLevel.unknown()); + rewritingFlags.getAmendLibraryMethod(), + rewritingFlags.getAmendLibraryField(), + appInfo, + reporter, + ComputedApiLevel.unknown()); rewritingFlags.getAmendLibraryMethod().forEach(builder::amendLibraryMethod); + rewritingFlags.getAmendLibraryField().forEach(builder::amendLibraryField); new HumanToMachineRetargetConverter(appInfo) .convertRetargetFlags(rewritingFlags, builder, this::warnMissingReferences); new HumanToMachineEmulatedInterfaceConverter(appInfo)
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java index b1c59bc..4446275 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
@@ -24,6 +24,7 @@ import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.GenericSignature; import com.android.tools.r8.graph.GenericSignature.ClassTypeSignature; +import com.android.tools.r8.graph.LookupMethodTarget; import com.android.tools.r8.graph.MethodAccessFlags; import com.android.tools.r8.graph.MethodResolutionResult; import com.android.tools.r8.graph.ProgramMethod; @@ -717,8 +718,9 @@ } } assert resolutionResult.isSuccessfulMemberResolutionResult(); - DexClassAndMethod virtualDispatchTarget = + LookupMethodTarget lookupMethodTarget = resolutionResult.lookupVirtualDispatchTarget(clazz, appInfo); + DexClassAndMethod virtualDispatchTarget = lookupMethodTarget.getTarget(); assert virtualDispatchTarget != null; // If resolution targets a default interface method, forward it.
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java index dc739bb..3919202 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java
@@ -98,6 +98,13 @@ if (isEmulatedInterface(clazz.type)) { return true; } + if (appView + .options() + .machineDesugaredLibrarySpecification + .getMaintainType() + .contains(clazz.type)) { + return true; + } return appView.typeRewriter.hasRewrittenType(clazz.type, 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 2ae7d7d..778ed23 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
@@ -713,7 +713,10 @@ DexClass clazz, DexMethod invokedMethod, ProgramMethod context) { DexClassAndMethod superTarget = appView.appInfoForDesugaring().lookupSuperTarget(invokedMethod, context); - if (clazz.isInterface() && appView.typeRewriter.hasRewrittenType(clazz.type, appView)) { + if (clazz.isInterface() + && clazz.isLibraryClass() + && helper.isInDesugaredLibrary(clazz) + && !helper.isEmulatedInterface(clazz.type)) { if (superTarget != null && superTarget.getDefinition().isDefaultMethod()) { DexClass holder = superTarget.getHolder(); if (holder.isLibraryClass() && holder.isInterface()) {
diff --git a/src/main/java/com/android/tools/r8/optimize/interfaces/collection/DefaultOpenClosedInterfacesCollection.java b/src/main/java/com/android/tools/r8/optimize/interfaces/collection/DefaultOpenClosedInterfacesCollection.java index df264e4..f8aeb81 100644 --- a/src/main/java/com/android/tools/r8/optimize/interfaces/collection/DefaultOpenClosedInterfacesCollection.java +++ b/src/main/java/com/android/tools/r8/optimize/interfaces/collection/DefaultOpenClosedInterfacesCollection.java
@@ -22,7 +22,8 @@ @Override public boolean isDefinitelyClosed(DexClass clazz) { - return false; + // TODO(b/214496607): Should return false. + return true; } @Override
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java index 8b8e13b..eac02d2 100644 --- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java +++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -36,6 +36,7 @@ import com.android.tools.r8.graph.GraphLens; import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens; import com.android.tools.r8.graph.InstantiatedSubTypeInfo; +import com.android.tools.r8.graph.LookupMethodTarget; import com.android.tools.r8.graph.LookupResult.LookupResultSuccess; import com.android.tools.r8.graph.LookupTarget; import com.android.tools.r8.graph.MethodAccessInfoCollection; @@ -1467,17 +1468,17 @@ if (receiverLowerBoundType != null && receiverLowerBoundType.getClassType() == refinedReceiverType) { if (refinedReceiverClass.isProgramClass()) { - DexClassAndMethod clazzAndMethod = + LookupMethodTarget methodTarget = resolution.lookupVirtualDispatchTarget(refinedReceiverClass.asProgramClass(), this); - if (clazzAndMethod == null - || (clazzAndMethod.isProgramMethod() + if (methodTarget == null + || (methodTarget.getTarget().isProgramMethod() && !getKeepInfo() - .getMethodInfo(clazzAndMethod.asProgramMethod()) + .getMethodInfo(methodTarget.getTarget().asProgramMethod()) .isOptimizationAllowed(options()))) { // TODO(b/150640456): We should maybe only consider program methods. return DexEncodedMethod.SENTINEL; } - return clazzAndMethod.getDefinition(); + return methodTarget.getDefinition(); } else { // TODO(b/150640456): We should maybe only consider program methods. // If we resolved to a method on the refined receiver in the library, then we report the
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 c7917ce..7d54b09 100644 --- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java +++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -66,6 +66,7 @@ import com.android.tools.r8.graph.InnerClassAttribute; import com.android.tools.r8.graph.InvalidCode; import com.android.tools.r8.graph.LookupLambdaTarget; +import com.android.tools.r8.graph.LookupMethodTarget; import com.android.tools.r8.graph.LookupResult; import com.android.tools.r8.graph.LookupTarget; import com.android.tools.r8.graph.MethodAccessInfoCollection; @@ -3277,20 +3278,25 @@ private void markVirtualDispatchTargetAsLive( LookupTarget target, Function<ProgramMethod, KeepReasonWitness> reason) { target.accept( - method -> markVirtualDispatchTargetAsLive(method, reason), - lambda -> markVirtualDispatchTargetAsLive(lambda, reason)); + method -> markVirtualDispatchMethodTargetAsLive(method, reason), + lambda -> markVirtualDispatchLambdaTargetAsLive(lambda, reason)); analyses.forEach(analysis -> analysis.notifyMarkVirtualDispatchTargetAsLive(target, workList)); } - private void markVirtualDispatchTargetAsLive( - DexClassAndMethod target, Function<ProgramMethod, KeepReasonWitness> reason) { - ProgramMethod programMethod = target.asProgramMethod(); + private void markVirtualDispatchMethodTargetAsLive( + LookupMethodTarget target, Function<ProgramMethod, KeepReasonWitness> reason) { + ProgramMethod programMethod = target.getTarget().asProgramMethod(); if (programMethod != null && !programMethod.getDefinition().isAbstract()) { - markVirtualMethodAsLive(programMethod, reason.apply(programMethod)); + KeepReasonWitness appliedReason = reason.apply(programMethod); + markVirtualMethodAsLive(programMethod, appliedReason); + DexClassAndMethod accessOverride = target.getAccessOverride(); + if (accessOverride != null && accessOverride.isProgramMethod()) { + markMethodAsTargeted(accessOverride.asProgramMethod(), appliedReason); + } } } - private void markVirtualDispatchTargetAsLive( + private void markVirtualDispatchLambdaTargetAsLive( LookupLambdaTarget target, Function<ProgramMethod, KeepReasonWitness> reason) { ProgramMethod implementationMethod = target.getImplementationMethod().asProgramMethod(); if (implementationMethod != null) { @@ -4434,7 +4440,7 @@ if (override.isLambdaTarget()) { return true; } - ProgramMethod programMethod = override.asMethodTarget().asProgramMethod(); + ProgramMethod programMethod = override.asMethodTarget().getTarget().asProgramMethod(); if (programMethod == null) { return false; }
diff --git a/src/main/java/com/android/tools/r8/shaking/L8TreePruner.java b/src/main/java/com/android/tools/r8/shaking/L8TreePruner.java index 46e77e8..d462818 100644 --- a/src/main/java/com/android/tools/r8/shaking/L8TreePruner.java +++ b/src/main/java/com/android/tools/r8/shaking/L8TreePruner.java
@@ -9,7 +9,6 @@ import com.android.tools.r8.graph.DexType; import com.android.tools.r8.ir.desugar.TypeRewriter; import com.android.tools.r8.utils.InternalOptions; -import com.google.common.collect.Sets; import java.util.ArrayList; import java.util.IdentityHashMap; import java.util.List; @@ -22,25 +21,24 @@ public class L8TreePruner { private final InternalOptions options; - private final Set<DexType> emulatedInterfaces = Sets.newIdentityHashSet(); - private final Set<DexType> backports = Sets.newIdentityHashSet(); private final List<DexType> pruned = new ArrayList<>(); public L8TreePruner(InternalOptions options) { this.options = options; - backports.addAll(options.machineDesugaredLibrarySpecification.getLegacyBackport().keySet()); - emulatedInterfaces.addAll( - options.machineDesugaredLibrarySpecification.getEmulatedInterfaces().keySet()); } public DexApplication prune(DexApplication app, TypeRewriter typeRewriter) { + Set<DexType> maintainType = options.machineDesugaredLibrarySpecification.getMaintainType(); + Set<DexType> emulatedInterfaces = + options.machineDesugaredLibrarySpecification.getEmulatedInterfaces().keySet(); Map<DexType, DexProgramClass> typeMap = new IdentityHashMap<>(); List<DexProgramClass> toKeep = new ArrayList<>(); boolean pruneNestMember = false; for (DexProgramClass aClass : app.classes()) { typeMap.put(aClass.type, aClass); if (typeRewriter.hasRewrittenType(aClass.type, null) - || emulatedInterfaces.contains(aClass.type)) { + || emulatedInterfaces.contains(aClass.type) + || maintainType.contains(aClass.type)) { toKeep.add(aClass); } else { pruneNestMember |= aClass.isInANest();
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticMarker.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticMarker.java index ceb9a37..67dbe6a 100644 --- a/src/main/java/com/android/tools/r8/synthesis/SyntheticMarker.java +++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticMarker.java
@@ -14,6 +14,7 @@ import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind; import com.android.tools.r8.utils.DescriptorUtils; import com.android.tools.r8.utils.InternalOptions; +import java.nio.charset.StandardCharsets; import org.objectweb.asm.Attribute; import org.objectweb.asm.ByteVector; import org.objectweb.asm.ClassReader; @@ -23,33 +24,51 @@ public class SyntheticMarker { private static final String SYNTHETIC_MARKER_ATTRIBUTE_TYPE_NAME = - "com.android.tools.r8.SynthesizedClass"; + "com.android.tools.r8.SynthesizedClassV2"; public static Attribute getMarkerAttributePrototype(SyntheticNaming syntheticNaming) { - return new MarkerAttribute(null, syntheticNaming); + return new MarkerAttribute(null, null, syntheticNaming); } public static void writeMarkerAttribute( ClassWriter writer, SyntheticKind kind, SyntheticItems syntheticItems) { - writer.visitAttribute(new MarkerAttribute(kind, syntheticItems.getNaming())); + SyntheticNaming naming = syntheticItems.getNaming(); + writer.visitAttribute(new MarkerAttribute(kind, naming.getVersionHash(), naming)); } public static SyntheticMarker readMarkerAttribute(Attribute attribute) { if (attribute instanceof MarkerAttribute) { MarkerAttribute marker = (MarkerAttribute) attribute; - return new SyntheticMarker(marker.kind, null); + if (marker.versionHash.equals(marker.syntheticNaming.getVersionHash())) { + return new SyntheticMarker(marker.kind, null); + } } return null; } + /** + * CF attribute for marking synthetic classes. + * + * <p>The attribute name is defined by {@code SYNTHETIC_MARKER_ATTRIBUTE_TYPE_NAME}. The format of + * the attribute payload is + * + * <pre> + * u2 syntheticKindId + * u2 versionHashLength + * u1[versionHashLength] versionHashBytes + * </pre> + */ private static class MarkerAttribute extends Attribute { - private SyntheticKind kind; + private final SyntheticKind kind; + private final String versionHash; private final SyntheticNaming syntheticNaming; - public MarkerAttribute(SyntheticKind kind, SyntheticNaming syntheticNaming) { + public MarkerAttribute( + SyntheticKind kind, String versionHash, SyntheticNaming syntheticNaming) { super(SYNTHETIC_MARKER_ATTRIBUTE_TYPE_NAME); this.kind = kind; + this.versionHash = versionHash; this.syntheticNaming = syntheticNaming; } @@ -61,18 +80,29 @@ char[] charBuffer, int codeAttributeOffset, Label[] labels) { - short id = classReader.readShort(offset); - assert id >= 0; - SyntheticKind kind = syntheticNaming.fromId(id); - return new MarkerAttribute(kind, syntheticNaming); + short syntheticKindId = classReader.readShort(offset); + offset += 2; + short versionHashLength = classReader.readShort(offset); + offset += 2; + byte[] versionHashBytes = new byte[versionHashLength]; + for (int i = 0; i < versionHashLength; i++) { + versionHashBytes[i] = (byte) classReader.readByte(offset++); + } + assert syntheticKindId >= 0; + SyntheticKind kind = syntheticNaming.fromId(syntheticKindId); + String versionHash = new String(versionHashBytes, StandardCharsets.UTF_8); + return new MarkerAttribute(kind, versionHash, syntheticNaming); } @Override protected ByteVector write( ClassWriter classWriter, byte[] code, int codeLength, int maxStack, int maxLocals) { - ByteVector byteVector = new ByteVector(); assert 0 <= kind.getId() && kind.getId() <= Short.MAX_VALUE; + ByteVector byteVector = new ByteVector(); byteVector.putShort(kind.getId()); + byte[] versionHashBytes = versionHash.getBytes(StandardCharsets.UTF_8); + byteVector.putShort(versionHashBytes.length); + byteVector.putByteArray(versionHashBytes, 0, versionHashBytes.length); return byteVector; } }
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java index edc591e..3559fe3 100644 --- a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java +++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
@@ -3,6 +3,7 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.synthesis; +import com.android.tools.r8.Version; import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.graph.DexItemFactory; import com.android.tools.r8.graph.DexType; @@ -12,131 +13,143 @@ import com.android.tools.r8.utils.DescriptorUtils; import com.android.tools.r8.utils.structural.Equatable; import com.android.tools.r8.utils.structural.Ordered; -import it.unimi.dsi.fastutil.ints.IntArraySet; -import it.unimi.dsi.fastutil.ints.IntSet; +import com.google.common.hash.Hasher; +import com.google.common.hash.Hashing; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collection; import java.util.List; public class SyntheticNaming { - public SyntheticNaming() {} - private KindGenerator generator = new KindGenerator(); // Global synthetics. - public final SyntheticKind RECORD_TAG = generator.forGlobalClass(1); - public final SyntheticKind API_MODEL_STUB = generator.forGlobalClass(33); + public final SyntheticKind RECORD_TAG = generator.forGlobalClass(); + public final SyntheticKind API_MODEL_STUB = generator.forGlobalClass(); // Classpath only synthetics in the global type namespace. - public final SyntheticKind RETARGET_STUB = generator.forGlobalClasspathClass(36); - public final SyntheticKind EMULATED_INTERFACE_MARKER_CLASS = - generator.forGlobalClasspathClass(29); + public final SyntheticKind RETARGET_STUB = generator.forGlobalClasspathClass(); + public final SyntheticKind EMULATED_INTERFACE_MARKER_CLASS = generator.forGlobalClasspathClass(); // Fixed suffix synthetics. Each has a hygienic prefix type. public final SyntheticKind ENUM_UNBOXING_LOCAL_UTILITY_CLASS = - generator.forFixedClass(24, "$EnumUnboxingLocalUtility"); + generator.forFixedClass("$EnumUnboxingLocalUtility"); public final SyntheticKind ENUM_UNBOXING_SHARED_UTILITY_CLASS = - generator.forFixedClass(25, "$EnumUnboxingSharedUtility"); - public final SyntheticKind COMPANION_CLASS = generator.forFixedClass(2, "$-CC"); + generator.forFixedClass("$EnumUnboxingSharedUtility"); + public final SyntheticKind COMPANION_CLASS = generator.forFixedClass("$-CC"); public final SyntheticKind EMULATED_INTERFACE_CLASS = - generator.forFixedClass(3, InterfaceDesugaringForTesting.EMULATED_INTERFACE_CLASS_SUFFIX); - public final SyntheticKind RETARGET_CLASS = generator.forFixedClass(20, "RetargetClass"); - public final SyntheticKind RETARGET_INTERFACE = generator.forFixedClass(21, "RetargetInterface"); - public final SyntheticKind WRAPPER = generator.forFixedClass(22, "$Wrapper"); - public final SyntheticKind VIVIFIED_WRAPPER = generator.forFixedClass(23, "$VivifiedWrapper"); - public final SyntheticKind INIT_TYPE_ARGUMENT = generator.forFixedClass(5, "-IA"); + generator.forFixedClass(InterfaceDesugaringForTesting.EMULATED_INTERFACE_CLASS_SUFFIX); + public final SyntheticKind RETARGET_CLASS = generator.forFixedClass("RetargetClass"); + public final SyntheticKind RETARGET_INTERFACE = generator.forFixedClass("RetargetInterface"); + public final SyntheticKind WRAPPER = generator.forFixedClass("$Wrapper"); + public final SyntheticKind VIVIFIED_WRAPPER = generator.forFixedClass("$VivifiedWrapper"); + public final SyntheticKind INIT_TYPE_ARGUMENT = generator.forFixedClass("-IA"); public final SyntheticKind HORIZONTAL_INIT_TYPE_ARGUMENT_1 = - generator.forFixedClass(6, SYNTHETIC_CLASS_SEPARATOR + "IA$1"); + generator.forFixedClass(SYNTHETIC_CLASS_SEPARATOR + "IA$1"); public final SyntheticKind HORIZONTAL_INIT_TYPE_ARGUMENT_2 = - generator.forFixedClass(7, SYNTHETIC_CLASS_SEPARATOR + "IA$2"); + generator.forFixedClass(SYNTHETIC_CLASS_SEPARATOR + "IA$2"); public final SyntheticKind HORIZONTAL_INIT_TYPE_ARGUMENT_3 = - generator.forFixedClass(8, SYNTHETIC_CLASS_SEPARATOR + "IA$3"); - public final SyntheticKind ENUM_CONVERSION = generator.forFixedClass(31, "$EnumConversion"); + generator.forFixedClass(SYNTHETIC_CLASS_SEPARATOR + "IA$3"); + public final SyntheticKind ENUM_CONVERSION = generator.forFixedClass("$EnumConversion"); // Locally generated synthetic classes. - public final SyntheticKind LAMBDA = generator.forInstanceClass(4, "Lambda"); + public final SyntheticKind LAMBDA = generator.forInstanceClass("Lambda"); // TODO(b/214901256): Sharing of synthetic classes may lead to duplicate method errors. public final SyntheticKind NON_FIXED_INIT_TYPE_ARGUMENT = - generator.forNonSharableInstanceClass(35, "$IA"); - public final SyntheticKind CONST_DYNAMIC = generator.forInstanceClass(30, "$Condy"); + generator.forNonSharableInstanceClass("$IA"); + public final SyntheticKind CONST_DYNAMIC = generator.forInstanceClass("$Condy"); // Method synthetics. public final SyntheticKind ENUM_UNBOXING_CHECK_NOT_ZERO_METHOD = - generator.forSingleMethod(27, "CheckNotZero"); - public final SyntheticKind RECORD_HELPER = generator.forSingleMethod(9, "Record"); - public final SyntheticKind BACKPORT = generator.forSingleMethod(10, "Backport"); + generator.forSingleMethod("CheckNotZero"); + public final SyntheticKind RECORD_HELPER = generator.forSingleMethod("Record"); + public final SyntheticKind BACKPORT = generator.forSingleMethod("Backport"); public final SyntheticKind BACKPORT_WITH_FORWARDING = - generator.forSingleMethod(34, "BackportWithForwarding"); + generator.forSingleMethod("BackportWithForwarding"); public final SyntheticKind STATIC_INTERFACE_CALL = - generator.forSingleMethod(11, "StaticInterfaceCall"); - public final SyntheticKind TO_STRING_IF_NOT_NULL = - generator.forSingleMethod(12, "ToStringIfNotNull"); - public final SyntheticKind THROW_CCE_IF_NOT_NULL = - generator.forSingleMethod(13, "ThrowCCEIfNotNull"); - public final SyntheticKind THROW_IAE = generator.forSingleMethod(14, "ThrowIAE"); - public final SyntheticKind THROW_ICCE = generator.forSingleMethod(15, "ThrowICCE"); - public final SyntheticKind THROW_NSME = generator.forSingleMethod(16, "ThrowNSME"); - public final SyntheticKind TWR_CLOSE_RESOURCE = generator.forSingleMethod(17, "TwrCloseResource"); - public final SyntheticKind SERVICE_LOADER = generator.forSingleMethod(18, "ServiceLoad"); - public final SyntheticKind OUTLINE = generator.forSingleMethod(19, "Outline"); - public final SyntheticKind API_CONVERSION = generator.forSingleMethod(26, "APIConversion"); + generator.forSingleMethod("StaticInterfaceCall"); + public final SyntheticKind TO_STRING_IF_NOT_NULL = generator.forSingleMethod("ToStringIfNotNull"); + public final SyntheticKind THROW_CCE_IF_NOT_NULL = generator.forSingleMethod("ThrowCCEIfNotNull"); + public final SyntheticKind THROW_IAE = generator.forSingleMethod("ThrowIAE"); + public final SyntheticKind THROW_ICCE = generator.forSingleMethod("ThrowICCE"); + public final SyntheticKind THROW_NSME = generator.forSingleMethod("ThrowNSME"); + public final SyntheticKind TWR_CLOSE_RESOURCE = generator.forSingleMethod("TwrCloseResource"); + public final SyntheticKind SERVICE_LOADER = generator.forSingleMethod("ServiceLoad"); + public final SyntheticKind OUTLINE = generator.forSingleMethod("Outline"); + public final SyntheticKind API_CONVERSION = generator.forSingleMethod("APIConversion"); public final SyntheticKind API_CONVERSION_PARAMETERS = - generator.forSingleMethod(28, "APIConversionParameters"); - public final SyntheticKind ARRAY_CONVERSION = generator.forSingleMethod(37, "$ArrayConversion"); - public final SyntheticKind API_MODEL_OUTLINE = generator.forSingleMethod(32, "ApiModelOutline"); + generator.forSingleMethod("APIConversionParameters"); + public final SyntheticKind ARRAY_CONVERSION = generator.forSingleMethod("$ArrayConversion"); + public final SyntheticKind API_MODEL_OUTLINE = generator.forSingleMethod("ApiModelOutline"); - private final List<SyntheticKind> ALL_KINDS = generator.getAllKinds(); + private final String versionHash; + private final List<SyntheticKind> ALL_KINDS; + + public SyntheticNaming() { + generator.hasher.putString(Version.getVersionString(), StandardCharsets.UTF_8); + versionHash = generator.hasher.hash().toString(); + ALL_KINDS = generator.getAllKinds(); + generator = null; + } + + public String getVersionHash() { + return versionHash; + } public Collection<SyntheticKind> kinds() { return ALL_KINDS; } public SyntheticKind fromId(int id) { - for (SyntheticKind kind : ALL_KINDS) { - if (kind.getId() == id) { - return kind; - } + if (0 < id && id <= ALL_KINDS.size()) { + return ALL_KINDS.get(id - 1); } return null; } private static class KindGenerator { + private int nextId = 1; private List<SyntheticKind> kinds = new ArrayList<>(); - private IntSet usedIds = new IntArraySet(); + private Hasher hasher = Hashing.sha256().newHasher(); private SyntheticKind register(SyntheticKind kind) { - if (!usedIds.add(kind.getId())) { - throw new Unreachable("Invalid reuse of synthetic kind id: " + kind.getId()); - } + kind.hash(hasher); kinds.add(kind); + if (kinds.size() != kind.getId()) { + throw new Unreachable("Invalid synthetic kind id: " + kind.getId()); + } return kind; } - SyntheticKind forSingleMethod(int id, String descriptor) { - return register(new SyntheticMethodKind(id, descriptor)); + private int getNextId() { + return nextId++; + } + + SyntheticKind forSingleMethod(String descriptor) { + return register(new SyntheticMethodKind(getNextId(), descriptor)); } // TODO(b/214901256): Remove once fixed. - SyntheticKind forNonSharableInstanceClass(int id, String descriptor) { - return register(new SyntheticClassKind(id, descriptor, false)); + SyntheticKind forNonSharableInstanceClass(String descriptor) { + return register(new SyntheticClassKind(getNextId(), descriptor, false)); } - SyntheticKind forInstanceClass(int id, String descriptor) { - return register(new SyntheticClassKind(id, descriptor, true)); + SyntheticKind forInstanceClass(String descriptor) { + return register(new SyntheticClassKind(getNextId(), descriptor, true)); } - SyntheticKind forFixedClass(int id, String descriptor) { - return register(new SyntheticFixedClassKind(id, descriptor, false)); + SyntheticKind forFixedClass(String descriptor) { + return register(new SyntheticFixedClassKind(getNextId(), descriptor, false)); } - SyntheticKind forGlobalClass(int id) { - return register(new SyntheticFixedClassKind(id, "", true)); + SyntheticKind forGlobalClass() { + return register(new SyntheticFixedClassKind(getNextId(), "", true)); } - SyntheticKind forGlobalClasspathClass(int id) { - return register(new SyntheticFixedClassKind(id, "", false)); + SyntheticKind forGlobalClasspathClass() { + return register(new SyntheticFixedClassKind(getNextId(), "", false)); } List<SyntheticKind> getAllKinds() { @@ -198,6 +211,13 @@ public abstract boolean isMayOverridesNonProgramType(); + public final void hash(Hasher hasher) { + hasher.putInt(getId()); + hasher.putString(getDescriptor(), StandardCharsets.UTF_8); + internalHash(hasher); + } + + public abstract void internalHash(Hasher hasher); } private static class SyntheticMethodKind extends SyntheticKind { @@ -232,6 +252,10 @@ return false; } + @Override + public void internalHash(Hasher hasher) { + hasher.putString("method", StandardCharsets.UTF_8); + } } private static class SyntheticClassKind extends SyntheticKind { @@ -269,6 +293,11 @@ return false; } + @Override + public void internalHash(Hasher hasher) { + hasher.putString("class", StandardCharsets.UTF_8); + hasher.putBoolean(sharable); + } } private static class SyntheticFixedClassKind extends SyntheticClassKind { @@ -299,6 +328,11 @@ return mayOverridesNonProgramType; } + @Override + public void internalHash(Hasher hasher) { + hasher.putString(isGlobal() ? "global" : "fixed", StandardCharsets.UTF_8); + hasher.putBoolean(mayOverridesNonProgramType); + } } private static final String SYNTHETIC_CLASS_SEPARATOR = "$$";
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java index 50c315a..309e92a5 100644 --- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java +++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -25,7 +25,6 @@ import com.android.tools.r8.dex.Marker.Tool; import com.android.tools.r8.dump.DumpOptions; import com.android.tools.r8.errors.CompilationError; -import com.android.tools.r8.errors.ExperimentalClassFileVersionDiagnostic; import com.android.tools.r8.errors.IncompleteNestNestDesugarDiagnosic; import com.android.tools.r8.errors.InterfaceDesugarMissingTypeDiagnostic; import com.android.tools.r8.errors.InvalidDebugInfoException; @@ -108,6 +107,7 @@ import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; +import java.util.stream.Collectors; import org.objectweb.asm.Opcodes; public class InternalOptions implements GlobalKeepInfoConfiguration { @@ -138,7 +138,6 @@ } public static final CfVersion SUPPORTED_CF_VERSION = CfVersion.V17; - public static final CfVersion EXPERIMENTAL_CF_VERSION = CfVersion.V14; public static final int SUPPORTED_DEX_VERSION = AndroidApiLevel.LATEST.getDexVersion().getIntValue(); @@ -272,9 +271,34 @@ // Flag to toggle if DEX code objects should pass-through without IR processing. public boolean passthroughDexCode = false; + public static class NeverMergeGroup<T> { + private final List<T> prefixes; + private final List<T> exceptionPrefixes; + + NeverMergeGroup(List<T> prefixes, List<T> exceptionPrefixes) { + this.prefixes = prefixes; + this.exceptionPrefixes = exceptionPrefixes; + } + + public List<T> getPrefixes() { + return prefixes; + } + + public List<T> getExceptionPrefixes() { + return exceptionPrefixes; + } + + public <R> NeverMergeGroup<R> map(Function<T, R> fn) { + return new NeverMergeGroup<>( + prefixes.stream().map(fn).collect(Collectors.toList()), + exceptionPrefixes.stream().map(fn).collect(Collectors.toList())); + } + } + // Flag to toggle if the prefix based merge restriction should be enforced. public boolean enableNeverMergePrefixes = true; - public Set<String> neverMergePrefixes = ImmutableSet.of("j$."); + public NeverMergeGroup<String> neverMerge = + new NeverMergeGroup<>(ImmutableList.of("j$."), ImmutableList.of("java.")); public boolean classpathInterfacesMayHaveStaticInitialization = false; public boolean libraryInterfacesMayHaveStaticInitialization = false; @@ -1135,23 +1159,6 @@ } } - private final Box<Boolean> reportedExperimentClassFileVersion = new Box<>(false); - - public void warningExperimentalClassFileVersion(Origin origin) { - synchronized (reportedExperimentClassFileVersion) { - if (reportedExperimentClassFileVersion.get()) { - return; - } - reportedExperimentClassFileVersion.set(true); - reporter.warning( - new ExperimentalClassFileVersionDiagnostic( - origin, - "One or more classes has class file version >= " - + EXPERIMENTAL_CF_VERSION.major() - + " which is not officially supported.")); - } - } - public boolean printWarnings() { boolean printed = false; boolean printOutdatedToolchain = false; @@ -1722,10 +1729,6 @@ } } - public static void allowExperimentClassFileVersion(InternalOptions options) { - options.reportedExperimentClassFileVersion.set(true); - } - public static int NO_LIMIT = -1; public ArgumentPropagatorEventConsumer argumentPropagatorEventConsumer =
diff --git a/src/test/java/com/android/tools/r8/L8TestBuilder.java b/src/test/java/com/android/tools/r8/L8TestBuilder.java index 7a42ab1..2ff60ba 100644 --- a/src/test/java/com/android/tools/r8/L8TestBuilder.java +++ b/src/test/java/com/android/tools/r8/L8TestBuilder.java
@@ -184,7 +184,10 @@ .inspect( inspector -> inspector.forAllClasses( - clazz -> assertTrue(clazz.getFinalName().startsWith("j$.")))); + clazz -> + assertTrue( + clazz.getFinalName().startsWith("j$.") + || clazz.getFinalName().startsWith("java.")))); } private Collection<Path> getProgramFiles() {
diff --git a/src/test/java/com/android/tools/r8/R8CommandTest.java b/src/test/java/com/android/tools/r8/R8CommandTest.java index d076ec0..1242d17 100644 --- a/src/test/java/com/android/tools/r8/R8CommandTest.java +++ b/src/test/java/com/android/tools/r8/R8CommandTest.java
@@ -86,6 +86,7 @@ assertTrue(command.getEnableTreeShaking()); assertEquals(CompilationMode.RELEASE, command.getMode()); assertTrue(command.getProgramConsumer() instanceof DexIndexedConsumer); + assertFalse(command.getProguardCompatibility()); } @Test(expected = CompilationFailedException.class) @@ -268,6 +269,12 @@ } @Test + public void proguardCompatMode() throws Throwable { + assertFalse(parse("").getProguardCompatibility()); + assertTrue(parse("--pg-compat").getProguardCompatibility()); + } + + @Test public void classFileOutputModeOption() throws Throwable { assertTrue(parse("--classfile").getProgramConsumer() instanceof ClassFileConsumer); }
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java index 4daa6a9..3a482a5 100644 --- a/src/test/java/com/android/tools/r8/TestBase.java +++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -416,7 +416,7 @@ } public static TestParametersBuilder getTestParameters() { - return TestParametersBuilder.builder(); + return TestParameters.builder(); } public static KotlinTestParameters.Builder getKotlinTestParameters() {
diff --git a/src/test/java/com/android/tools/r8/TestParameters.java b/src/test/java/com/android/tools/r8/TestParameters.java index a9b67e3..7853170 100644 --- a/src/test/java/com/android/tools/r8/TestParameters.java +++ b/src/test/java/com/android/tools/r8/TestParameters.java
@@ -31,6 +31,14 @@ this.apiLevel = apiLevel; } + public static TestParametersBuilder builder() { + return new TestParametersBuilder(); + } + + public static TestParametersCollection justNoneRuntime() { + return builder().withNoneRuntime().build(); + } + public boolean canUseDefaultAndStaticInterfaceMethods() { assert isCfRuntime() || isDexRuntime(); assert !isCfRuntime() || apiLevel == null
diff --git a/src/test/java/com/android/tools/r8/TestParametersBuilder.java b/src/test/java/com/android/tools/r8/TestParametersBuilder.java index 375f6f8..cb24356 100644 --- a/src/test/java/com/android/tools/r8/TestParametersBuilder.java +++ b/src/test/java/com/android/tools/r8/TestParametersBuilder.java
@@ -26,11 +26,7 @@ private Predicate<TestParameters> filter = param -> false; private boolean hasDexRuntimeFilter = false; - private TestParametersBuilder() {} - - public static TestParametersBuilder builder() { - return new TestParametersBuilder(); - } + TestParametersBuilder() {} private TestParametersBuilder withFilter(Predicate<TestParameters> predicate) { filter = filter.or(predicate);
diff --git a/src/test/java/com/android/tools/r8/TestRuntime.java b/src/test/java/com/android/tools/r8/TestRuntime.java index 336ffd1..ec3d29c 100644 --- a/src/test/java/com/android/tools/r8/TestRuntime.java +++ b/src/test/java/com/android/tools/r8/TestRuntime.java
@@ -189,7 +189,7 @@ if (version.startsWith("9.")) { return new CfRuntime(CfVm.JDK9, Paths.get(home)); } - if (version.startsWith("11.")) { + if (version.equals("11") || version.startsWith("11.")) { return new CfRuntime(CfVm.JDK11, Paths.get(home)); } throw new Unimplemented("No support for JDK version: " + version);
diff --git a/src/test/java/com/android/tools/r8/bisect/BisectTest.java b/src/test/java/com/android/tools/r8/bisect/BisectTest.java index bd1d3c4..3b34747 100644 --- a/src/test/java/com/android/tools/r8/bisect/BisectTest.java +++ b/src/test/java/com/android/tools/r8/bisect/BisectTest.java
@@ -7,7 +7,6 @@ import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestParametersBuilder; import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.bisect.BisectOptions.Result; import com.android.tools.r8.dex.ApplicationReader; @@ -34,7 +33,7 @@ @Parameters(name = "{0}") public static TestParametersCollection parameters() { - return TestParametersBuilder.builder().withNoneRuntime().build(); + return TestParameters.builder().withNoneRuntime().build(); } public BisectTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapTest.java b/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapTest.java index 14fff26..8c70d15 100644 --- a/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapTest.java +++ b/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapTest.java
@@ -3,24 +3,22 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.cf.bootstrap; -import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import com.android.tools.r8.ClassFileConsumer; import com.android.tools.r8.CompilationMode; -import com.android.tools.r8.R8Command; +import com.android.tools.r8.R8; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.TestRuntime.CfRuntime; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.ProcessResult; -import com.android.tools.r8.origin.Origin; import com.android.tools.r8.utils.FileUtils; +import com.android.tools.r8.utils.StringUtils; import com.google.common.base.Charsets; -import com.google.common.collect.ImmutableList; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Collections; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -29,18 +27,10 @@ @RunWith(Parameterized.class) public class BootstrapTest extends TestBase { - private static final Path R8_STABLE_JAR = Paths.get("third_party/r8/r8.jar"); + private static final Path R8_STABLE_JAR = + Paths.get("third_party", "r8-releases", "3.2.54", "r8.jar"); - private static final String R8_NAME = "com.android.tools.r8.R8"; - private static final String[] KEEP_R8 = { - "-keep public class " + R8_NAME + " {", " public static void main(...);", "}", - }; - private static final Path DONTWARN_R8 = Paths.get("src/main/dontwarn.txt"); - - private static final String HELLO_NAME = "hello.Hello"; - private static final String[] KEEP_HELLO = { - "-keep class " + HELLO_NAME + " {", " public static void main(...);", "}", - }; + private static final String HELLO_EXPECTED = StringUtils.lines("Hello World!"); private static class R8Result { @@ -65,98 +55,112 @@ return getTestParameters().withNoneRuntime().build(); } + private static CfRuntime getHostRuntime() { + return CfRuntime.getSystemRuntime(); + } + public BootstrapTest(TestParameters parameters) { - // TODO: use parameters to fork the right Java. parameters.assertNoneRuntime(); } + private Path getHelloInputs() { + return ToolHelper.getClassFileForTestClass(Hello.class); + } + + private String getHelloKeepRules() { + return TestBase.keepMainProguardConfiguration(Hello.class); + } + @Test - public void test() throws Exception { - // Run hello.jar to ensure it exists and is valid. - Path hello = Paths.get(ToolHelper.EXAMPLES_BUILD_DIR, "hello" + JAR_EXTENSION); - ProcessResult runHello = ToolHelper.runJava(hello, "hello.Hello"); - assertEquals(0, runHello.exitCode); + public void reference() throws Exception { + testForJvm() + .addProgramFiles(getHelloInputs()) + .run(getHostRuntime(), Hello.class) + .assertSuccessWithOutput(HELLO_EXPECTED); + } + @Test + public void testDebug() throws Exception { + compareForMode(CompilationMode.DEBUG); + } + + @Test + public void testRelease() throws Exception { + compareForMode(CompilationMode.RELEASE); + } + + private R8Result compareForMode(CompilationMode mode) throws Exception { // Run r8.jar on hello.jar to ensure that r8.jar is a working compiler. - R8Result runInputR8 = runExternalR8(R8_STABLE_JAR, hello, "input", KEEP_HELLO, "--debug"); - ProcessResult runHelloR8 = ToolHelper.runJava(runInputR8.outputJar, "hello.Hello"); - assertEquals(runHello.toString(), runHelloR8.toString()); + R8Result helloCompiledWithR8 = + runExternalR8(R8_STABLE_JAR, getHelloInputs(), getHelloKeepRules(), mode); + testForJvm() + .addProgramFiles(helloCompiledWithR8.outputJar) + .run(getHostRuntime(), Hello.class) + .assertSuccessWithOutput(HELLO_EXPECTED); - compareR8(hello, runInputR8, CompilationMode.RELEASE, "r8-r8-rel", "--release", "output-rel"); - compareR8(hello, runInputR8, CompilationMode.DEBUG, "r8-r8", "--debug", "output"); + compareR8(helloCompiledWithR8, mode); + return helloCompiledWithR8; } - private void compareR8( - Path hello, - R8Result runInputR8, - CompilationMode internalMode, - String internalOutput, - String externalMode, - String externalOutput) - throws Exception { + private void compareR8(R8Result referenceCompilation, CompilationMode mode) throws Exception { // Run R8 on r8.jar. - Path output = runR8(internalOutput, internalMode); + Path r8CompiledByR8 = compileR8WithR8(mode); // Run the resulting compiler on hello.jar. - R8Result runR8R8 = runExternalR8(output, hello, externalOutput, KEEP_HELLO, externalMode); + R8Result runR8R8 = runExternalR8(r8CompiledByR8, getHelloInputs(), getHelloKeepRules(), mode); // Check that the process outputs (exit code, stdout, stderr) are the same. - assertEquals(runInputR8.toString(), runR8R8.toString()); + assertEquals(referenceCompilation.toString(), runR8R8.toString()); // Check that the output jars are the same. - if (true) { - // TODO(b/223770583): These should be equal but an error in assertProgramEquals was hiding it. - assertFalse(filesAreEqual(runInputR8.outputJar, runR8R8.outputJar)); - } else { - assertProgramsEqual(runInputR8.outputJar, runR8R8.outputJar); - } + assertProgramsEqual(referenceCompilation.outputJar, runR8R8.outputJar); } - private Path runR8(String outputFolder, CompilationMode mode) throws Exception { - Path outputPath = temp.newFolder(outputFolder).toPath(); - Path outputJar = outputPath.resolve("output.jar"); - Path pgConfigFile = outputPath.resolve("keep.rules"); - FileUtils.writeTextFile(pgConfigFile, BootstrapTest.KEEP_R8); - ToolHelper.runR8( - R8Command.builder() - .setMode(mode) - .addLibraryFiles(ToolHelper.getJava8RuntimeJar()) - .setProgramConsumer(new ClassFileConsumer.ArchiveConsumer(outputJar, true)) - .addProgramFiles(R8_STABLE_JAR) - .addProguardConfigurationFiles(pgConfigFile, DONTWARN_R8) - // The R8_STABLE_JAR is from when Guava 23.0 was used, and that included - // javax.annotation.Nullable annotations in the retained code. - .addProguardConfiguration( - ImmutableList.of("-dontwarn javax.annotation.Nullable"), Origin.unknown()) - .build()); - return outputJar; + private Path compileR8WithR8(CompilationMode mode) throws Exception { + return testForR8(Backend.CF) + .setMode(mode) + .addProgramFiles(R8_STABLE_JAR) + .addKeepRules(TestBase.keepMainProguardConfiguration(R8.class)) + // The r8 stable/release hits open interface issues. + .addOptionsModification(o -> o.getOpenClosedInterfacesOptions().suppressAllOpenInterfaces()) + // The r8 stable/release contains missing com.google.errorprone annotation references. + .addDontWarnGoogle() + .compile() + .writeToZip(); } - private R8Result runExternalR8( - Path r8Jar, Path inputJar, String outputFolder, String[] keepRules, String mode) + private R8Result runExternalR8(Path r8Jar, Path inputJar, String keepRules, CompilationMode mode) throws Exception { - Path outputPath = temp.newFolder(outputFolder).toPath(); + Path outputPath = temp.newFolder().toPath(); Path pgConfigFile = outputPath.resolve("keep.rules"); Path outputJar = outputPath.resolve("output.jar"); Path pgMapFile = outputPath.resolve("map.txt"); FileUtils.writeTextFile(pgConfigFile, keepRules); ProcessResult processResult = ToolHelper.runJava( - r8Jar, - R8_NAME, + getHostRuntime(), + Collections.singletonList(r8Jar), + R8.class.getTypeName(), "--lib", - ToolHelper.JAVA_8_RUNTIME, + ToolHelper.getJava8RuntimeJar().toString(), "--classfile", inputJar.toString(), "--output", outputJar.toString(), "--pg-conf", pgConfigFile.toString(), - mode, + mode == CompilationMode.DEBUG ? "--debug" : "--release", "--pg-map-output", pgMapFile.toString()); if (processResult.exitCode != 0) { System.out.println(processResult); } - assertEquals(processResult.stderr, 0, processResult.exitCode); + assertEquals(processResult.toString(), 0, processResult.exitCode); String pgMap = FileUtils.readTextFile(pgMapFile, Charsets.UTF_8); return new R8Result(processResult, outputJar, pgMap); } + + public static class Hello { + + public static void main(String[] args) { + System.out.println("Hello World!"); + } + } }
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupLambdaTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupLambdaTest.java index 7081080..a136180 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupLambdaTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupLambdaTest.java
@@ -18,8 +18,8 @@ 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.utils.codeinspector.ClassSubject; -import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -46,7 +46,13 @@ // I and J are not eligible for merging, since the lambda that implements I & J inherits a // default m() method from K, which is also on J. .addHorizontallyMergedClassesInspector( - HorizontallyMergedClassesInspector::assertNoClassesMerged) + inspector -> { + if (parameters.isCfRuntime()) { + inspector.assertIsCompleteMergeGroup(I.class, J.class); + } else { + inspector.assertNoClassesMerged(); + } + }) .enableInliningAnnotations() .enableNeverClassInliningAnnotations() .enableNoHorizontalClassMergingAnnotations() @@ -60,13 +66,26 @@ ClassSubject aClassSubject = inspector.clazz(A.class); if (parameters.canUseDefaultAndStaticInterfaceMethods()) { assertThat(aClassSubject, isPresent()); - assertThat(aClassSubject, isImplementing(inspector.clazz(J.class))); + if (parameters.isCfRuntime()) { + assertThat(aClassSubject, isImplementing(inspector.clazz(I.class))); + } else { + assertThat(aClassSubject, isImplementing(inspector.clazz(J.class))); + } } else { assertThat(aClassSubject, isAbsent()); } }) .run(parameters.getRuntime(), Main.class) - .assertSuccessWithOutputLines("K", "J"); + // TODO(b/173990042): Should succeed with "K", "J". + .applyIf( + parameters.isCfRuntime(), + builder -> + builder.assertFailureWithErrorThatThrows( + parameters.isCfRuntime() + && parameters.getRuntime().asCf().isNewerThanOrEqual(CfVm.JDK11) + ? AbstractMethodError.class + : IncompatibleClassChangeError.class), + builder -> builder.assertSuccessWithOutputLines("K", "J")); } static class Main {
diff --git a/src/test/java/com/android/tools/r8/debug/KotlinStdLibCompilationTest.java b/src/test/java/com/android/tools/r8/debug/KotlinStdLibCompilationTest.java index ebba822..2c3a478 100644 --- a/src/test/java/com/android/tools/r8/debug/KotlinStdLibCompilationTest.java +++ b/src/test/java/com/android/tools/r8/debug/KotlinStdLibCompilationTest.java
@@ -13,7 +13,6 @@ import com.android.tools.r8.TestBase; import com.android.tools.r8.TestDiagnosticMessages; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestParametersBuilder; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.utils.AndroidApiLevel; import java.util.List; @@ -31,7 +30,7 @@ @Parameters(name = "{0}, kotlinc: {1}") public static List<Object[]> setup() { return buildParameters( - TestParametersBuilder.builder().withAllRuntimesAndApiLevels().build(), + TestParameters.builder().withAllRuntimesAndApiLevels().build(), getKotlinTestParameters().withAllCompilers().withNoTargetVersion().build()); }
diff --git a/src/test/java/com/android/tools/r8/debug/LineNumberOptimizationTest.java b/src/test/java/com/android/tools/r8/debug/LineNumberOptimizationTest.java index d227ea3..af193de 100644 --- a/src/test/java/com/android/tools/r8/debug/LineNumberOptimizationTest.java +++ b/src/test/java/com/android/tools/r8/debug/LineNumberOptimizationTest.java
@@ -9,7 +9,6 @@ import com.android.tools.r8.CompilationMode; import com.android.tools.r8.R8TestCompileResult; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestParametersBuilder; import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.utils.InternalOptions.LineNumberOptimization; import org.junit.Test; @@ -34,7 +33,7 @@ @Parameterized.Parameters(name = "{0}") public static TestParametersCollection setup() { - return TestParametersBuilder.builder().withAllRuntimesAndApiLevels().build(); + return TestParameters.builder().withAllRuntimesAndApiLevels().build(); } public LineNumberOptimizationTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/debuginfo/DebugSetFileSmaliTest.java b/src/test/java/com/android/tools/r8/debuginfo/DebugSetFileSmaliTest.java index 008d3cf..416cada 100644 --- a/src/test/java/com/android/tools/r8/debuginfo/DebugSetFileSmaliTest.java +++ b/src/test/java/com/android/tools/r8/debuginfo/DebugSetFileSmaliTest.java
@@ -8,7 +8,6 @@ import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestParametersBuilder; import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.graph.DexDebugEvent.SetFile; import com.android.tools.r8.smali.SmaliBuilder; @@ -30,10 +29,7 @@ @Parameters(name = "{0}") public static TestParametersCollection parameters() { - return TestParametersBuilder.builder() - .withDexRuntimes() - .withApiLevel(AndroidApiLevel.B) - .build(); + return TestParameters.builder().withDexRuntimes().withApiLevel(AndroidApiLevel.B).build(); } private static final String CLASS_NAME = "Test";
diff --git a/src/test/java/com/android/tools/r8/debuginfo/InliningWithoutPositionsTestRunner.java b/src/test/java/com/android/tools/r8/debuginfo/InliningWithoutPositionsTestRunner.java index b683d7d..6af4440 100644 --- a/src/test/java/com/android/tools/r8/debuginfo/InliningWithoutPositionsTestRunner.java +++ b/src/test/java/com/android/tools/r8/debuginfo/InliningWithoutPositionsTestRunner.java
@@ -11,7 +11,6 @@ import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestParametersBuilder; import com.android.tools.r8.debuginfo.InliningWithoutPositionsTestSourceDump.Location; import com.android.tools.r8.naming.retrace.StackTrace; import com.android.tools.r8.naming.retrace.StackTrace.StackTraceLine; @@ -44,7 +43,7 @@ public static Collection<Object[]> data() { List<Object[]> testCases = new ArrayList<>(); for (TestParameters parameters : - TestParametersBuilder.builder().withAllRuntimes().withApiLevel(AndroidApiLevel.B).build()) { + TestParameters.builder().withAllRuntimes().withApiLevel(AndroidApiLevel.B).build()) { for (int i = 0; i < 16; ++i) { for (Location throwLocation : Location.values()) { if (throwLocation != Location.MAIN) {
diff --git a/src/test/java/com/android/tools/r8/desugar/DesugarToClassFileB156591935.java b/src/test/java/com/android/tools/r8/desugar/DesugarToClassFileB156591935.java index 674f288..e288d12 100644 --- a/src/test/java/com/android/tools/r8/desugar/DesugarToClassFileB156591935.java +++ b/src/test/java/com/android/tools/r8/desugar/DesugarToClassFileB156591935.java
@@ -8,7 +8,6 @@ import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestParametersBuilder; import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.cf.code.CfStackInstruction.Opcode; import com.android.tools.r8.utils.AndroidApiLevel; @@ -29,7 +28,7 @@ @Parameterized.Parameters(name = "{0}") public static TestParametersCollection data() { - return TestParametersBuilder.builder().withCfRuntimes().withAllApiLevelsAlsoForCf().build(); + return TestParameters.builder().withCfRuntimes().withAllApiLevelsAlsoForCf().build(); } private final AndroidApiLevel apiLevel;
diff --git a/src/test/java/com/android/tools/r8/desugar/InterfaceInvokePrivateTest.java b/src/test/java/com/android/tools/r8/desugar/InterfaceInvokePrivateTest.java index 86c5440..1d88df8 100644 --- a/src/test/java/com/android/tools/r8/desugar/InterfaceInvokePrivateTest.java +++ b/src/test/java/com/android/tools/r8/desugar/InterfaceInvokePrivateTest.java
@@ -3,7 +3,6 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.desugar; -import static com.android.tools.r8.utils.InternalOptions.EXPERIMENTAL_CF_VERSION; import static org.hamcrest.CoreMatchers.containsString; import static org.junit.Assume.assumeTrue; @@ -106,8 +105,6 @@ // TODO(b/185463156): Not keeping I and its members will "fix" the ICCE for all runtimes. .addKeepClassAndMembersRules(I.class) .setMinApi(parameters.getApiLevel()) - .allowDiagnosticWarningMessages( - inputCfVersion.isGreaterThanOrEqualTo(EXPERIMENTAL_CF_VERSION)) .compile() .run(parameters.getRuntime(), TestRunner.class) .applyIf(
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java index f79b585..93ea904 100644 --- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java +++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java
@@ -51,13 +51,7 @@ } private String expectedOutput() { - return StringUtils.lines( - "Hello", - "Larry", - "Page", - parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N) - ? "Caught java.io.UncheckedIOException" - : "Caught j$.io.UncheckedIOException"); + return StringUtils.lines("Hello", "Larry", "Page", "Caught java.io.UncheckedIOException"); } DesugaredLibrarySpecification configurationAlternative3(
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LocalDateEpochTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LocalDateEpochTest.java new file mode 100644 index 0000000..23a146e --- /dev/null +++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LocalDateEpochTest.java
@@ -0,0 +1,132 @@ +// Copyright (c) 2022, 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.desugaredlibrary; + +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.ToolHelper.DexVm.Version; +import com.android.tools.r8.dex.Constants; +import com.android.tools.r8.graph.DexField; +import com.android.tools.r8.graph.DexString; +import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.graph.FieldAccessFlags; +import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecification; +import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecification; +import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanRewritingFlags; +import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanTopLevelFlags; +import com.android.tools.r8.origin.Origin; +import com.android.tools.r8.transformers.MethodTransformer; +import com.android.tools.r8.utils.DescriptorUtils; +import com.android.tools.r8.utils.InternalOptions; +import com.android.tools.r8.utils.StringUtils; +import com.google.common.collect.ImmutableList; +import java.io.IOException; +import java.time.LocalDate; +import java.util.Collection; +import org.junit.Assume; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class LocalDateEpochTest extends DesugaredLibraryTestBase { + + private final TestParameters parameters; + + private static final String EXPECTED_OUTPUT = StringUtils.lines("1970-01-01"); + + @Parameterized.Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters() + .withDexRuntimesStartingFromIncluding(Version.V8_1_0) + .withAllApiLevels() + .build(); + } + + public LocalDateEpochTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void testD8() throws Exception { + testForD8(parameters.getBackend()) + .addLibraryFiles(getLibraryFile()) + .addProgramClasses(DesugarLocalDate.class) + .addProgramClassFileData(getMainClassFileData()) + .setMinApi(parameters.getApiLevel()) + .addOptionsModification(opt -> opt.setDesugaredLibrarySpecification(getSpecification(opt))) + .compile() + .run(parameters.getRuntime(), Main.class) + .assertSuccessWithOutput(EXPECTED_OUTPUT); + } + + @Test + public void testR8() throws Exception { + Assume.assumeTrue(parameters.isDexRuntime()); + testForR8(parameters.getBackend()) + .addLibraryFiles(getLibraryFile()) + .addProgramClasses(DesugarLocalDate.class) + .addProgramClassFileData(getMainClassFileData()) + .addKeepMainRule(Main.class) + .setMinApi(parameters.getApiLevel()) + .addOptionsModification(opt -> opt.setDesugaredLibrarySpecification(getSpecification(opt))) + .compile() + .run(parameters.getRuntime(), Main.class) + .assertSuccessWithOutput(EXPECTED_OUTPUT); + } + + private DesugaredLibrarySpecification getSpecification(InternalOptions options) { + DexType date = options.dexItemFactory().createType("Ljava/time/LocalDate;"); + DexType desugarDate = + options + .dexItemFactory() + .createType("L" + DescriptorUtils.getClassBinaryName(DesugarLocalDate.class) + ";"); + DexString epoch = options.dexItemFactory().createString("EPOCH"); + DexField src = options.dexItemFactory().createField(date, date, epoch); + HumanRewritingFlags rewritingFlags = + HumanRewritingFlags.builder(options.reporter, Origin.unknown()) + .retargetStaticField(src, desugarDate) + .amendLibraryField( + src, + FieldAccessFlags.fromSharedAccessFlags( + Constants.ACC_PUBLIC | Constants.ACC_STATIC | Constants.ACC_FINAL)) + .build(); + return new HumanDesugaredLibrarySpecification( + HumanTopLevelFlags.testing(), rewritingFlags, false); + } + + private Collection<byte[]> getMainClassFileData() throws IOException { + return ImmutableList.of( + transformer(Main.class) + .addMethodTransformer( + new MethodTransformer() { + @Override + public void visitFieldInsn( + final int opcode, + final String owner, + final String name, + final String descriptor) { + if (name.equals("MIN")) { + super.visitFieldInsn(opcode, owner, "EPOCH", descriptor); + } else { + super.visitFieldInsn(opcode, owner, name, descriptor); + } + } + }) + .transform()); + } + + static class Main { + + public static void main(String[] args) { + System.out.println(LocalDate.MIN); + } + } + + static class DesugarLocalDate { + + public static final LocalDate EPOCH = LocalDate.of(1970, 1, 1); + } +}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MergingJ$Test.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MergingJ$Test.java index 1208414..568de28 100644 --- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MergingJ$Test.java +++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MergingJ$Test.java
@@ -7,12 +7,14 @@ import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType; import static junit.framework.TestCase.assertNotNull; import static junit.framework.TestCase.assertTrue; +import static org.hamcrest.CoreMatchers.containsString; import static org.junit.Assert.assertThrows; import static org.junit.Assert.fail; import com.android.tools.r8.CompilationFailedException; import com.android.tools.r8.D8Command; import com.android.tools.r8.DexFileMergerHelper; +import com.android.tools.r8.DiagnosticsMatcher; import com.android.tools.r8.L8; import com.android.tools.r8.L8Command; import com.android.tools.r8.OutputMode; @@ -24,8 +26,10 @@ import com.android.tools.r8.errors.DuplicateTypesDiagnostic; import com.android.tools.r8.utils.AndroidApiLevel; import com.android.tools.r8.utils.codeinspector.CodeInspector; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import java.nio.file.Path; +import java.util.List; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -127,4 +131,134 @@ .build()); return outputDex; } + + @Test + public void testJavaMergesWithEverything() throws Exception { + // java and other prefixes can co-exist in same DEX. + Path dexWithJavaAndNonJ$Classes = + testForD8() + .addProgramClassFileData( + transformer(A.class).setClassDescriptor("Ljava/A;").transform()) + .addInnerClasses(getClass()) + .compile() + .writeToZip(); + + // Can be merged with other java classes. + testForD8() + .addProgramFiles(dexWithJavaAndNonJ$Classes) + .addProgramFiles( + testForD8() + .addProgramClassFileData( + transformer(B.class).setClassDescriptor("Ljava/B;").transform()) + .compile() + .writeToZip()) + .compile(); + + // Cannot be merged with j$ classes. + Path j$Dex = + testForD8() + .addProgramClassFileData(transformer(A.class).setClassDescriptor("Lj$/A;").transform()) + .compile() + .writeToZip(); + assertThrows( + CompilationFailedException.class, + () -> + testForD8() + .addProgramFiles(dexWithJavaAndNonJ$Classes) + .addProgramFiles(j$Dex) + .compileWithExpectedDiagnostics( + diagnostics -> + diagnostics.assertErrorsMatch( + DiagnosticsMatcher.diagnosticMessage( + containsString( + "Merging DEX file containing classes with prefix"))))); + + // Cannot be merged with j$ even if they are together with java. + Path j$JavaDex = + testForD8() + .addProgramClassFileData(transformer(A.class).setClassDescriptor("Lj$/A;").transform()) + .addProgramClassFileData( + transformer(B.class).setClassDescriptor("Ljava/B;").transform()) + .compile() + .writeToZip(); + assertThrows( + CompilationFailedException.class, + () -> + testForD8() + .addProgramFiles(dexWithJavaAndNonJ$Classes) + .addProgramFiles(j$JavaDex) + .compileWithExpectedDiagnostics( + diagnostics -> + diagnostics.assertErrorsMatch( + DiagnosticsMatcher.diagnosticMessage( + containsString( + "Merging DEX file containing classes with prefix"))))); + } + + @Test + public void testJ$OnlyMergesWithJava() throws Exception { + Path j$Dex = + testForD8() + .addProgramClassFileData(transformer(A.class).setClassDescriptor("Lj$/A;").transform()) + .compile() + .writeToZip(); + Path javaDex = + testForD8() + .addProgramClassFileData( + transformer(A.class).setClassDescriptor("Ljava/A;").transform()) + .compile() + .writeToZip(); + Path j$Dex2 = + testForD8() + .addProgramClassFileData(transformer(B.class).setClassDescriptor("Lj$/B;").transform()) + .compile() + .writeToZip(); + Path javaDex2 = + testForD8() + .addProgramClassFileData( + transformer(B.class).setClassDescriptor("Ljava/B;").transform()) + .compile() + .writeToZip(); + Path javaJ$Dex = + testForD8() + .addProgramClassFileData(transformer(C.class).setClassDescriptor("Lj$/C;").transform()) + .addProgramClassFileData( + transformer(C.class).setClassDescriptor("Ljava/C;").transform()) + .compile() + .writeToZip(); + + List<Path> j$Dexes = ImmutableList.of(j$Dex, j$Dex2, javaJ$Dex); + List<Path> allDexes = ImmutableList.of(j$Dex, javaDex, j$Dex2, javaDex2, javaJ$Dex); + testForD8().addProgramFiles(allDexes).compile(); + + for (Path first : allDexes) { + for (Path second : allDexes) { + if (first != second) { + testForD8().addProgramFiles(first, second).compile(); + } + } + if (j$Dexes.contains(first)) { + assertThrows( + CompilationFailedException.class, + () -> + testForD8() + .addInnerClasses(getClass()) + .addProgramFiles(first) + .compileWithExpectedDiagnostics( + diagnostics -> + diagnostics.assertErrorsMatch( + DiagnosticsMatcher.diagnosticMessage( + containsString( + "Merging DEX file containing classes with prefix"))))); + } else { + testForD8().addInnerClasses(getClass()).addProgramFiles(first).compile(); + } + } + } + + static class A {} + + static class B {} + + static class C {} }
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/NeverMergeCoreLibDesugarClasses.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/NeverMergeCoreLibDesugarClasses.java index 8a86def..607ff9b 100644 --- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/NeverMergeCoreLibDesugarClasses.java +++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/NeverMergeCoreLibDesugarClasses.java
@@ -52,8 +52,9 @@ assertThat( message, containsString( - "Merging dex file containing classes with prefix 'j$.' " - + "with classes with any other prefixes is not allowed")); + "Merging DEX file containing classes with prefix 'j$.' " + + "with other classes, except classes with prefix 'java.', " + + "is not allowed:")); }); } catch (CompilationFailedException e) { // Expected compilation failed. @@ -78,8 +79,9 @@ assertThat( message, containsString( - "Merging dex file containing classes with prefix 'j$.' " - + "with classes with any other prefixes is not allowed")); + "Merging DEX file containing classes with prefix 'j$.' " + + "with other classes, except classes with prefix 'java.', " + + "is not allowed:")); }); } catch (CompilationFailedException e) { // Expected compilation failed.
diff --git a/src/test/java/com/android/tools/r8/desugar/jdk8272564/Jdk8272564Test.java b/src/test/java/com/android/tools/r8/desugar/jdk8272564/Jdk8272564Test.java index 13e021d..15473c5 100644 --- a/src/test/java/com/android/tools/r8/desugar/jdk8272564/Jdk8272564Test.java +++ b/src/test/java/com/android/tools/r8/desugar/jdk8272564/Jdk8272564Test.java
@@ -14,7 +14,6 @@ import com.android.tools.r8.TestRuntime.CfVm; import com.android.tools.r8.examples.jdk18.jdk8272564.Jdk8272564; import com.android.tools.r8.utils.AndroidApiLevel; -import com.android.tools.r8.utils.InternalOptions.TestingOptions; import com.android.tools.r8.utils.codeinspector.CodeInspector; import com.android.tools.r8.utils.codeinspector.InstructionSubject; import org.junit.Test; @@ -160,7 +159,6 @@ .setMinApi(parameters.getApiLevel()) .noTreeShaking() .addKeepClassAndMembersRules(Jdk8272564.Main.typeName()) - .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) .run(parameters.getRuntime(), Jdk8272564.Main.typeName()) .inspect(this::assertJdk8272564NotFixedCodeR8) .assertSuccess();
diff --git a/src/test/java/com/android/tools/r8/desugar/records/EmptyRecordAnnotationTest.java b/src/test/java/com/android/tools/r8/desugar/records/EmptyRecordAnnotationTest.java index 1624c29..8de3689 100644 --- a/src/test/java/com/android/tools/r8/desugar/records/EmptyRecordAnnotationTest.java +++ b/src/test/java/com/android/tools/r8/desugar/records/EmptyRecordAnnotationTest.java
@@ -4,7 +4,6 @@ package com.android.tools.r8.desugar.records; -import static com.android.tools.r8.utils.InternalOptions.TestingOptions; import static org.junit.Assume.assumeTrue; import com.android.tools.r8.R8FullTestBuilder; @@ -61,7 +60,6 @@ testForD8(parameters.getBackend()) .addProgramClassFileData(PROGRAM_DATA) .setMinApi(parameters.getApiLevel()) - .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) .compile() .run(parameters.getRuntime(), MAIN_TYPE) .assertSuccessWithOutput(EXPECTED_RESULT_DEX); @@ -77,8 +75,7 @@ .addKeepRules("-keep class records.EmptyRecordAnnotation { *; }") .addKeepRules("-keepattributes *Annotation*") .addKeepRules("-keep class records.EmptyRecordAnnotation$Empty") - .addKeepMainRule(MAIN_TYPE) - .addOptionsModification(TestingOptions::allowExperimentClassFileVersion); + .addKeepMainRule(MAIN_TYPE); if (parameters.isCfRuntime()) { builder .addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp))
diff --git a/src/test/java/com/android/tools/r8/desugar/records/EmptyRecordTest.java b/src/test/java/com/android/tools/r8/desugar/records/EmptyRecordTest.java index 5f1b68f..3e44232 100644 --- a/src/test/java/com/android/tools/r8/desugar/records/EmptyRecordTest.java +++ b/src/test/java/com/android/tools/r8/desugar/records/EmptyRecordTest.java
@@ -4,8 +4,6 @@ package com.android.tools.r8.desugar.records; -import static com.android.tools.r8.utils.InternalOptions.TestingOptions; - import com.android.tools.r8.R8FullTestBuilder; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; @@ -52,7 +50,6 @@ testForD8(parameters.getBackend()) .addProgramClassFileData(PROGRAM_DATA) .setMinApi(parameters.getApiLevel()) - .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) .compile() .run(parameters.getRuntime(), MAIN_TYPE) .assertSuccessWithOutput(EXPECTED_RESULT_D8); @@ -64,8 +61,7 @@ testForR8(parameters.getBackend()) .addProgramClassFileData(PROGRAM_DATA) .setMinApi(parameters.getApiLevel()) - .addKeepMainRule(MAIN_TYPE) - .addOptionsModification(TestingOptions::allowExperimentClassFileVersion); + .addKeepMainRule(MAIN_TYPE); if (parameters.isCfRuntime()) { builder .addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp))
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordInstanceOfTest.java b/src/test/java/com/android/tools/r8/desugar/records/RecordInstanceOfTest.java index 743b2b4..f0d89d6 100644 --- a/src/test/java/com/android/tools/r8/desugar/records/RecordInstanceOfTest.java +++ b/src/test/java/com/android/tools/r8/desugar/records/RecordInstanceOfTest.java
@@ -8,7 +8,6 @@ import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestRuntime.CfVm; -import com.android.tools.r8.utils.InternalOptions.TestingOptions; import com.android.tools.r8.utils.StringUtils; import java.util.List; import org.junit.Test; @@ -50,7 +49,6 @@ testForD8(parameters.getBackend()) .addProgramClassFileData(PROGRAM_DATA) .setMinApi(parameters.getApiLevel()) - .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) .compile() .run(parameters.getRuntime(), MAIN_TYPE) .assertSuccessWithOutput(EXPECTED_RESULT); @@ -62,8 +60,7 @@ testForR8(parameters.getBackend()) .addProgramClassFileData(PROGRAM_DATA) .setMinApi(parameters.getApiLevel()) - .addKeepMainRule(MAIN_TYPE) - .addOptionsModification(TestingOptions::allowExperimentClassFileVersion); + .addKeepMainRule(MAIN_TYPE); if (parameters.isCfRuntime()) { builder .addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp))
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordInvokeCustomSplitDesugaringTest.java b/src/test/java/com/android/tools/r8/desugar/records/RecordInvokeCustomSplitDesugaringTest.java index 9ee21d2..f6c39e6 100644 --- a/src/test/java/com/android/tools/r8/desugar/records/RecordInvokeCustomSplitDesugaringTest.java +++ b/src/test/java/com/android/tools/r8/desugar/records/RecordInvokeCustomSplitDesugaringTest.java
@@ -6,7 +6,6 @@ import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.utils.InternalOptions.TestingOptions; import com.android.tools.r8.utils.StringUtils; import java.nio.file.Path; import java.util.List; @@ -56,13 +55,11 @@ testForD8(Backend.CF) .addProgramClassFileData(PROGRAM_DATA) .setMinApi(parameters.getApiLevel()) - .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) .compile() .writeToZip(); testForD8(parameters.getBackend()) .addProgramFiles(desugared) .setMinApi(parameters.getApiLevel()) - .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) .compile() .run(parameters.getRuntime(), MAIN_TYPE) .assertSuccessWithOutput(EXPECTED_RESULT_D8); @@ -74,14 +71,12 @@ testForD8(Backend.CF) .addProgramClassFileData(PROGRAM_DATA) .setMinApi(parameters.getApiLevel()) - .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) .compile() .writeToZip(); testForR8(parameters.getBackend()) .addProgramFiles(desugared) .setMinApi(parameters.getApiLevel()) .addKeepMainRule(MAIN_TYPE) - .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) .compile() .run(parameters.getRuntime(), MAIN_TYPE) .assertSuccessWithOutput(EXPECTED_RESULT_R8);
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordInvokeCustomTest.java b/src/test/java/com/android/tools/r8/desugar/records/RecordInvokeCustomTest.java index 61ee05b..b851c88 100644 --- a/src/test/java/com/android/tools/r8/desugar/records/RecordInvokeCustomTest.java +++ b/src/test/java/com/android/tools/r8/desugar/records/RecordInvokeCustomTest.java
@@ -8,7 +8,6 @@ import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestRuntime.CfVm; -import com.android.tools.r8.utils.InternalOptions.TestingOptions; import com.android.tools.r8.utils.StringUtils; import java.util.List; import org.junit.Test; @@ -67,7 +66,6 @@ testForD8(parameters.getBackend()) .addProgramClassFileData(PROGRAM_DATA) .setMinApi(parameters.getApiLevel()) - .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) .compile() .run(parameters.getRuntime(), MAIN_TYPE) .assertSuccessWithOutput(EXPECTED_RESULT_D8); @@ -79,8 +77,7 @@ testForR8(parameters.getBackend()) .addProgramClassFileData(PROGRAM_DATA) .setMinApi(parameters.getApiLevel()) - .addKeepMainRule(MAIN_TYPE) - .addOptionsModification(TestingOptions::allowExperimentClassFileVersion); + .addKeepMainRule(MAIN_TYPE); if (parameters.isCfRuntime()) { builder .addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp))
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordKeepRulesTest.java b/src/test/java/com/android/tools/r8/desugar/records/RecordKeepRulesTest.java index 49df3e2..f34bd67 100644 --- a/src/test/java/com/android/tools/r8/desugar/records/RecordKeepRulesTest.java +++ b/src/test/java/com/android/tools/r8/desugar/records/RecordKeepRulesTest.java
@@ -7,7 +7,6 @@ import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.utils.BooleanUtils; -import com.android.tools.r8.utils.InternalOptions.TestingOptions; import com.android.tools.r8.utils.StringUtils; import java.nio.file.Path; import java.util.List; @@ -87,7 +86,6 @@ .setMinApi(parameters.getApiLevel()) .addKeepMainRule(MAIN_TYPE) .addKeepRules(keepRules) - .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) .run(parameters.getRuntime(), MAIN_TYPE) .assertSuccessWithOutput(expectedOutput); } @@ -100,13 +98,11 @@ .addKeepMainRule(MAIN_TYPE) .addKeepRules(keepRules) .addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp)) - .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) .compile() .writeToZip(); testForD8(parameters.getBackend()) .addProgramFiles(desugared) .setMinApi(parameters.getApiLevel()) - .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) .run(parameters.getRuntime(), MAIN_TYPE) .assertSuccessWithOutput(expectedOutput); }
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordLibMergeTest.java b/src/test/java/com/android/tools/r8/desugar/records/RecordLibMergeTest.java index 53eff63..1701934 100644 --- a/src/test/java/com/android/tools/r8/desugar/records/RecordLibMergeTest.java +++ b/src/test/java/com/android/tools/r8/desugar/records/RecordLibMergeTest.java
@@ -8,7 +8,6 @@ import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestRuntime.CfVm; -import com.android.tools.r8.utils.InternalOptions.TestingOptions; import com.android.tools.r8.utils.StringUtils; import java.nio.file.Path; import java.util.List; @@ -52,7 +51,6 @@ "-keep class records.RecordLib { public static java.lang.Object getRecord(); }") .addKeepRules("-keep class records.RecordLib$LibRecord") .addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp)) - .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) .compile() .writeToZip(); R8FullTestBuilder builder = @@ -62,8 +60,7 @@ .setMinApi(parameters.getApiLevel()) .addKeepMainRule(MAIN_TYPE) .addKeepRules("-keep class records.RecordLib$LibRecord") - .addKeepRules("-keep class records.RecordMain$MainRecord") - .addOptionsModification(TestingOptions::allowExperimentClassFileVersion); + .addKeepRules("-keep class records.RecordMain$MainRecord"); if (parameters.isCfRuntime()) { builder .addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp))
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordMergeTest.java b/src/test/java/com/android/tools/r8/desugar/records/RecordMergeTest.java index cf3c955..d3816f8 100644 --- a/src/test/java/com/android/tools/r8/desugar/records/RecordMergeTest.java +++ b/src/test/java/com/android/tools/r8/desugar/records/RecordMergeTest.java
@@ -19,7 +19,6 @@ import com.android.tools.r8.errors.DuplicateTypesDiagnostic; import com.android.tools.r8.errors.MissingGlobalSyntheticsConsumerDiagnostic; import com.android.tools.r8.synthesis.globals.GlobalSyntheticsConsumerAndProvider; -import com.android.tools.r8.utils.InternalOptions.TestingOptions; import com.android.tools.r8.utils.StringUtils; import com.android.tools.r8.utils.codeinspector.CodeInspector; import java.nio.file.Path; @@ -62,7 +61,6 @@ testForD8(parameters.getBackend()) .addProgramClassFileData(PROGRAM_DATA_1) .setMinApi(parameters.getApiLevel()) - .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) .setIntermediate(true) .compileWithExpectedDiagnostics( diagnostics -> @@ -79,7 +77,6 @@ testForD8(parameters.getBackend()) .addProgramClassFileData(PROGRAM_DATA_1) .setMinApi(parameters.getApiLevel()) - .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) .setIntermediate(true) .apply(b -> b.getBuilder().setGlobalSyntheticsConsumer(globals1)) .compile() @@ -91,7 +88,6 @@ testForD8(parameters.getBackend()) .addProgramClassFileData(PROGRAM_DATA_2) .setMinApi(parameters.getApiLevel()) - .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) .setIntermediate(true) .apply(b -> b.getBuilder().setGlobalSyntheticsConsumer(globals2)) .compile() @@ -106,7 +102,6 @@ .addProgramFiles(output1, output2) .apply(b -> b.getBuilder().addGlobalSyntheticsResourceProviders(globals1, globals2)) .setMinApi(parameters.getApiLevel()) - .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) .compile() .inspect(this::assertHasRecordTag); @@ -121,7 +116,6 @@ testForD8(parameters.getBackend()) .addProgramClassFileData(PROGRAM_DATA_1) .setMinApi(parameters.getApiLevel()) - .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) .setIntermediate(true) .apply(b -> b.getBuilder().setGlobalSyntheticsConsumer(globals1)) .compile() @@ -133,7 +127,6 @@ .apply(b -> b.getBuilder().addGlobalSyntheticsResourceProviders(globals1)) .addProgramClassFileData(PROGRAM_DATA_2) .setMinApi(parameters.getApiLevel()) - .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) .compile(); result.run(parameters.getRuntime(), MAIN_TYPE_1).assertSuccessWithOutput(EXPECTED_RESULT_1); result.run(parameters.getRuntime(), MAIN_TYPE_2).assertSuccessWithOutput(EXPECTED_RESULT_2); @@ -145,7 +138,6 @@ testForD8(parameters.getBackend()) .addProgramClassFileData(PROGRAM_DATA_1) .setMinApi(parameters.getApiLevel()) - .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) .compile() .inspect(this::assertHasRecordTag) .writeToZip(); @@ -154,7 +146,6 @@ testForD8(parameters.getBackend()) .addProgramClassFileData(PROGRAM_DATA_2) .setMinApi(parameters.getApiLevel()) - .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) .compile() .inspect(this::assertHasRecordTag) .writeToZip(); @@ -165,7 +156,6 @@ testForD8(parameters.getBackend()) .addProgramFiles(output1, output2) .setMinApi(parameters.getApiLevel()) - .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) .compileWithExpectedDiagnostics( diagnostics -> diagnostics
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordReflectionTest.java b/src/test/java/com/android/tools/r8/desugar/records/RecordReflectionTest.java index 7b1096d..1bdc08d 100644 --- a/src/test/java/com/android/tools/r8/desugar/records/RecordReflectionTest.java +++ b/src/test/java/com/android/tools/r8/desugar/records/RecordReflectionTest.java
@@ -7,7 +7,6 @@ import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestRuntime.CfVm; -import com.android.tools.r8.utils.InternalOptions.TestingOptions; import com.android.tools.r8.utils.StringUtils; import java.util.List; import org.junit.Test; @@ -61,7 +60,6 @@ .addKeepRules("-keepattributes *") .addKeepRules("-keep class * extends java.lang.Record { private final <fields>; }") .addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp)) - .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) .compile() .inspect(RecordTestUtils::assertRecordsAreRecords) .enableJVMPreview()
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordShrinkFieldTest.java b/src/test/java/com/android/tools/r8/desugar/records/RecordShrinkFieldTest.java index 8dc4a82..2c426fc 100644 --- a/src/test/java/com/android/tools/r8/desugar/records/RecordShrinkFieldTest.java +++ b/src/test/java/com/android/tools/r8/desugar/records/RecordShrinkFieldTest.java
@@ -8,7 +8,6 @@ import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.utils.InternalOptions.TestingOptions; import com.android.tools.r8.utils.StringUtils; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector; @@ -47,7 +46,6 @@ testForD8(parameters.getBackend()) .addProgramClassFileData(PROGRAM_DATA) .setMinApi(parameters.getApiLevel()) - .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) .compile() .run(parameters.getRuntime(), MAIN_TYPE) .assertSuccessWithOutput(EXPECTED_RESULT_D8); @@ -59,7 +57,6 @@ .addProgramClassFileData(PROGRAM_DATA) .setMinApi(parameters.getApiLevel()) .addKeepMainRule(MAIN_TYPE) - .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) .compile() .inspect(this::assertSingleField) .run(parameters.getRuntime(), MAIN_TYPE) @@ -74,14 +71,12 @@ .setMinApi(parameters.getApiLevel()) .addKeepMainRule(MAIN_TYPE) .addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp)) - .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) .compile() .writeToZip(); testForR8(parameters.getBackend()) .addProgramFiles(desugared) .setMinApi(parameters.getApiLevel()) .addKeepMainRule(MAIN_TYPE) - .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) .compile() .inspect(this::assertSingleField) .run(parameters.getRuntime(), MAIN_TYPE)
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordWithMembersTest.java b/src/test/java/com/android/tools/r8/desugar/records/RecordWithMembersTest.java index eb8ef2d..07ae7cb 100644 --- a/src/test/java/com/android/tools/r8/desugar/records/RecordWithMembersTest.java +++ b/src/test/java/com/android/tools/r8/desugar/records/RecordWithMembersTest.java
@@ -8,7 +8,6 @@ import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestRuntime.CfVm; -import com.android.tools.r8.utils.InternalOptions.TestingOptions; import com.android.tools.r8.utils.StringUtils; import java.util.List; import org.junit.Test; @@ -52,7 +51,6 @@ testForD8(parameters.getBackend()) .addProgramClassFileData(PROGRAM_DATA) .setMinApi(parameters.getApiLevel()) - .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) .compile() .run(parameters.getRuntime(), MAIN_TYPE) .assertSuccessWithOutput(EXPECTED_RESULT); @@ -64,8 +62,7 @@ testForR8(parameters.getBackend()) .addProgramClassFileData(PROGRAM_DATA) .setMinApi(parameters.getApiLevel()) - .addKeepMainRule(MAIN_TYPE) - .addOptionsModification(TestingOptions::allowExperimentClassFileVersion); + .addKeepMainRule(MAIN_TYPE); if (parameters.isCfRuntime()) { builder .addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp))
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordWithNonMaterializableConstClassTest.java b/src/test/java/com/android/tools/r8/desugar/records/RecordWithNonMaterializableConstClassTest.java index f76b137..7ee039f 100644 --- a/src/test/java/com/android/tools/r8/desugar/records/RecordWithNonMaterializableConstClassTest.java +++ b/src/test/java/com/android/tools/r8/desugar/records/RecordWithNonMaterializableConstClassTest.java
@@ -6,7 +6,6 @@ import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.utils.InternalOptions.TestingOptions; import com.android.tools.r8.utils.StringUtils; import java.nio.file.Path; import java.util.List; @@ -56,7 +55,6 @@ .addProgramClassFileData(PROGRAM_DATA) .addProgramClassFileData(EXTRA_DATA) .setMinApi(parameters.getApiLevel()) - .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) .compile() .run(parameters.getRuntime(), MAIN_TYPE) .assertSuccessWithOutput(EXPECTED_RESULT_D8); @@ -70,7 +68,6 @@ .setMinApi(parameters.getApiLevel()) .addKeepMainRule(MAIN_TYPE) .addKeepRules("-keep class " + PRIVATE_CLASS_NAME) - .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) .compile() .run(parameters.getRuntime(), MAIN_TYPE) .assertSuccessWithOutput(EXPECTED_RESULT_R8); @@ -86,7 +83,6 @@ .addKeepMainRule(MAIN_TYPE) .addKeepRules("-keep class " + PRIVATE_CLASS_NAME) .addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp)) - .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) .compile() .writeToZip(); testForR8(parameters.getBackend()) @@ -94,7 +90,6 @@ .setMinApi(parameters.getApiLevel()) .addKeepMainRule(MAIN_TYPE) .addKeepRules("-keep class " + PRIVATE_CLASS_NAME) - .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) .compile() .run(parameters.getRuntime(), MAIN_TYPE) .assertSuccessWithOutput(EXPECTED_RESULT_R8);
diff --git a/src/test/java/com/android/tools/r8/desugar/records/SimpleRecordTest.java b/src/test/java/com/android/tools/r8/desugar/records/SimpleRecordTest.java index 212098c..e94b6b6 100644 --- a/src/test/java/com/android/tools/r8/desugar/records/SimpleRecordTest.java +++ b/src/test/java/com/android/tools/r8/desugar/records/SimpleRecordTest.java
@@ -10,11 +10,9 @@ import com.android.tools.r8.R8FullTestBuilder; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestRuntime.CfRuntime; import com.android.tools.r8.TestRuntime.CfVm; import com.android.tools.r8.synthesis.globals.GlobalSyntheticsConsumerAndProvider; import com.android.tools.r8.utils.AndroidApiLevel; -import com.android.tools.r8.utils.InternalOptions.TestingOptions; import com.android.tools.r8.utils.StringUtils; import java.nio.file.Path; import java.util.List; @@ -66,7 +64,6 @@ testForD8(parameters.getBackend()) .addProgramClassFileData(PROGRAM_DATA) .setMinApi(parameters.getApiLevel()) - .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) .compile() .inspectWithOptions( RecordTestUtils::assertNoJavaLangRecord, @@ -110,7 +107,6 @@ return testForD8(Backend.DEX) .addProgramClassFileData(PROGRAM_DATA) .setMinApi(parameters.getApiLevel()) - .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) .setIntermediate(true) .setIncludeClassesChecksum(true) .apply(b -> b.getBuilder().setGlobalSyntheticsConsumer(globalSyntheticsConsumer)) @@ -125,8 +121,7 @@ testForR8(parameters.getBackend()) .addProgramClassFileData(PROGRAM_DATA) .setMinApi(parameters.getApiLevel()) - .addKeepMainRule(MAIN_TYPE) - .addOptionsModification(TestingOptions::allowExperimentClassFileVersion); + .addKeepMainRule(MAIN_TYPE); if (parameters.isCfRuntime()) { builder .addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp)) @@ -153,8 +148,7 @@ .addProgramClassFileData(PROGRAM_DATA) .noMinification() .setMinApi(parameters.getApiLevel()) - .addKeepMainRule(MAIN_TYPE) - .addOptionsModification(TestingOptions::allowExperimentClassFileVersion); + .addKeepMainRule(MAIN_TYPE); if (parameters.isCfRuntime()) { builder .addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp))
diff --git a/src/test/java/com/android/tools/r8/desugar/records/UnusedRecordFieldTest.java b/src/test/java/com/android/tools/r8/desugar/records/UnusedRecordFieldTest.java index 24ae5d7..bd59f23 100644 --- a/src/test/java/com/android/tools/r8/desugar/records/UnusedRecordFieldTest.java +++ b/src/test/java/com/android/tools/r8/desugar/records/UnusedRecordFieldTest.java
@@ -4,8 +4,6 @@ package com.android.tools.r8.desugar.records; -import static com.android.tools.r8.utils.InternalOptions.TestingOptions; - import com.android.tools.r8.R8FullTestBuilder; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; @@ -51,7 +49,6 @@ testForD8(parameters.getBackend()) .addProgramClassFileData(PROGRAM_DATA) .setMinApi(parameters.getApiLevel()) - .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) .compile() .run(parameters.getRuntime(), MAIN_TYPE) .assertSuccessWithOutput(EXPECTED_RESULT); @@ -64,8 +61,7 @@ .addProgramClassFileData(PROGRAM_DATA) .setMinApi(parameters.getApiLevel()) .addKeepRules("-keep class records.UnusedRecordField { *; }") - .addKeepMainRule(MAIN_TYPE) - .addOptionsModification(TestingOptions::allowExperimentClassFileVersion); + .addKeepMainRule(MAIN_TYPE); if (parameters.isCfRuntime()) { builder .addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp))
diff --git a/src/test/java/com/android/tools/r8/desugar/records/UnusedRecordMethodTest.java b/src/test/java/com/android/tools/r8/desugar/records/UnusedRecordMethodTest.java index 702cc75..70ed745 100644 --- a/src/test/java/com/android/tools/r8/desugar/records/UnusedRecordMethodTest.java +++ b/src/test/java/com/android/tools/r8/desugar/records/UnusedRecordMethodTest.java
@@ -4,8 +4,6 @@ package com.android.tools.r8.desugar.records; -import static com.android.tools.r8.utils.InternalOptions.TestingOptions; - import com.android.tools.r8.R8FullTestBuilder; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; @@ -51,7 +49,6 @@ testForD8(parameters.getBackend()) .addProgramClassFileData(PROGRAM_DATA) .setMinApi(parameters.getApiLevel()) - .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) .compile() .run(parameters.getRuntime(), MAIN_TYPE) .assertSuccessWithOutput(EXPECTED_RESULT); @@ -64,8 +61,7 @@ .addProgramClassFileData(PROGRAM_DATA) .setMinApi(parameters.getApiLevel()) .addKeepRules("-keep class records.UnusedRecordMethod { *; }") - .addKeepMainRule(MAIN_TYPE) - .addOptionsModification(TestingOptions::allowExperimentClassFileVersion); + .addKeepMainRule(MAIN_TYPE); if (parameters.isCfRuntime()) { builder .addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp))
diff --git a/src/test/java/com/android/tools/r8/desugar/records/UnusedRecordReflectionTest.java b/src/test/java/com/android/tools/r8/desugar/records/UnusedRecordReflectionTest.java index 0687934..066c68b 100644 --- a/src/test/java/com/android/tools/r8/desugar/records/UnusedRecordReflectionTest.java +++ b/src/test/java/com/android/tools/r8/desugar/records/UnusedRecordReflectionTest.java
@@ -4,8 +4,6 @@ package com.android.tools.r8.desugar.records; -import static com.android.tools.r8.utils.InternalOptions.TestingOptions; - import com.android.tools.r8.R8FullTestBuilder; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; @@ -51,7 +49,6 @@ testForD8(parameters.getBackend()) .addProgramClassFileData(PROGRAM_DATA) .setMinApi(parameters.getApiLevel()) - .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) .compile() .run(parameters.getRuntime(), MAIN_TYPE) .assertSuccessWithOutput(EXPECTED_RESULT); @@ -64,8 +61,7 @@ .addProgramClassFileData(PROGRAM_DATA) .setMinApi(parameters.getApiLevel()) .addKeepRules("-keep class records.UnusedRecordReflection { *; }") - .addKeepMainRule(MAIN_TYPE) - .addOptionsModification(TestingOptions::allowExperimentClassFileVersion); + .addKeepMainRule(MAIN_TYPE); if (parameters.isCfRuntime()) { builder .addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp))
diff --git a/src/test/java/com/android/tools/r8/desugar/sealed/SealedAttributeClasspathTest.java b/src/test/java/com/android/tools/r8/desugar/sealed/SealedAttributeClasspathTest.java index a74132f..facd808 100644 --- a/src/test/java/com/android/tools/r8/desugar/sealed/SealedAttributeClasspathTest.java +++ b/src/test/java/com/android/tools/r8/desugar/sealed/SealedAttributeClasspathTest.java
@@ -10,7 +10,6 @@ import com.android.tools.r8.TestCompilerBuilder; import com.android.tools.r8.TestParameters; import com.android.tools.r8.examples.jdk17.Sealed; -import com.android.tools.r8.utils.InternalOptions.TestingOptions; import java.util.List; import org.junit.Test; import org.junit.runner.RunWith; @@ -35,7 +34,6 @@ .addClasspathFiles(Sealed.jar()) .addInnerClasses(getClass()) .setMinApi(parameters.getApiLevel()) - .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) .run(parameters.getRuntime(), TestRunner.class) .assertSuccessWithOutputLines("Hello, world!"); }
diff --git a/src/test/java/com/android/tools/r8/desugar/sealed/SealedAttributeLibraryTest.java b/src/test/java/com/android/tools/r8/desugar/sealed/SealedAttributeLibraryTest.java index 4eb4bd2..57684f3 100644 --- a/src/test/java/com/android/tools/r8/desugar/sealed/SealedAttributeLibraryTest.java +++ b/src/test/java/com/android/tools/r8/desugar/sealed/SealedAttributeLibraryTest.java
@@ -10,7 +10,6 @@ import com.android.tools.r8.TestCompilerBuilder; import com.android.tools.r8.TestParameters; import com.android.tools.r8.examples.jdk17.Sealed; -import com.android.tools.r8.utils.InternalOptions.TestingOptions; import java.util.List; import org.junit.Test; import org.junit.runner.RunWith; @@ -36,7 +35,6 @@ .addLibraryFiles(Sealed.jar()) .addInnerClasses(getClass()) .setMinApi(parameters.getApiLevel()) - .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) .run(parameters.getRuntime(), TestRunner.class) .assertSuccessWithOutputLines("Hello, world!"); }
diff --git a/src/test/java/com/android/tools/r8/examples/abstractmethodremoval/AbstractMethodRemovalTestRunner.java b/src/test/java/com/android/tools/r8/examples/abstractmethodremoval/AbstractMethodRemovalTestRunner.java index 20cda19..39d93b9 100644 --- a/src/test/java/com/android/tools/r8/examples/abstractmethodremoval/AbstractMethodRemovalTestRunner.java +++ b/src/test/java/com/android/tools/r8/examples/abstractmethodremoval/AbstractMethodRemovalTestRunner.java
@@ -6,7 +6,6 @@ import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.ToolHelper.DexVm.Version; import com.android.tools.r8.examples.abstractmethodremoval.a.PackageBase; import com.android.tools.r8.examples.abstractmethodremoval.a.Public; import com.android.tools.r8.examples.abstractmethodremoval.b.Impl1; @@ -66,11 +65,6 @@ .addKeepMainRule(getMainClass()) .setMinApi(parameters.getApiLevel()) .run(parameters.getRuntime(), getMainClass()) - .applyIf( - // TODO(b/227302144): The program should not fail after R8. - parameters.isDexRuntime() - && parameters.asDexRuntime().getVersion().isNewerThanOrEqual(Version.V5_1_1), - r -> r.assertFailure(), - r -> r.assertSuccessWithOutput(getExpected())); + .assertSuccessWithOutput(getExpected()); } }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/MemberValuePropagationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/MemberValuePropagationTest.java index 5162100..1433f2e 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/MemberValuePropagationTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/MemberValuePropagationTest.java
@@ -7,7 +7,6 @@ import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestParametersBuilder; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.utils.FileUtils; import com.android.tools.r8.utils.codeinspector.ClassSubject; @@ -38,7 +37,7 @@ @Parameterized.Parameters(name = "Backend: {1}") public static List<Object[]> data() { return buildParameters( - TestParametersBuilder.builder().withNoneRuntime().build(), ToolHelper.getBackends()); + TestParameters.builder().withNoneRuntime().build(), ToolHelper.getBackends()); } public MemberValuePropagationTest(TestParameters parameters, TestBase.Backend backend) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenInterfaceCheckCastTest.java b/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenInterfaceCheckCastTest.java index c756f8c..e8d4e5f 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenInterfaceCheckCastTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenInterfaceCheckCastTest.java
@@ -41,7 +41,7 @@ .addProgramClasses(getProgramClasses()) .addProgramClassFileData(getTransformedMainClass()) .run(parameters.getRuntime(), Main.class) - .assertSuccessWithOutputLines(getExpectedOutputLines()); + .assertSuccessWithOutputLines(getExpectedOutputLines(false)); } @Test @@ -52,7 +52,7 @@ .addProgramClassFileData(getTransformedMainClass()) .setMinApi(parameters.getApiLevel()) .run(parameters.getRuntime(), Main.class) - .assertSuccessWithOutputLines(getExpectedOutputLines()); + .assertSuccessWithOutputLines(getExpectedOutputLines(false)); } @Test @@ -68,7 +68,7 @@ .enableNoVerticalClassMergingAnnotations() .setMinApi(parameters.getApiLevel()) .run(parameters.getRuntime(), Main.class) - .assertSuccessWithOutputLines(getExpectedOutputLines()); + .assertSuccessWithOutputLines(getExpectedOutputLines(true)); } private List<Class<?>> getProgramClasses() { @@ -102,7 +102,11 @@ .transform(); } - private List<String> getExpectedOutputLines() { + private List<String> getExpectedOutputLines(boolean isR8) { + if (isR8) { + // TODO(b/214496607): R8 should not optimize the check-cast instruction since I is open. + return ImmutableList.of("OK", "OK"); + } if (parameters.isDexRuntime() && parameters .getDexRuntimeVersion()
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenInterfaceInliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenInterfaceInliningTest.java index 4eaa6a4..e2b4a29 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenInterfaceInliningTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenInterfaceInliningTest.java
@@ -71,7 +71,8 @@ .enableNoVerticalClassMergingAnnotations() .setMinApi(parameters.getApiLevel()) .run(parameters.getRuntime(), Main.class) - .assertSuccessWithOutputLines(getExpectedOutputLines()); + // TODO(b/214496607): Should succeed with the expected output. + .assertFailureWithErrorThatThrows(ClassCastException.class); } private List<Class<?>> getProgramClasses() {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenInterfaceInstanceofTest.java b/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenInterfaceInstanceofTest.java index dac3848..5a57dc4 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenInterfaceInstanceofTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenInterfaceInstanceofTest.java
@@ -41,7 +41,7 @@ .addProgramClasses(getProgramClasses()) .addProgramClassFileData(getTransformedMainClass()) .run(parameters.getRuntime(), Main.class) - .assertSuccessWithOutputLines(getExpectedOutputLines()); + .assertSuccessWithOutputLines(getExpectedOutputLines(false)); } @Test @@ -52,7 +52,7 @@ .addProgramClassFileData(getTransformedMainClass()) .setMinApi(parameters.getApiLevel()) .run(parameters.getRuntime(), Main.class) - .assertSuccessWithOutputLines(getExpectedOutputLines()); + .assertSuccessWithOutputLines(getExpectedOutputLines(false)); } @Test @@ -68,7 +68,7 @@ .enableNoVerticalClassMergingAnnotations() .setMinApi(parameters.getApiLevel()) .run(parameters.getRuntime(), Main.class) - .assertSuccessWithOutputLines(getExpectedOutputLines()); + .assertSuccessWithOutputLines(getExpectedOutputLines(true)); } private List<Class<?>> getProgramClasses() { @@ -95,7 +95,11 @@ .transform(); } - private List<String> getExpectedOutputLines() { + private List<String> getExpectedOutputLines(boolean isR8) { + if (isR8) { + // TODO(b/214496607): R8 should not optimize the instanceof instruction since I is open. + return ImmutableList.of("true", "true"); + } if (parameters.isDexRuntime() && parameters.getDexRuntimeVersion().isEqualTo(Version.V7_0_0)) { return ImmutableList.of("true", "true"); }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenUninstantiatedInterfaceInstanceofTest.java b/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenUninstantiatedInterfaceInstanceofTest.java index 606871f..a729899 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenUninstantiatedInterfaceInstanceofTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenUninstantiatedInterfaceInstanceofTest.java
@@ -40,7 +40,7 @@ .addProgramClasses(getProgramClasses()) .addProgramClassFileData(getTransformedMainClass()) .run(parameters.getRuntime(), Main.class) - .assertSuccessWithOutputLines(getExpectedOutputLines()); + .assertSuccessWithOutputLines(getExpectedOutputLines(false)); } @Test @@ -51,7 +51,7 @@ .addProgramClassFileData(getTransformedMainClass()) .setMinApi(parameters.getApiLevel()) .run(parameters.getRuntime(), Main.class) - .assertSuccessWithOutputLines(getExpectedOutputLines()); + .assertSuccessWithOutputLines(getExpectedOutputLines(false)); } @Test @@ -65,7 +65,7 @@ .enableInliningAnnotations() .setMinApi(parameters.getApiLevel()) .run(parameters.getRuntime(), Main.class) - .assertSuccessWithOutputLines(getExpectedOutputLines()); + .assertSuccessWithOutputLines(getExpectedOutputLines(true)); } private List<Class<?>> getProgramClasses() { @@ -92,7 +92,11 @@ .transform(); } - private List<String> getExpectedOutputLines() { + private List<String> getExpectedOutputLines(boolean isR8) { + if (isR8) { + // TODO(b/214496607): R8 should not optimize the instanceof instruction since I is open. + return ImmutableList.of("true"); + } if (parameters.isDexRuntime()) { if (parameters.getDexRuntimeVersion().isEqualTo(Version.V7_0_0) || parameters.getDexRuntimeVersion().isEqualTo(Version.V13_MASTER)) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/lambda/LambdaMethodInliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/lambda/LambdaMethodInliningTest.java index a3d1e6b..a63addb 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/lambda/LambdaMethodInliningTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/lambda/LambdaMethodInliningTest.java
@@ -65,7 +65,7 @@ assertTrue( testClassMethodSubject .streamInstructions() - .noneMatch( + .anyMatch( instruction -> instruction.isInvokeVirtual() && instruction.getMethod().toSourceString().contains("println")));
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/LambdaInstantiatedTypeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/LambdaInstantiatedTypeTest.java index 1b3e93e..4d032c7 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/LambdaInstantiatedTypeTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/LambdaInstantiatedTypeTest.java
@@ -9,7 +9,6 @@ import com.android.tools.r8.NeverInline; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestParametersBuilder; import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.utils.StringUtils; import com.android.tools.r8.utils.codeinspector.ClassSubject; @@ -30,7 +29,7 @@ @Parameters(name = "{0}") public static TestParametersCollection data() { - return TestParametersBuilder.builder().withAllRuntimesAndApiLevels().build(); + return TestParameters.builder().withAllRuntimesAndApiLevels().build(); } public LambdaInstantiatedTypeTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentsTestBase.java b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentsTestBase.java index d88a978..822f9f9 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentsTestBase.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentsTestBase.java
@@ -11,7 +11,6 @@ import com.android.tools.r8.R8FullTestBuilder; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestParametersBuilder; import com.android.tools.r8.utils.BooleanUtils; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector; @@ -36,8 +35,7 @@ @Parameters(name = "{0}, minification:{1}") public static Collection<Object[]> data() { return buildParameters( - TestParametersBuilder.builder().withAllRuntimesAndApiLevels().build(), - BooleanUtils.values()); + TestParameters.builder().withAllRuntimesAndApiLevels().build(), BooleanUtils.values()); } public void configure(R8FullTestBuilder builder) {
diff --git a/src/test/java/com/android/tools/r8/jasmin/InvalidClassNames.java b/src/test/java/com/android/tools/r8/jasmin/InvalidClassNames.java index bd462b7..b8378c3 100644 --- a/src/test/java/com/android/tools/r8/jasmin/InvalidClassNames.java +++ b/src/test/java/com/android/tools/r8/jasmin/InvalidClassNames.java
@@ -3,8 +3,7 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.jasmin; - -import com.android.tools.r8.ToolHelper; +import com.android.tools.r8.TestParameters; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -23,32 +22,19 @@ @Parameters(name = "\"{0}\", jvm: {1}, art: {2}") public static Collection<Object[]> data() { Collection<Object[]> data = new ArrayList<>(); - data.addAll(NameTestBase.getCommonNameTestData(true)); - data.addAll( - Arrays.asList( - new Object[][] { - {new TestString("a/b/c/a/D/"), true, false}, - { - new TestString("a<b"), - !ToolHelper.isWindows() && !ToolHelper.isJava9Runtime(), - false - }, - { - new TestString("a>b"), - !ToolHelper.isWindows() && !ToolHelper.isJava9Runtime(), - false - }, - { - new TestString("<a>b"), - !ToolHelper.isWindows() && !ToolHelper.isJava9Runtime(), - false - }, - { - new TestString("<a>"), - !ToolHelper.isWindows() && !ToolHelper.isJava9Runtime(), - false - } - })); + for (TestParameters parameter : TestParameters.justNoneRuntime()) { + parameter.assertNoneRuntime(); + data.addAll(NameTestBase.getCommonNameTestData()); + data.addAll( + Arrays.asList( + new Object[][] { + {new TestString("a/b/c/a/D/"), true, false}, + {new TestString("a<b"), false, false}, + {new TestString("a>b"), false, false}, + {new TestString("<a>b"), false, false}, + {new TestString("<a>"), false, false} + })); + } return data; }
diff --git a/src/test/java/com/android/tools/r8/jasmin/InvalidFieldNames.java b/src/test/java/com/android/tools/r8/jasmin/InvalidFieldNames.java index 8f680ee..cea2557 100644 --- a/src/test/java/com/android/tools/r8/jasmin/InvalidFieldNames.java +++ b/src/test/java/com/android/tools/r8/jasmin/InvalidFieldNames.java
@@ -3,7 +3,7 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.jasmin; -import com.android.tools.r8.ToolHelper; +import com.android.tools.r8.TestParameters; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -21,17 +21,20 @@ @Parameters(name = "\"{0}\", jvm: {1}, art: {2}") public static Collection<Object[]> data() { Collection<Object[]> data = new ArrayList<>(); - data.addAll(NameTestBase.getCommonNameTestData(false)); - data.addAll( - Arrays.asList( - new Object[][] { - {new TestString("a/b"), false, false}, - {new TestString("<a"), !ToolHelper.isJava9Runtime(), false}, - {new TestString("a>"), !ToolHelper.isJava9Runtime(), false}, - {new TestString("a<b>"), !ToolHelper.isJava9Runtime(), false}, - {new TestString("<a>b"), !ToolHelper.isJava9Runtime(), false}, - {new TestString("<a>"), false, true} - })); + for (TestParameters parameter : TestParameters.justNoneRuntime()) { + parameter.assertNoneRuntime(); + data.addAll(NameTestBase.getCommonNameTestData()); + data.addAll( + Arrays.asList( + new Object[][] { + {new TestString("a/b"), false, false}, + {new TestString("<a"), false, false}, + {new TestString("a>"), false, false}, + {new TestString("a<b>"), false, false}, + {new TestString("<a>b"), false, false}, + {new TestString("<a>"), false, true} + })); + } return data; }
diff --git a/src/test/java/com/android/tools/r8/jasmin/InvalidMethodNames.java b/src/test/java/com/android/tools/r8/jasmin/InvalidMethodNames.java index fa2882e..0327b76 100644 --- a/src/test/java/com/android/tools/r8/jasmin/InvalidMethodNames.java +++ b/src/test/java/com/android/tools/r8/jasmin/InvalidMethodNames.java
@@ -3,8 +3,7 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.jasmin; - -import com.android.tools.r8.ToolHelper; +import com.android.tools.r8.TestParameters; import com.google.common.collect.ImmutableList; import java.util.ArrayList; import java.util.Arrays; @@ -27,15 +26,18 @@ @Parameters(name = "\"{0}\", jvm: {1}, art: {2}") public static Collection<Object[]> data() { Collection<Object[]> data = new ArrayList<>(); - data.addAll(NameTestBase.getCommonNameTestData(false)); - data.addAll( - Arrays.asList( - new Object[][] { - {new TestString("a/b"), false, false}, - {new TestString("<a"), false, false}, - {new TestString("a>"), !ToolHelper.isJava9Runtime(), false}, - {new TestString("<a>"), false, false} - })); + for (TestParameters parameter : TestParameters.justNoneRuntime()) { + parameter.assertNoneRuntime(); + data.addAll(NameTestBase.getCommonNameTestData()); + data.addAll( + Arrays.asList( + new Object[][] { + {new TestString("a/b"), false, false}, + {new TestString("<a"), false, false}, + {new TestString("a>"), false, false}, + {new TestString("<a>"), false, false} + })); + } return data; }
diff --git a/src/test/java/com/android/tools/r8/jasmin/NameTestBase.java b/src/test/java/com/android/tools/r8/jasmin/NameTestBase.java index 594bdbe..0b335a9 100644 --- a/src/test/java/com/android/tools/r8/jasmin/NameTestBase.java +++ b/src/test/java/com/android/tools/r8/jasmin/NameTestBase.java
@@ -39,51 +39,43 @@ // - Name (String) to test (can be class name, field name, method name). // - boolean, whether it runs on the JVM. // - boolean, whether it runs on the ART. - static Collection<Object[]> getCommonNameTestData(boolean classNames) { - - boolean windowsSensitive = classNames && ToolHelper.isWindows(); + static Collection<Object[]> getCommonNameTestData() { boolean supportSpaces = ToolHelper.getMinApiLevelForDexVm().getLevel() >= AndroidApiLevel.R.getLevel(); - boolean java9 = ToolHelper.isJava9Runtime(); - return Arrays.asList( new Object[][] { {new TestString("azAZ09$_"), true, true}, - {new TestString("_"), !java9, true}, - {new TestString("a-b"), !java9, true}, - {new TestString("\u00a0"), !java9, supportSpaces}, - {new TestString("\u00a1"), !java9, true}, - {new TestString("\u1fff"), !java9, true}, - {new TestString("\u2000"), !windowsSensitive && !java9, supportSpaces}, - {new TestString("\u200f"), !windowsSensitive && !java9, false}, - {new TestString("\u2010"), !windowsSensitive && !java9, true}, - {new TestString("\u2027"), !windowsSensitive && !java9, true}, - {new TestString("\u2028"), !windowsSensitive && !java9, false}, - {new TestString("\u202f"), !windowsSensitive && !java9, supportSpaces}, - {new TestString("\u2030"), !windowsSensitive && !java9, true}, - {new TestString("\ud7ff"), !windowsSensitive && !java9, true}, - {new TestString("\ue000"), !windowsSensitive && !java9, true}, - {new TestString("\uffef"), !windowsSensitive && !java9, true}, - {new TestString("\ufff0"), !windowsSensitive && !java9, false}, - {new TestString("\uffff"), !windowsSensitive && !java9, false}, + {new TestString("_"), false, true}, + {new TestString("a-b"), false, true}, + {new TestString("\u00a0"), false, supportSpaces}, + {new TestString("\u00a1"), false, true}, + {new TestString("\u1fff"), false, true}, + {new TestString("\u2000"), false, supportSpaces}, + {new TestString("\u200f"), false, false}, + {new TestString("\u2010"), false, true}, + {new TestString("\u2027"), false, true}, + {new TestString("\u2028"), false, false}, + {new TestString("\u202f"), false, supportSpaces}, + {new TestString("\u2030"), false, true}, + {new TestString("\ud7ff"), false, true}, + {new TestString("\ue000"), false, true}, + {new TestString("\uffef"), false, true}, + {new TestString("\ufff0"), false, false}, + {new TestString("\uffff"), false, false}, // Standalone high and low surrogates. - {new TestString("\ud800"), !classNames && !java9, false}, - {new TestString("\udbff"), !classNames && !java9, false}, - {new TestString("\udc00"), !classNames && !java9, false}, - {new TestString("\udfff"), !classNames && !java9, false}, + {new TestString("\ud800"), false, false}, + {new TestString("\udbff"), false, false}, + {new TestString("\udc00"), false, false}, + {new TestString("\udfff"), false, false}, // Single and double code points above 0x10000. {new TestString("\ud800\udc00"), true, true}, {new TestString("\ud800\udcfa"), true, true}, - {new TestString("\ud800\udcfb"), !windowsSensitive && !java9, true}, - {new TestString("\udbff\udfff"), !windowsSensitive && !java9, true}, + {new TestString("\ud800\udcfb"), false, true}, + {new TestString("\udbff\udfff"), false, true}, {new TestString("\ud800\udc00\ud800\udcfa"), true, true}, - { - new TestString("\ud800\udc00\udbff\udfff"), - !windowsSensitive && !java9, - true - } + {new TestString("\ud800\udc00\udbff\udfff"), false, true} }); }
diff --git a/src/test/java/com/android/tools/r8/java_language/pattern_matching_for_instenceof/PattternMatchingForInstanceOfTest.java b/src/test/java/com/android/tools/r8/java_language/pattern_matching_for_instenceof/PattternMatchingForInstanceOfTest.java index e81d20c..b40cca0 100644 --- a/src/test/java/com/android/tools/r8/java_language/pattern_matching_for_instenceof/PattternMatchingForInstanceOfTest.java +++ b/src/test/java/com/android/tools/r8/java_language/pattern_matching_for_instenceof/PattternMatchingForInstanceOfTest.java
@@ -9,7 +9,6 @@ import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestRuntime.CfVm; import com.android.tools.r8.examples.jdk17.PatternMatchingForInstanceof; -import com.android.tools.r8.utils.InternalOptions.TestingOptions; import com.google.common.collect.ImmutableList; import java.nio.file.Path; import java.util.List; @@ -50,7 +49,6 @@ testForD8(parameters.getBackend()) .addProgramFiles(JAR) .setMinApi(parameters.getApiLevel()) - .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) .compile() .run(parameters.getRuntime(), MAIN) .assertSuccessWithOutputLines(EXPECTED); @@ -62,8 +60,7 @@ testForR8(parameters.getBackend()) .addProgramFiles(JAR) .setMinApi(parameters.getApiLevel()) - .addKeepMainRule(MAIN) - .addOptionsModification(TestingOptions::allowExperimentClassFileVersion); + .addKeepMainRule(MAIN); if (parameters.getBackend().isDex()) { builder.run(parameters.getRuntime(), MAIN).assertSuccessWithOutputLines(EXPECTED); } else {
diff --git a/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java b/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java index c14b357..488271b 100644 --- a/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java
@@ -73,9 +73,8 @@ .count(); long paramNullCheckCount = countCall(testMethod, "Intrinsics", "checkParameterIsNotNull"); - // TODO(b/214496607): Should be one after Iterator#hasNext, and another in the filter - // predicate: sinceYear != null. - assertEquals(testParameters.isCfRuntime() ? 5 : 2, ifzCount); + // One after Iterator#hasNext, and another in the filter predicate: sinceYear != null. + assertEquals(2, ifzCount); assertEquals(0, paramNullCheckCount); }); }
diff --git a/src/test/java/com/android/tools/r8/resolution/PublicFieldInnerClassTest.java b/src/test/java/com/android/tools/r8/resolution/PublicFieldInnerClassTest.java index 4fb3d8b..11f560d 100644 --- a/src/test/java/com/android/tools/r8/resolution/PublicFieldInnerClassTest.java +++ b/src/test/java/com/android/tools/r8/resolution/PublicFieldInnerClassTest.java
@@ -6,7 +6,6 @@ import com.android.tools.r8.CompilationMode; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestParametersBuilder; import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.utils.StringUtils; import org.junit.Test; @@ -75,7 +74,7 @@ @Parameterized.Parameters(name = "Backend: {0}") public static TestParametersCollection data() { - return TestParametersBuilder.builder().withAllRuntimesAndApiLevels().build(); + return TestParameters.builder().withAllRuntimesAndApiLevels().build(); } public PublicFieldInnerClassTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/resolution/SingleTargetExecutionTest.java b/src/test/java/com/android/tools/r8/resolution/SingleTargetExecutionTest.java index 1446e76..4726fe4 100644 --- a/src/test/java/com/android/tools/r8/resolution/SingleTargetExecutionTest.java +++ b/src/test/java/com/android/tools/r8/resolution/SingleTargetExecutionTest.java
@@ -84,6 +84,10 @@ } private String getExpectedOutput() { + String icceOrNot = + enableInliningAnnotations || !parameters.canUseDefaultAndStaticInterfaceMethods() + ? "ICCE" + : "InterfaceWithDefault"; return StringUtils.lines( "SubSubClassOne", "SubSubClassOne", @@ -93,7 +97,7 @@ "com.android.tools.r8.resolution.singletarget.one.AbstractSubClass", "InterfaceWithDefault", "InterfaceWithDefault", - "ICCE", + icceOrNot, "com.android.tools.r8.resolution.singletarget.one.SubSubClassTwo", "com.android.tools.r8.resolution.singletarget.one.SubSubClassTwo", "AbstractTopClass", @@ -105,7 +109,7 @@ "InterfaceWithDefault", "InterfaceWithDefault", "InterfaceWithDefault", - "ICCE", + icceOrNot, "InterfaceWithDefault", "com.android.tools.r8.resolution.singletarget.one.SubSubClassTwo", "InterfaceWithDefault",
diff --git a/src/test/java/com/android/tools/r8/resolution/b123730538/B123730538.java b/src/test/java/com/android/tools/r8/resolution/b123730538/B123730538.java index c61d7fa..029fc63 100644 --- a/src/test/java/com/android/tools/r8/resolution/b123730538/B123730538.java +++ b/src/test/java/com/android/tools/r8/resolution/b123730538/B123730538.java
@@ -11,7 +11,6 @@ import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestParametersBuilder; import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.resolution.b123730538.runner.PublicClassExtender; @@ -40,7 +39,7 @@ @Parameterized.Parameters(name = "Backend: {0}") public static TestParametersCollection data() { - return TestParametersBuilder.builder().withAllRuntimesAndApiLevels().build(); + return TestParameters.builder().withAllRuntimesAndApiLevels().build(); } public B123730538(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/resource/KeepDirectoriesTest.java b/src/test/java/com/android/tools/r8/resource/KeepDirectoriesTest.java index 416835f..fedfb29 100644 --- a/src/test/java/com/android/tools/r8/resource/KeepDirectoriesTest.java +++ b/src/test/java/com/android/tools/r8/resource/KeepDirectoriesTest.java
@@ -16,7 +16,6 @@ import com.android.tools.r8.DiagnosticsHandler; import com.android.tools.r8.R8Command; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestParametersBuilder; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.naming.ClassNameMapper; @@ -55,7 +54,7 @@ return buildParameters( ToolHelper.getBackends(), BooleanUtils.values(), - TestParametersBuilder.builder().withNoneRuntime().build()); + TestParameters.builder().withNoneRuntime().build()); } public KeepDirectoriesTest(Backend backend, boolean minify, TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTestBase.java b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTestBase.java index 4dcd86a..9196e6a 100644 --- a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTestBase.java +++ b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTestBase.java
@@ -6,7 +6,6 @@ import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestParametersBuilder; import com.android.tools.r8.TestParametersCollection; import org.junit.Test; import org.junit.runner.RunWith; @@ -18,7 +17,7 @@ @Parameters(name = "{0}") public static TestParametersCollection data() { - return TestParametersBuilder.builder().withNoneRuntime().build(); + return TestParameters.builder().withNoneRuntime().build(); } public RetraceApiTestBase(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/rewrite/switchmaps/RewriteSwitchMapsTest.java b/src/test/java/com/android/tools/r8/rewrite/switchmaps/RewriteSwitchMapsTest.java index cfffbac..c6c5cc2 100644 --- a/src/test/java/com/android/tools/r8/rewrite/switchmaps/RewriteSwitchMapsTest.java +++ b/src/test/java/com/android/tools/r8/rewrite/switchmaps/RewriteSwitchMapsTest.java
@@ -8,7 +8,6 @@ import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestParametersBuilder; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.utils.AndroidApiLevel; import com.google.common.collect.ImmutableList; @@ -26,7 +25,7 @@ @Parameterized.Parameters(name = "{0}, backend: {1}") public static List<Object[]> data() { return buildParameters( - TestParametersBuilder.builder().withNoneRuntime().build(), ToolHelper.getBackends()); + TestParameters.builder().withNoneRuntime().build(), ToolHelper.getBackends()); } public RewriteSwitchMapsTest(TestParameters parameters, Backend backend) {
diff --git a/src/test/java/com/android/tools/r8/shaking/TreeShakingSpecificTest.java b/src/test/java/com/android/tools/r8/shaking/TreeShakingSpecificTest.java index f42443d..eacb522 100644 --- a/src/test/java/com/android/tools/r8/shaking/TreeShakingSpecificTest.java +++ b/src/test/java/com/android/tools/r8/shaking/TreeShakingSpecificTest.java
@@ -14,7 +14,6 @@ import com.android.tools.r8.CompilationFailedException; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestParametersBuilder; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.diagnostic.MissingDefinitionsDiagnostic; import com.android.tools.r8.utils.AndroidApiLevel; @@ -43,8 +42,7 @@ @Parameters(name = "Backend: {1}") public static List<Object[]> data() { - return buildParameters( - TestParametersBuilder.builder().withNoneRuntime().build(), Backend.values()); + return buildParameters(TestParameters.builder().withNoneRuntime().build(), Backend.values()); } public TreeShakingSpecificTest(TestParameters parameters, Backend backend) {
diff --git a/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByReachableSubclassTest.java b/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByReachableSubclassTest.java index b848b1c..ae37674 100644 --- a/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByReachableSubclassTest.java +++ b/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByReachableSubclassTest.java
@@ -11,7 +11,6 @@ import com.android.tools.r8.NoVerticalClassMerging; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestParametersBuilder; import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.origin.Origin; import com.android.tools.r8.references.Reference; @@ -33,7 +32,7 @@ @Parameters(name = "{0}") public static TestParametersCollection data() { - return TestParametersBuilder.builder().withCfRuntimes().build(); + return TestParameters.builder().withCfRuntimes().build(); } public KeptByReachableSubclassTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptSingletonIsNotCyclicTest.java b/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptSingletonIsNotCyclicTest.java index b34d36d..f6f59dd 100644 --- a/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptSingletonIsNotCyclicTest.java +++ b/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptSingletonIsNotCyclicTest.java
@@ -14,7 +14,6 @@ import com.android.tools.r8.NeverPropagateValue; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestParametersBuilder; import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.origin.Origin; import com.android.tools.r8.references.ClassReference; @@ -41,7 +40,7 @@ @Parameters(name = "{0}") public static TestParametersCollection data() { - return TestParametersBuilder.builder().withCfRuntimes().build(); + return TestParameters.builder().withCfRuntimes().build(); } public KeptSingletonIsNotCyclicTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptSubclassKeepsSuperTest.java b/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptSubclassKeepsSuperTest.java index 12d1f35..cd34b55 100644 --- a/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptSubclassKeepsSuperTest.java +++ b/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptSubclassKeepsSuperTest.java
@@ -11,7 +11,6 @@ import com.android.tools.r8.NoVerticalClassMerging; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestParametersBuilder; import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.origin.Origin; import com.android.tools.r8.references.Reference; @@ -33,7 +32,7 @@ @Parameters(name = "{0}") public static TestParametersCollection data() { - return TestParametersBuilder.builder().withCfRuntimes().build(); + return TestParameters.builder().withCfRuntimes().build(); } public KeptSubclassKeepsSuperTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/shaking/proxy/MockitoTest.java b/src/test/java/com/android/tools/r8/shaking/proxy/MockitoTest.java index 1464e82..d31da79 100644 --- a/src/test/java/com/android/tools/r8/shaking/proxy/MockitoTest.java +++ b/src/test/java/com/android/tools/r8/shaking/proxy/MockitoTest.java
@@ -3,6 +3,7 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.shaking.proxy; +import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed; import static org.hamcrest.MatcherAssert.assertThat; @@ -57,11 +58,8 @@ .inspector(); ClassSubject itf = inspector.clazz(M_I); assertThat(itf, isPresent()); - // TODO(b/214496607): This could be removed as a result of devirtualization, but this only - // happens if the call site is reprocessed, since we don't have knowledge of open/closed - // interfaces until the second optimization pass. MethodSubject mtd = itf.uniqueMethodWithName("onEnterForeground"); - assertThat(mtd, isPresent()); + assertThat(mtd, isAbsent()); } @Test
diff --git a/src/test/java/com/android/tools/r8/shaking/proxy/ProxiesTest.java b/src/test/java/com/android/tools/r8/shaking/proxy/ProxiesTest.java index c850ae8..047a109 100644 --- a/src/test/java/com/android/tools/r8/shaking/proxy/ProxiesTest.java +++ b/src/test/java/com/android/tools/r8/shaking/proxy/ProxiesTest.java
@@ -139,8 +139,8 @@ private void noInterfaceKept(CodeInspector inspector) { // Indirectly assert that method is inlined into x, y and z and that redundant field loads // remove invokes. - assertEquals(3, countInstructionInX(inspector, InstructionSubject::isInvokeInterface)); - assertEquals(3, countInstructionInY(inspector, InstructionSubject::isInvokeInterface)); + assertEquals(0, countInstructionInX(inspector, InstructionSubject::isInvokeInterface)); + assertEquals(0, countInstructionInY(inspector, InstructionSubject::isInvokeInterface)); assertEquals(0, countInstructionInZ(inspector, InstructionSubject::isInvokeVirtual)); } @@ -149,10 +149,7 @@ runTest( ImmutableList.of(), this::noInterfaceKept, - "TestClass 1\nTestClass 1\nTestClass 1\nProxy\nProxy\nProxy\n" - + "TestClass 2\nTestClass 2\nTestClass 2\nProxy\nProxy\nProxy\n" - + "TestClass 3\nTestClass 3\nTestClass 3\n" - + "TestClass 4\nTestClass 4\nTestClass 4\nSUCCESS\n"); + "TestClass 1\nTestClass 1\nTestClass 1\nEXCEPTION\n"); } private void baseInterfaceKept(CodeInspector inspector) { @@ -160,7 +157,7 @@ assertEquals(3, countInstructionInX(inspector, InstructionSubject::isInvokeInterface)); // Indirectly assert that method is inlined into y and z and that redundant field loads // remove invokes. - assertEquals(3, countInstructionInY(inspector, InstructionSubject::isInvokeInterface)); + assertEquals(0, countInstructionInY(inspector, InstructionSubject::isInvokeInterface)); assertEquals(0, countInstructionInZ(inspector, InstructionSubject::isInvokeVirtual)); assertEquals(0, countInstructionInZSubClass(inspector, InstructionSubject::isInvokeVirtual)); } @@ -174,9 +171,7 @@ "}"), this::baseInterfaceKept, "TestClass 1\nTestClass 1\nTestClass 1\nProxy\nProxy\nProxy\n" - + "TestClass 2\nTestClass 2\nTestClass 2\nProxy\nProxy\nProxy\n" - + "TestClass 3\nTestClass 3\nTestClass 3\n" - + "TestClass 4\nTestClass 4\nTestClass 4\nSUCCESS\n"); + + "TestClass 2\nTestClass 2\nTestClass 2\nEXCEPTION\n"); } private void subInterfaceKept(CodeInspector inspector) {
diff --git a/src/test/java/com/android/tools/r8/synthesis/SyntheticMarkerCfTest.java b/src/test/java/com/android/tools/r8/synthesis/SyntheticMarkerCfTest.java new file mode 100644 index 0000000..706ace9 --- /dev/null +++ b/src/test/java/com/android/tools/r8/synthesis/SyntheticMarkerCfTest.java
@@ -0,0 +1,261 @@ +// Copyright (c) 2022, 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.synthesis; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; + +import com.android.tools.r8.ByteDataView; +import com.android.tools.r8.ClassFileConsumer; +import com.android.tools.r8.CompilationFailedException; +import com.android.tools.r8.D8TestBuilder; +import com.android.tools.r8.DiagnosticsHandler; +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.transformers.ClassTransformer; +import com.android.tools.r8.utils.AndroidApiLevel; +import com.android.tools.r8.utils.Box; +import com.android.tools.r8.utils.InternalOptions; +import com.android.tools.r8.utils.StringUtils; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.objectweb.asm.Attribute; +import org.objectweb.asm.ByteVector; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.Label; + +@RunWith(Parameterized.class) +public class SyntheticMarkerCfTest extends TestBase { + + static final String EXPECTED = StringUtils.lines("Hello, world"); + + private final TestParameters parameters; + + @Parameterized.Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters() + .withDefaultCfRuntime() + .withApiLevel(AndroidApiLevel.B) + .enableApiLevelsForCf() + .build(); + } + + public SyntheticMarkerCfTest(TestParameters parameters) { + this.parameters = parameters; + } + + /** + * Mirror of the initial D8 synthetic marker format. + * + * <p>The legacy marker just had the synthetic kind id as payload. + */ + private static class SyntheticMarkerV1 extends Attribute { + static final SyntheticMarkerV1 PROTO = new SyntheticMarkerV1((short) 0); + + final short kindId; + + public SyntheticMarkerV1(short kindId) { + super("com.android.tools.r8.SynthesizedClass"); + this.kindId = kindId; + } + + @Override + protected Attribute read( + ClassReader classReader, + int offset, + int length, + char[] charBuffer, + int codeAttributeOffset, + Label[] labels) { + short id = classReader.readShort(offset); + return new SyntheticMarkerV1(id); + } + + @Override + protected ByteVector write( + ClassWriter classWriter, byte[] code, int codeLength, int maxStack, int maxLocals) { + ByteVector byteVector = new ByteVector(); + byteVector.putShort(kindId); + return byteVector; + } + } + + /** + * Format of the current synthetic marker. + * + * <p>The marker is distinguished by a new attribute type name. + * + * <p>The payload is the kind id, version hash length and then version hash bytes. + */ + private static class SyntheticMarkerV2 extends Attribute { + static final SyntheticMarkerV2 PROTO = new SyntheticMarkerV2((short) 0, null); + + final short kindId; + final byte[] versionHash; + + public SyntheticMarkerV2(short kindId, byte[] versionHash) { + super("com.android.tools.r8.SynthesizedClassV2"); + this.versionHash = versionHash; + this.kindId = kindId; + } + + @Override + protected Attribute read( + ClassReader classReader, + int offset, + int length, + char[] charBuffer, + int codeAttributeOffset, + Label[] labels) { + short kindId = classReader.readShort(offset); + offset += 2; + short versionLength = classReader.readShort(offset); + offset += 2; + byte[] versionBytes = new byte[versionLength]; + for (int i = 0; i < versionLength; i++) { + versionBytes[i] = (byte) classReader.readByte(offset++); + } + return new SyntheticMarkerV2(kindId, versionBytes); + } + + @Override + protected ByteVector write( + ClassWriter classWriter, byte[] code, int codeLength, int maxStack, int maxLocals) { + ByteVector byteVector = new ByteVector(); + byteVector.putShort(kindId); + byteVector.putShort(versionHash.length); + byteVector.putByteArray(versionHash, 0, versionHash.length); + return byteVector; + } + } + + private static List<Attribute> readAttributes(byte[] bytes) { + List<Attribute> attributes = new ArrayList<>(); + ClassReader reader = new ClassReader(bytes); + reader.accept( + new ClassVisitor(InternalOptions.ASM_VERSION) { + @Override + public void visit( + int version, + int access, + String name, + String signature, + String superName, + String[] interfaces) { + super.visit(version, access, name, signature, superName, interfaces); + } + + @Override + public void visitAttribute(Attribute attribute) { + attributes.add(attribute); + } + }, + new Attribute[] {SyntheticMarkerV1.PROTO, SyntheticMarkerV2.PROTO}, + ClassReader.SKIP_CODE | ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG); + return attributes; + } + + private byte[] getTestClassWithMarker(Attribute marker) throws IOException { + return transformer(TestClass.class) + .addClassTransformer( + new ClassTransformer() { + @Override + public void visit( + int version, + int access, + String name, + String signature, + String superName, + String[] interfaces) { + super.visit(version, access, name, signature, superName, interfaces); + super.visitAttribute(marker); + } + }) + .transform(); + } + + /** Test that reads the correct marker from a compilation unit and fails if then manipulated. */ + @Test + public void testInvalidMarkerFailsCompilation() throws Exception { + Box<SyntheticMarkerV2> currentCompilerMarker = new Box<>(); + testForD8(parameters.getBackend()) + .addProgramClasses(TestClass.class) + .setMinApi(parameters.getApiLevel()) + .setIntermediate(true) + .setProgramConsumer( + new ClassFileConsumer() { + private final List<Attribute> attributes = new ArrayList<>(); + + @Override + public void accept(ByteDataView data, String descriptor, DiagnosticsHandler handler) { + attributes.addAll(readAttributes(data.copyByteData())); + } + + @Override + public void finished(DiagnosticsHandler handler) { + assertEquals(1, attributes.size()); + assertEquals(SyntheticMarkerV2.PROTO.type, attributes.get(0).type); + currentCompilerMarker.set((SyntheticMarkerV2) attributes.get(0)); + } + }) + .compile() + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutput(EXPECTED); + + // Test that if a "current" marker with invalid content is given to the compiler it will + // cause a failure. This test ensures that we can witness the markers being ignored in the + // tests below. + D8TestBuilder builder = + testForD8(parameters.getBackend()) + .setMinApi(parameters.getApiLevel()) + .addProgramClassFileData( + getTestClassWithMarker( + new SyntheticMarkerV2( + Short.MAX_VALUE, currentCompilerMarker.get().versionHash))); + assertThrows(CompilationFailedException.class, builder::compile); + } + + @Test + public void testIgnoreV1Markers() throws Exception { + // Test that inputs with a legacy marker will be ignored. + // We do so by injecting an old marker and put in a non-valid ID which would cause the compiler + // to fail if it was read. + testForD8(parameters.getBackend()) + .setMinApi(parameters.getApiLevel()) + .addProgramClassFileData(getTestClassWithMarker(new SyntheticMarkerV1(Short.MAX_VALUE))) + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutput(EXPECTED); + } + + @Test + public void testIgnorePreviousV2Markers() throws Exception { + // Test that inputs with a V2 marker from a previous compiler version are ignored. + // We do so by injecting an old marker and put in a non-valid ID which would cause the compiler + // to fail if it was read. + testForD8(parameters.getBackend()) + .setMinApi(parameters.getApiLevel()) + .addProgramClassFileData( + getTestClassWithMarker(new SyntheticMarkerV2(Short.MAX_VALUE, new byte[0]))) + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutput(EXPECTED); + } + + public static class TestClass { + + public static void run(Runnable r) { + r.run(); + } + + public static void main(String[] args) { + run(() -> System.out.println("Hello, world")); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/synthesis/SyntheticMarkerDexTest.java b/src/test/java/com/android/tools/r8/synthesis/SyntheticMarkerDexTest.java new file mode 100644 index 0000000..5b00775 --- /dev/null +++ b/src/test/java/com/android/tools/r8/synthesis/SyntheticMarkerDexTest.java
@@ -0,0 +1,89 @@ +// Copyright (c) 2022, 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.synthesis; + +import static org.junit.Assert.assertEquals; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.graph.DexAnnotation; +import com.android.tools.r8.graph.DexEncodedAnnotation; +import com.android.tools.r8.references.Reference; +import com.android.tools.r8.utils.AndroidApiLevel; +import com.android.tools.r8.utils.StringUtils; +import com.android.tools.r8.utils.codeinspector.CodeInspector; +import java.nio.file.Path; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class SyntheticMarkerDexTest extends TestBase { + + static final String EXPECTED = StringUtils.lines("Hello, world"); + + private final TestParameters parameters; + + @Parameterized.Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withDefaultDexRuntime().withApiLevel(AndroidApiLevel.B).build(); + } + + public SyntheticMarkerDexTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void test() throws Exception { + Path out = + testForD8(parameters.getBackend()) + .setMinApi(parameters.getApiLevel()) + .setIntermediate(true) + .addProgramClasses(TestClass.class) + .compile() + .inspect(this::checkSyntheticClassIsMarked) + .writeToZip(); + testForD8(parameters.getBackend()) + .setMinApi(parameters.getApiLevel()) + .addProgramFiles(out) + // Use intermediate again to preserve synthetics. + .setIntermediate(true) + // Disable desugaring so we are sure the lambda synthetic is created in the first round. + .disableDesugaring() + .compile() + .inspect(this::checkSyntheticClassIsMarked) + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutput(EXPECTED); + } + + private void checkSyntheticClassIsMarked(CodeInspector inspector) { + // Compilation gives rise to the main class plus one lambda. + assertEquals(2, inspector.allClasses().size()); + inspector.forAllClasses( + clazz -> { + if (!clazz.getFinalReference().equals(Reference.classFromClass(TestClass.class))) { + // This should be the lambda class. + DexAnnotation[] annotations = clazz.getDexProgramClass().annotations().annotations; + assertEquals(1, annotations.length); + DexEncodedAnnotation annotation = annotations[0].annotation; + assertEquals(2, annotation.elements.length); + assertEquals( + "com.android.tools.r8.annotations.SynthesizedClassV2", + annotation.type.toSourceString()); + } + }); + } + + public static class TestClass { + + public static void run(Runnable r) { + r.run(); + } + + public static void main(String[] args) { + run(() -> System.out.println("Hello, world")); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/utils/TestParametersTest.java b/src/test/java/com/android/tools/r8/utils/TestParametersTest.java index 7d26b86..a217b8c 100644 --- a/src/test/java/com/android/tools/r8/utils/TestParametersTest.java +++ b/src/test/java/com/android/tools/r8/utils/TestParametersTest.java
@@ -31,7 +31,7 @@ assumeFalse( "Test is only valid when no runtimes property is set", TestParametersBuilder.isRuntimesPropertySet()); - TestParametersCollection params = TestParametersBuilder.builder().withNoneRuntime().build(); + TestParametersCollection params = TestParameters.builder().withNoneRuntime().build(); assertTrue(params.stream().anyMatch(TestParameters::isNoneRuntime)); } @@ -40,7 +40,7 @@ assumeFalse( "Test is only valid when no runtimes property is set", TestParametersBuilder.isRuntimesPropertySet()); - TestParametersCollection params = TestParametersBuilder.builder().withAllRuntimes().build(); + TestParametersCollection params = TestParameters.builder().withAllRuntimes().build(); assertTrue(params.stream().noneMatch(TestParameters::isNoneRuntime)); assertTrue(params.stream().anyMatch(TestParameters::isDexRuntime)); assertTrue(params.stream().anyMatch(TestParameters::isCfRuntime)); @@ -53,7 +53,7 @@ TestParametersBuilder.isRuntimesPropertySet()); // This test may also fail once the tests can be configured for with API levels to run. TestParametersCollection params = - TestParametersBuilder.builder().withAllRuntimesAndApiLevels().build(); + TestParameters.builder().withAllRuntimesAndApiLevels().build(); assertTrue(params.stream().noneMatch(TestParameters::isNoneRuntime)); assertTrue(params.stream().anyMatch(p -> p.isCfRuntime() && p.getApiLevel() == null)); // Default API levels are min and max for each DEX VM. @@ -79,12 +79,9 @@ public void testJdk9Presence() { assumeTrue(!TestParametersBuilder.isRuntimesPropertySet() || TestParametersBuilder.getRuntimesProperty().contains("jdk9")); - assertTrue(TestParametersBuilder - .builder() - .withAllRuntimesAndApiLevels() - .build() - .stream() - .anyMatch(parameter -> parameter.getRuntime().equals(TestRuntime.getCheckedInJdk9()))); + assertTrue( + TestParameters.builder().withAllRuntimesAndApiLevels().build().stream() + .anyMatch(parameter -> parameter.getRuntime().equals(TestRuntime.getCheckedInJdk9()))); } @Test @@ -92,12 +89,9 @@ assumeTrue(ToolHelper.isLinux()); assumeTrue(!TestParametersBuilder.isRuntimesPropertySet() || TestParametersBuilder.getRuntimesProperty().contains("dex-default")); - assertTrue(TestParametersBuilder - .builder() - .withAllRuntimesAndApiLevels() - .build() - .stream() - .anyMatch(parameter -> parameter.getRuntime().name().equals("dex-default"))); + assertTrue( + TestParameters.builder().withAllRuntimesAndApiLevels().build().stream() + .anyMatch(parameter -> parameter.getRuntime().name().equals("dex-default"))); } @Test @@ -105,11 +99,8 @@ assumeTrue(ToolHelper.isLinux()); assumeTrue(!TestParametersBuilder.isRuntimesPropertySet() || TestParametersBuilder.getRuntimesProperty().contains("dex-4.4.4")); - assertTrue(TestParametersBuilder - .builder() - .withAllRuntimesAndApiLevels() - .build() - .stream() - .anyMatch(parameter -> parameter.getRuntime().name().equals("dex-4.4.4"))); + assertTrue( + TestParameters.builder().withAllRuntimesAndApiLevels().build().stream() + .anyMatch(parameter -> parameter.getRuntime().name().equals("dex-4.4.4"))); } }
diff --git a/third_party/r8-releases/3.2.54.tar.gz.sha1 b/third_party/r8-releases/3.2.54.tar.gz.sha1 new file mode 100644 index 0000000..dac3b33 --- /dev/null +++ b/third_party/r8-releases/3.2.54.tar.gz.sha1
@@ -0,0 +1 @@ +f1019609a854b348981e65b34b401c15d93eb91f \ No newline at end of file
diff --git a/tools/internal_test.py b/tools/internal_test.py index df4174f..8e4caa5 100755 --- a/tools/internal_test.py +++ b/tools/internal_test.py
@@ -111,6 +111,8 @@ # TODO(b/210982978): Enable testing of min xmx again TEST_COMMANDS = [ + # Make sure we have a clean build to not be polluted by old test files + ['tools/gradle.py', 'clean'], # Run test.py internal testing. ['tools/test.py', '--only_internal', '--slow_tests', '--java_max_memory_size=8G'],
diff --git a/tools/jdk.py b/tools/jdk.py index 23cacc8..ea6be86 100755 --- a/tools/jdk.py +++ b/tools/jdk.py
@@ -3,14 +3,18 @@ # 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. -import defines import os import sys +import defines + JDK_DIR = os.path.join(defines.THIRD_PARTY, 'openjdk') def GetJdkHome(): - root = os.path.join(JDK_DIR, 'openjdk-9.0.4') + return GetJdk9Home() + +def GetJdk11Home(): + root = os.path.join(JDK_DIR, 'jdk-11') if defines.IsLinux(): return os.path.join(root, 'linux') elif defines.IsOsX(): @@ -20,8 +24,8 @@ else: return os.environ['JAVA_HOME'] -def GetJdk11Home(): - root = os.path.join(JDK_DIR, 'jdk-11') +def GetJdk9Home(): + root = os.path.join(JDK_DIR, 'openjdk-9.0.4') if defines.IsLinux(): return os.path.join(root, 'linux') elif defines.IsOsX():
diff --git a/tools/trigger.py b/tools/trigger.py index 2b9816a..d467a81 100755 --- a/tools/trigger.py +++ b/tools/trigger.py
@@ -13,7 +13,7 @@ import subprocess import sys import urllib - +from urllib.request import urlopen import utils LUCI_SCHEDULE = os.path.join(utils.REPO_ROOT, 'infra', 'config', 'global', @@ -47,7 +47,7 @@ with open(LUCI_SCHEDULE, 'r') as fp: lines = fp.readlines() for line in lines: - if 'branch-gitiles-trigger' in line: + if 'branch-gitiles' in line: is_release = True if 'main-gitiles-trigger' in line: is_release = False @@ -66,7 +66,7 @@ return (main_builders, release_builders) def sanity_check_url(url): - a = urllib.urlopen(url) + a = urlopen(url) if a.getcode() != 200: raise Exception('Url: %s \n returned %s' % (url, a.getcode())) @@ -85,15 +85,15 @@ def Main(): (options, args) = ParseOptions() if len(args) != 1 and not options.cl and not options.desugar: - print 'Takes exactly one argument, the commit to run' + print('Takes exactly one argument, the commit to run') return 1 if options.cl and options.release: - print 'You can\'t run cls on the release bots' + print('You can\'t run cls on the release bots') return 1 if options.cl and options.desugar: - print 'You can\'t run cls on the desugar bot' + print('You can\'t run cls on the desugar bot') return 1 commit = None if (options.cl or options.desugar) else args[0]