Merge commit '9cd6f525f542275e9bb588855e16364fbfff1c26' into dev-release
diff --git a/Regress78493232.jar b/Regress78493232.jar
new file mode 100644
index 0000000..6dee6f0
--- /dev/null
+++ b/Regress78493232.jar
Binary files differ
diff --git a/build.gradle b/build.gradle
index 5b9c8df..a138d84 100644
--- a/build.gradle
+++ b/build.gradle
@@ -2,9 +2,10 @@
// 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 desugaredlibrary.CustomConversionAsmRewriterTask
import dx.DexMergerTask
import dx.DxTask
-import desugaredlibrary.CustomConversionAsmRewriterTask
import net.ltgt.gradle.errorprone.CheckSeverity
import org.gradle.internal.os.OperatingSystem
import smali.SmaliTask
@@ -79,6 +80,14 @@
srcDirs "third_party/api_database/api_database"
}
}
+ main17 {
+ java {
+ srcDirs = ['src/main/java']
+ }
+ resources {
+ srcDirs "third_party/api_database/api_database"
+ }
+ }
test {
java {
srcDirs = [
@@ -252,6 +261,25 @@
main11Implementation group: 'org.ow2.asm', name: 'asm-analysis', version: asmVersion
main11Implementation group: 'org.ow2.asm', name: 'asm-util', version: asmVersion
+ main17Implementation "net.sf.jopt-simple:jopt-simple:$joptSimpleVersion"
+ main17Implementation "com.google.code.gson:gson:$gsonVersion"
+ // Include all of guava when compiling the code, but exclude annotations that we don't
+ // need from the packaging.
+ main17CompileOnly("com.google.guava:guava:$guavaVersion")
+ main17Implementation("com.google.guava:guava:$guavaVersion", {
+ exclude group: 'com.google.errorprone'
+ exclude group: 'com.google.code.findbugs'
+ exclude group: 'com.google.j2objc'
+ exclude group: 'org.codehaus.mojo'
+ })
+ main17Implementation group: 'it.unimi.dsi', name: 'fastutil', version: fastutilVersion
+ main17Implementation "org.jetbrains.kotlinx:kotlinx-metadata-jvm:$kotlinExtMetadataJVMVersion"
+ main17Implementation group: 'org.ow2.asm', name: 'asm', version: asmVersion
+ main17Implementation group: 'org.ow2.asm', name: 'asm-commons', version: asmVersion
+ main17Implementation group: 'org.ow2.asm', name: 'asm-tree', version: asmVersion
+ main17Implementation group: 'org.ow2.asm', name: 'asm-analysis', version: asmVersion
+ main17Implementation group: 'org.ow2.asm', name: 'asm-util', version: asmVersion
+
examplesTestNGRunnerCompile group: 'org.testng', name: 'testng', version: testngVersion
testCompile sourceSets.examples.output
@@ -630,6 +658,11 @@
JavaVersion.VERSION_11,
false)
setJdkCompilationWithCompatibility(
+ sourceSets.main17.compileJavaTaskName,
+ 'jdk-17',
+ JavaVersion.VERSION_17,
+ false)
+setJdkCompilationWithCompatibility(
sourceSets.examplesJava11.compileJavaTaskName,
'jdk-11',
JavaVersion.VERSION_11,
@@ -782,6 +815,11 @@
archiveFileName = 'sources_main_11.jar'
}
+task repackageSources17(type: Jar) {
+ from sourceSets.main17.output
+ archiveFileName = 'sources_main_17.jar'
+}
+
def r8CreateTask(name, baseName, sources, includeLibraryLicenses, includeSwissArmyKnife) {
return tasks.create("r8Create${name}", Jar) {
entryCompression ZipEntryCompression.STORED
@@ -872,6 +910,20 @@
outputs.files r8Task.outputs.files
}
+task r8WithDeps17 {
+ dependsOn repackageSources17
+ dependsOn repackageDeps
+ inputs.files ([repackageSources17.outputs, repackageDeps.outputs])
+ def r8Task = r8CreateTask(
+ 'WithDeps17',
+ 'r8_with_deps_17.jar',
+ repackageSources17.outputs.files + repackageDeps.outputs.files,
+ true,
+ true)
+ dependsOn r8Task
+ outputs.files r8Task.outputs.files
+}
+
task r8WithRelocatedDeps {
def output = "${buildDir}/libs/r8_with_relocated_deps.jar"
dependsOn r8RelocateTask(r8WithDeps, output)
@@ -886,6 +938,13 @@
outputs.file output
}
+task r8WithRelocatedDeps17 {
+ def output = "${buildDir}/libs/r8_with_relocated_deps_17.jar"
+ dependsOn r8RelocateTask(r8WithDeps17, output)
+ inputs.files r8WithDeps17.outputs.files
+ outputs.file output
+}
+
task r8WithoutDeps {
dependsOn repackageSources
inputs.files repackageSources.outputs
diff --git a/src/main/java/com/android/tools/r8/DexFileMergerHelper.java b/src/main/java/com/android/tools/r8/DexFileMergerHelper.java
index ad3bf13..a94d834 100644
--- a/src/main/java/com/android/tools/r8/DexFileMergerHelper.java
+++ b/src/main/java/com/android/tools/r8/DexFileMergerHelper.java
@@ -4,27 +4,11 @@
package com.android.tools.r8;
-import static com.android.tools.r8.utils.ExceptionUtils.unwrapExecutionException;
-import com.android.tools.r8.dex.ApplicationReader;
-import com.android.tools.r8.dex.ApplicationWriter;
-import com.android.tools.r8.dex.Marker;
-import com.android.tools.r8.graph.AppInfo;
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.synthesis.SyntheticItems.GlobalSyntheticsStrategy;
-import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.ExceptionUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.InternalOptions.DesugarState;
-import com.android.tools.r8.utils.ThreadUtils;
-import com.android.tools.r8.utils.Timing;
-import java.io.IOException;
-import java.util.List;
import java.util.Map;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
public class DexFileMergerHelper {
@@ -57,63 +41,26 @@
return aIndex <= bIndex ? a.get() : b.get();
}
+ // NOTE: Don't change this signature! Reflectively accessed from bazel DexFileMerger.
public static void run(
D8Command command, Boolean minimalMainDex, Map<String, Integer> inputOrdering)
throws CompilationFailedException {
InternalOptions options = command.getInternalOptions();
- ExceptionUtils.withD8CompilationHandler(
- options.reporter,
- () -> runInternal(command.getInputApp(), options, minimalMainDex, inputOrdering));
- }
- private static void runInternal(
- AndroidApp inputApp,
- InternalOptions options,
- Boolean minimalMainDex,
- Map<String, Integer> inputOrdering)
- throws IOException {
+ // TODO(b/241063980): Move this to D8Command.Builder.setDisableDesugaring(true) in bazel.
options.desugarState = DesugarState.OFF;
+
+ // TODO(b/241063980): Is this configuration needed?
options.enableMainDexListCheck = false;
+
+ // TODO(b/241063980): Is this configuration needed?
options.minimalMainDex = minimalMainDex;
- assert !options.isMinifying();
- options.inlinerOptions().enableInlining = false;
- options.outline.enabled = false;
- ExecutorService executor = ThreadUtils.getExecutorService(ThreadUtils.NOT_SPECIFIED);
- try {
- try {
- Timing timing = new Timing("DexFileMerger");
- ApplicationReader applicationReader = new ApplicationReader(inputApp, options, timing);
- DexApplication app =
- applicationReader.read(
- null,
- executor,
- new DexFileMergerHelper(inputOrdering)::keepFirstProgramClassConflictResolver);
+ // TODO(b/241063980): Add API to configure this in D8Command.Builder.
+ options.programClassConflictResolver =
+ new DexFileMergerHelper(inputOrdering)::keepFirstProgramClassConflictResolver;
- AppView<AppInfo> appView =
- AppView.createForD8(
- AppInfo.createInitialAppInfo(
- app,
- GlobalSyntheticsStrategy.forNonSynthesizing(),
- applicationReader.readMainDexClasses(app)));
-
- D8.optimize(appView, options, timing, executor);
-
- List<Marker> markers = appView.dexItemFactory().extractMarkers();
-
- assert !options.hasMethodsFilter();
- ApplicationWriter writer = new ApplicationWriter(appView, markers);
- writer.write(executor);
- options.printWarnings();
- } catch (ExecutionException e) {
- throw unwrapExecutionException(e);
- } finally {
- inputApp.signalFinishedToProviders(options.reporter);
- options.signalFinishedToConsumers();
- }
- } finally {
- executor.shutdown();
- }
+ D8.runForTesting(command.getInputApp(), options);
}
public static void runD8ForTesting(D8Command command, boolean dontCreateMarkerInD8)
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfAssignability.java b/src/main/java/com/android/tools/r8/cf/code/CfAssignability.java
index 6858fe9..d3f035b 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfAssignability.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfAssignability.java
@@ -145,17 +145,10 @@
public AssignabilityResult isLocalsAssignable(
Int2ObjectSortedMap<FrameType> sourceLocals, Int2ObjectSortedMap<FrameType> targetLocals) {
- // TODO(b/229826687): The tail of locals could have top(s) at destination but still be valid.
int localsLastKey = sourceLocals.isEmpty() ? -1 : sourceLocals.lastIntKey();
int otherLocalsLastKey = targetLocals.isEmpty() ? -1 : targetLocals.lastIntKey();
- if (localsLastKey < otherLocalsLastKey) {
- return new FailedAssignabilityResult(
- "Source locals "
- + MapUtils.toString(sourceLocals)
- + " have different local indices than "
- + MapUtils.toString(targetLocals));
- }
- for (int i = 0; i < otherLocalsLastKey; i++) {
+ int maxKey = Math.max(localsLastKey, otherLocalsLastKey);
+ for (int i = 0; i <= maxKey; i++) {
FrameType sourceType =
sourceLocals.containsKey(i) ? sourceLocals.get(i) : FrameType.oneWord();
FrameType destinationType =
@@ -164,23 +157,33 @@
destinationType = FrameType.twoWord();
}
if (!isFrameTypeAssignable(sourceType, destinationType)) {
- return new FailedAssignabilityResult(
- "Could not assign '"
- + MapUtils.toString(sourceLocals)
- + "' to '"
- + MapUtils.toString(targetLocals)
- + "'. The local at index "
- + i
- + " with '"
- + sourceType
- + "' not being assignable to '"
- + destinationType
- + "'");
+ return reportFailedAssignabilityResult(
+ sourceLocals, targetLocals, sourceType, destinationType, i);
}
}
return new SuccessfulAssignabilityResult();
}
+ private FailedAssignabilityResult reportFailedAssignabilityResult(
+ Int2ObjectSortedMap<FrameType> sourceLocals,
+ Int2ObjectSortedMap<FrameType> targetLocals,
+ FrameType sourceType,
+ FrameType destinationType,
+ int index) {
+ return new FailedAssignabilityResult(
+ "Could not assign '"
+ + MapUtils.toString(sourceLocals)
+ + "' to '"
+ + MapUtils.toString(targetLocals)
+ + "'. The local at index "
+ + index
+ + " with '"
+ + sourceType
+ + "' not being assignable to '"
+ + destinationType
+ + "'");
+ }
+
public AssignabilityResult isStackAssignable(
Deque<PreciseFrameType> sourceStack, Deque<PreciseFrameType> targetStack) {
if (sourceStack.size() != targetStack.size()) {
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfFrameVerifier.java b/src/main/java/com/android/tools/r8/cf/code/CfFrameVerifier.java
index bdd6512..09cabd8 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfFrameVerifier.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfFrameVerifier.java
@@ -107,15 +107,17 @@
for (int i = 0; i < code.getInstructions().size(); i++) {
CfInstruction instruction = code.getInstruction(i);
assert !state.isError();
- // Check the exceptional edge prior to evaluating the instruction. The local state is stable
- // at this point as store operations are not throwing and the current stack does not
- // affect the exceptional transfer (the exception edge is always a singleton stack).
- if (instruction.canThrow()) {
- assert !instruction.isStore();
- state = checkExceptionEdges(state, labelToFrameMap);
- }
if (instruction.isLabel()) {
updateActiveCatchHandlers(instruction.asLabel());
+ } else {
+ // The ExceptionStackFrame is defined as the current frame having an empty operand stack.
+ // All instructions, not only throwing instructions, check the exception frame to be
+ // assignable to all exception edges.
+ // https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.10.1.9
+ if (appView.options().enableCheckAllInstructionsDuringStackMapVerification
+ || instruction.canThrow()) {
+ state = checkExceptionEdges(state, labelToFrameMap);
+ }
}
eventConsumer.acceptInstructionState(instruction, state);
state = instruction.evaluate(state, appView, config);
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java b/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
index 285ebe5..249de08 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
@@ -34,6 +34,7 @@
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.optimize.interfaces.analysis.CfAnalysisConfig;
import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState;
+import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.structural.CompareToVisitor;
import com.android.tools.r8.utils.structural.HashingVisitor;
import com.android.tools.r8.utils.structural.StructuralSpecification;
@@ -111,12 +112,27 @@
LensCodeRewriterUtils rewriter,
MethodVisitor visitor) {
Invoke.Type invokeType = Invoke.Type.fromCfOpcode(opcode, method, context, appView);
- MethodLookupResult lookup = graphLens.lookupMethod(method, context.getReference(), invokeType);
- DexMethod rewrittenMethod = lookup.getReference();
- String owner = namingLens.lookupInternalName(rewrittenMethod.holder);
- String name = namingLens.lookupName(rewrittenMethod).toString();
- String desc = rewrittenMethod.proto.toDescriptorString(namingLens);
- visitor.visitMethodInsn(lookup.getType().getCfOpcode(), owner, name, desc, itf);
+ if (invokeType == Type.POLYMORPHIC) {
+ assert dexItemFactory.polymorphicMethods.isPolymorphicInvoke(method);
+ // The method is one of java.lang.MethodHandle.invoke/invokeExact.
+ // Only the method signature (getProto()) is to be type rewritten.
+ DexProto rewrittenProto = rewriter.rewriteProto(method.getProto());
+ visitor.visitMethodInsn(
+ invokeType.getCfOpcode(),
+ DescriptorUtils.descriptorToInternalName(method.holder.toDescriptorString()),
+ method.getName().toString(),
+ rewrittenProto.toDescriptorString(namingLens),
+ itf);
+ } else {
+ MethodLookupResult lookup =
+ graphLens.lookupMethod(method, context.getReference(), invokeType);
+ Invoke.Type rewrittenType = lookup.getType();
+ DexMethod rewrittenMethod = lookup.getReference();
+ String owner = namingLens.lookupInternalName(rewrittenMethod.holder);
+ String name = namingLens.lookupName(rewrittenMethod).toString();
+ String desc = rewrittenMethod.proto.toDescriptorString(namingLens);
+ visitor.visitMethodInsn(rewrittenType.getCfOpcode(), owner, name, desc, itf);
+ }
}
@Override
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
index e351e57..31967b1 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
@@ -46,7 +46,6 @@
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.LibraryClassCollection;
import com.android.tools.r8.utils.MainDexListParser;
-import com.android.tools.r8.utils.ProgramClassCollection;
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
@@ -98,46 +97,25 @@
}
}
- public final LazyLoadedDexApplication read(
- ExecutorService executorService)
- throws IOException {
- return read(
- inputApp.getProguardMapInputData(),
- executorService,
- ProgramClassCollection.defaultConflictResolver(options.reporter));
+ public final LazyLoadedDexApplication read(ExecutorService executorService) throws IOException {
+ return read(inputApp.getProguardMapInputData(), executorService);
}
public final LazyLoadedDexApplication readWithoutDumping(ExecutorService executorService)
throws IOException {
- return read(
- inputApp.getProguardMapInputData(),
- executorService,
- ProgramClassCollection.defaultConflictResolver(options.reporter),
- DumpInputFlags.noDump());
+ return read(inputApp.getProguardMapInputData(), executorService, DumpInputFlags.noDump());
}
public final LazyLoadedDexApplication read(
StringResource proguardMap,
ExecutorService executorService)
throws IOException {
- return read(
- proguardMap,
- executorService,
- ProgramClassCollection.defaultConflictResolver(options.reporter));
+ return read(proguardMap, executorService, options.getDumpInputFlags());
}
public final LazyLoadedDexApplication read(
StringResource proguardMap,
ExecutorService executorService,
- ProgramClassConflictResolver resolver)
- throws IOException {
- return read(proguardMap, executorService, resolver, options.getDumpInputFlags());
- }
-
- public final LazyLoadedDexApplication read(
- StringResource proguardMap,
- ExecutorService executorService,
- ProgramClassConflictResolver resolver,
DumpInputFlags dumpInputFlags)
throws IOException {
assert verifyMainDexOptionsCompatible(inputApp, options);
@@ -148,8 +126,7 @@
}
timing.begin("DexApplication.read");
- final LazyLoadedDexApplication.Builder builder =
- DexApplication.builder(options, timing, resolver);
+ final LazyLoadedDexApplication.Builder builder = DexApplication.builder(options, timing);
try {
List<Future<?>> futures = new ArrayList<>();
// Still preload some of the classes, primarily for two reasons:
diff --git a/src/main/java/com/android/tools/r8/dex/StartupMixedSectionLayoutStrategy.java b/src/main/java/com/android/tools/r8/dex/StartupMixedSectionLayoutStrategy.java
index c71d0d3..b297a2b 100644
--- a/src/main/java/com/android/tools/r8/dex/StartupMixedSectionLayoutStrategy.java
+++ b/src/main/java/com/android/tools/r8/dex/StartupMixedSectionLayoutStrategy.java
@@ -6,6 +6,8 @@
import com.android.tools.r8.dex.FileWriter.MixedSectionOffsets;
import com.android.tools.r8.experimental.startup.StartupClass;
+import com.android.tools.r8.experimental.startup.StartupItem;
+import com.android.tools.r8.experimental.startup.StartupMethod;
import com.android.tools.r8.experimental.startup.StartupOrder;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexAnnotation;
@@ -13,7 +15,6 @@
import com.android.tools.r8.graph.DexAnnotationSet;
import com.android.tools.r8.graph.DexCallSite;
import com.android.tools.r8.graph.DexEncodedArray;
-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.DexMethodHandle;
@@ -22,6 +23,7 @@
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexTypeList;
+import com.android.tools.r8.graph.DexWritableCode;
import com.android.tools.r8.graph.ParameterAnnotationsList;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
@@ -71,6 +73,7 @@
collectStartupItems(virtualFile);
}
+ /** This adds all startup items to the startup layouts (i.e., the fields of this class). */
private void collectStartupItems(VirtualFile virtualFile) {
Map<DexType, DexProgramClass> virtualFileDefinitions =
MapUtils.newIdentityHashMap(
@@ -79,11 +82,55 @@
virtualFile.classes().size());
LensCodeRewriterUtils rewriter = new LensCodeRewriterUtils(appView, true);
StartupIndexedItemCollection indexedItemCollection = new StartupIndexedItemCollection();
- for (StartupClass<DexType, DexMethod> startupClass : startupOrderForWriting.getClasses()) {
- assert !startupClass.isSynthetic();
- DexProgramClass definition = virtualFileDefinitions.get(startupClass.getReference());
- if (definition != null) {
- definition.collectIndexedItems(appView, indexedItemCollection, rewriter);
+ for (StartupItem<DexType, DexMethod, ?> startupItem : startupOrderForWriting.getItems()) {
+ // All synthetic startup items should be removed after calling
+ // StartupOrder#toStartupOrderForWriting.
+ assert !startupItem.isSynthetic();
+ startupItem.accept(
+ startupClass ->
+ collectStartupItems(startupClass, indexedItemCollection, virtualFileDefinitions),
+ startupMethod ->
+ collectStartupItems(
+ startupMethod, indexedItemCollection, virtualFileDefinitions, rewriter));
+ }
+ }
+
+ private void collectStartupItems(
+ StartupClass<DexType, DexMethod> startupClass,
+ StartupIndexedItemCollection indexedItemCollection,
+ Map<DexType, DexProgramClass> virtualFileDefinitions) {
+ DexProgramClass definition = virtualFileDefinitions.get(startupClass.getReference());
+ if (definition != null) {
+ // Note that this must not call definition.collectIndexedItems, since that would collect all
+ // items from the class, and not only the startup items.
+ indexedItemCollection.addClass(definition);
+
+ // Collect the descriptor of the current type.
+ definition.getType().collectIndexedItems(appView, indexedItemCollection);
+
+ // Collect the descriptors (strings) of the supertypes.
+ definition.forEachImmediateSupertype(
+ supertype -> supertype.collectIndexedItems(appView, indexedItemCollection));
+
+ // TODO(b/238173796): Consider collecting the source file, the annotations, the enclosing
+ // method attribute, the inner class attribute, and the fields (i.e., annotations and static
+ // values).
+ }
+ }
+
+ private void collectStartupItems(
+ StartupMethod<DexType, DexMethod> startupMethod,
+ StartupIndexedItemCollection indexedItemCollection,
+ Map<DexType, DexProgramClass> virtualFileDefinitions,
+ LensCodeRewriterUtils rewriter) {
+ DexMethod methodReference = startupMethod.getReference();
+ DexProgramClass holder = virtualFileDefinitions.get(methodReference.getHolderType());
+ ProgramMethod method = methodReference.lookupOnProgramClass(holder);
+ if (method != null) {
+ methodReference.collectIndexedItems(appView, indexedItemCollection);
+ if (indexedItemCollection.addCode(method)) {
+ DexWritableCode code = method.getDefinition().getCode().asDexWritableCode();
+ code.collectIndexedItems(appView, indexedItemCollection, method, rewriter);
}
}
}
@@ -163,7 +210,6 @@
classDataLayout.add(clazz);
}
addTypeList(clazz.getInterfaces());
- clazz.forEachProgramMethodMatching(DexEncodedMethod::hasCode, codeLayout::add);
DexAnnotationDirectory annotationDirectory =
mixedSectionOffsets.getAnnotationDirectoryForClass(clazz);
if (annotationDirectory != null) {
@@ -178,6 +224,14 @@
return true;
}
+ public boolean addCode(ProgramMethod method) {
+ if (method.getDefinition().hasCode()) {
+ codeLayout.add(method);
+ return true;
+ }
+ return false;
+ }
+
@Override
public boolean addField(DexField field) {
return true;
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexFormat45cc.java b/src/main/java/com/android/tools/r8/dex/code/DexFormat45cc.java
index e658265..61a0257 100644
--- a/src/main/java/com/android/tools/r8/dex/code/DexFormat45cc.java
+++ b/src/main/java/com/android/tools/r8/dex/code/DexFormat45cc.java
@@ -122,14 +122,17 @@
GraphLens graphLens,
ObjectToOffsetMapping mapping,
LensCodeRewriterUtils rewriter) {
- MethodLookupResult lookup =
- graphLens.lookupMethod(getMethod(), context.getReference(), Type.POLYMORPHIC);
- assert lookup.getType() == Type.POLYMORPHIC;
- writeFirst(A, G, dest);
- write16BitReference(lookup.getReference(), dest, mapping);
- write16BitValue(combineBytes(makeByte(F, E), makeByte(D, C)), dest);
-
+ // The method is one of java.lang.MethodHandle.invoke/invokeExact.
+ // Only the method signature (getProto()) is to be type rewritten.
+ assert rewriter.dexItemFactory().polymorphicMethods.isPolymorphicInvoke(getMethod());
+ assert getMethod()
+ == graphLens
+ .lookupMethod(getMethod(), context.getReference(), Type.POLYMORPHIC)
+ .getReference();
DexProto rewrittenProto = rewriter.rewriteProto(getProto());
+ writeFirst(A, G, dest);
+ write16BitReference(getMethod(), dest, mapping);
+ write16BitValue(combineBytes(makeByte(F, E), makeByte(D, C)), dest);
write16BitReference(rewrittenProto, dest, mapping);
}
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexFormat4rcc.java b/src/main/java/com/android/tools/r8/dex/code/DexFormat4rcc.java
index ace2725..b795316 100644
--- a/src/main/java/com/android/tools/r8/dex/code/DexFormat4rcc.java
+++ b/src/main/java/com/android/tools/r8/dex/code/DexFormat4rcc.java
@@ -59,14 +59,17 @@
GraphLens graphLens,
ObjectToOffsetMapping mapping,
LensCodeRewriterUtils rewriter) {
- MethodLookupResult lookup =
- graphLens.lookupMethod(getMethod(), context.getReference(), Type.POLYMORPHIC);
- assert lookup.getType() == Type.POLYMORPHIC;
- writeFirst(AA, dest);
- write16BitReference(lookup.getReference(), dest, mapping);
- write16BitValue(CCCC, dest);
-
+ // The method is one of java.lang.MethodHandle.invoke/invokeExact.
+ // Only the method signature (getProto()) is to be type rewritten.
+ assert rewriter.dexItemFactory().polymorphicMethods.isPolymorphicInvoke(getMethod());
+ assert getMethod()
+ == graphLens
+ .lookupMethod(getMethod(), context.getReference(), Type.POLYMORPHIC)
+ .getReference();
DexProto rewrittenProto = rewriter.rewriteProto(getProto());
+ writeFirst(AA, dest);
+ write16BitReference(getMethod(), dest, mapping);
+ write16BitValue(CCCC, dest);
write16BitReference(rewrittenProto, dest, mapping);
}
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/EmptyStartupOrder.java b/src/main/java/com/android/tools/r8/experimental/startup/EmptyStartupOrder.java
index 7bbaba0..d777790 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/EmptyStartupOrder.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/EmptyStartupOrder.java
@@ -23,7 +23,7 @@
}
@Override
- public Collection<StartupClass<DexType, DexMethod>> getClasses() {
+ public Collection<StartupItem<DexType, DexMethod, ?>> getItems() {
return Collections.emptyList();
}
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/NonEmptyStartupOrder.java b/src/main/java/com/android/tools/r8/experimental/startup/NonEmptyStartupOrder.java
index 4122aa0..4b749f3 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/NonEmptyStartupOrder.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/NonEmptyStartupOrder.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.PrunedItems;
@@ -26,20 +27,23 @@
public class NonEmptyStartupOrder extends StartupOrder {
- private final LinkedHashSet<StartupClass<DexType, DexMethod>> startupClasses;
+ private final LinkedHashSet<StartupItem<DexType, DexMethod, ?>> startupItems;
- // Redundant sets to allow efficient querying without boxing.
+ // Sets to allow efficient querying without boxing.
private final Set<DexType> nonSyntheticStartupClasses = Sets.newIdentityHashSet();
private final Set<DexType> syntheticStartupClasses = Sets.newIdentityHashSet();
- NonEmptyStartupOrder(LinkedHashSet<StartupClass<DexType, DexMethod>> startupClasses) {
- assert !startupClasses.isEmpty();
- this.startupClasses = startupClasses;
- for (StartupClass<DexType, DexMethod> startupClass : startupClasses) {
- if (startupClass.isSynthetic()) {
- syntheticStartupClasses.add(startupClass.getReference());
+ NonEmptyStartupOrder(LinkedHashSet<StartupItem<DexType, DexMethod, ?>> startupItems) {
+ assert !startupItems.isEmpty();
+ this.startupItems = startupItems;
+ for (StartupItem<DexType, DexMethod, ?> startupItem : startupItems) {
+ if (startupItem.isSynthetic()) {
+ assert startupItem.isStartupClass();
+ syntheticStartupClasses.add(startupItem.asStartupClass().getReference());
} else {
- nonSyntheticStartupClasses.add(startupClass.getReference());
+ DexReference reference =
+ startupItem.apply(StartupClass::getReference, StartupMethod::getReference);
+ nonSyntheticStartupClasses.add(reference.getContextType());
}
}
}
@@ -67,8 +71,8 @@
}
@Override
- public Collection<StartupClass<DexType, DexMethod>> getClasses() {
- return startupClasses;
+ public Collection<StartupItem<DexType, DexMethod, ?>> getItems() {
+ return startupItems;
}
@Override
@@ -78,22 +82,66 @@
@Override
public StartupOrder rewrittenWithLens(GraphLens graphLens) {
- LinkedHashSet<StartupClass<DexType, DexMethod>> rewrittenStartupClasses =
- new LinkedHashSet<>(startupClasses.size());
- for (StartupClass<DexType, DexMethod> startupClass : startupClasses) {
- rewrittenStartupClasses.add(
- StartupClass.dexBuilder()
- .setFlags(startupClass.getFlags())
- .setClassReference(graphLens.lookupType(startupClass.getReference()))
- .build());
+ LinkedHashSet<StartupItem<DexType, DexMethod, ?>> rewrittenStartupItems =
+ new LinkedHashSet<>(startupItems.size());
+ for (StartupItem<DexType, DexMethod, ?> startupItem : startupItems) {
+ if (startupItem.isStartupClass()) {
+ StartupClass<DexType, DexMethod> startupClass = startupItem.asStartupClass();
+ rewrittenStartupItems.add(
+ StartupClass.dexBuilder()
+ .setClassReference(graphLens.lookupType(startupClass.getReference()))
+ .setSynthetic(startupItem.isSynthetic())
+ .build());
+ } else {
+ assert !startupItem.isSynthetic();
+ StartupMethod<DexType, DexMethod> startupMethod = startupItem.asStartupMethod();
+ // TODO(b/238173796): This should account for one-to-many mappings. e.g., when a bridge is
+ // created.
+ rewrittenStartupItems.add(
+ StartupMethod.dexBuilder()
+ .setMethodReference(
+ graphLens.getRenamedMethodSignature(startupMethod.getReference()))
+ .build());
+ }
}
- return createNonEmpty(rewrittenStartupClasses);
+ return createNonEmpty(rewrittenStartupItems);
}
+ /**
+ * This is called to process the startup order before computing the startup layouts.
+ *
+ * <p>This processing makes two key changes to the startup order:
+ *
+ * <ul>
+ * <li>Synthetic startup classes on the form "SLcom/example/SyntheticContext;" represents that
+ * any method of any synthetic class that have been synthesized from SyntheticContext has
+ * been executed. This pass removes such entries from the startup order, and replaces them
+ * by all the methods from all of the synthetics that have been synthesized from
+ * SyntheticContext.
+ * <li>Moreover, this inserts a StartupClass event for all supertypes of a given class next to
+ * the class in the startup order. This ensures that the classes from the super hierarchy
+ * will be laid out close to their subclasses, at the point where the subclasses are used
+ * during startup.
+ * <p>Note that this normally follows from the trace already, except that the class
+ * initializers of interfaces are not executed when a subclass is used.
+ * </ul>
+ */
@Override
public StartupOrder toStartupOrderForWriting(AppView<?> appView) {
- LinkedHashSet<StartupClass<DexType, DexMethod>> rewrittenStartupClasses =
- new LinkedHashSet<>(startupClasses.size());
+ LinkedHashSet<StartupItem<DexType, DexMethod, ?>> rewrittenStartupItems =
+ new LinkedHashSet<>(startupItems.size());
+ Map<DexType, List<DexProgramClass>> syntheticContextsToSyntheticClasses =
+ computeSyntheticContextsToSyntheticClasses(appView);
+ for (StartupItem<DexType, DexMethod, ?> startupItem : startupItems) {
+ addStartupItem(
+ startupItem, rewrittenStartupItems, syntheticContextsToSyntheticClasses, appView);
+ }
+ assert rewrittenStartupItems.stream().noneMatch(StartupItem::isSynthetic);
+ return createNonEmpty(rewrittenStartupItems);
+ }
+
+ private Map<DexType, List<DexProgramClass>> computeSyntheticContextsToSyntheticClasses(
+ AppView<?> appView) {
Map<DexType, List<DexProgramClass>> syntheticContextsToSyntheticClasses =
new IdentityHashMap<>();
for (DexProgramClass clazz : appView.appInfo().classes()) {
@@ -106,84 +154,102 @@
}
}
}
- for (StartupClass<DexType, DexMethod> startupClass : startupClasses) {
- addStartupClass(
- startupClass, rewrittenStartupClasses, syntheticContextsToSyntheticClasses, appView);
- }
- assert rewrittenStartupClasses.stream().noneMatch(StartupClass::isSynthetic);
- return createNonEmpty(rewrittenStartupClasses);
+ return syntheticContextsToSyntheticClasses;
}
- private static void addStartupClass(
- StartupClass<DexType, DexMethod> startupClass,
- LinkedHashSet<StartupClass<DexType, DexMethod>> rewrittenStartupClasses,
+ private static void addStartupItem(
+ StartupItem<DexType, DexMethod, ?> startupItem,
+ LinkedHashSet<StartupItem<DexType, DexMethod, ?>> rewrittenStartupItems,
Map<DexType, List<DexProgramClass>> syntheticContextsToSyntheticClasses,
AppView<?> appView) {
- if (startupClass.isSynthetic()) {
+ if (startupItem.isSynthetic()) {
+ assert startupItem.isStartupClass();
+ StartupClass<DexType, DexMethod> startupClass = startupItem.asStartupClass();
List<DexProgramClass> syntheticClassesForContext =
syntheticContextsToSyntheticClasses.getOrDefault(
startupClass.getReference(), Collections.emptyList());
for (DexProgramClass clazz : syntheticClassesForContext) {
- addClassAndParentClasses(clazz, rewrittenStartupClasses, appView);
+ addClassAndParentClasses(clazz, rewrittenStartupItems, appView);
+ addAllMethods(clazz, rewrittenStartupItems);
}
} else {
- addClassAndParentClasses(startupClass.getReference(), rewrittenStartupClasses, appView);
+ if (startupItem.isStartupClass()) {
+ addClassAndParentClasses(
+ startupItem.asStartupClass().getReference(), rewrittenStartupItems, appView);
+ } else {
+ rewrittenStartupItems.add(startupItem);
+ }
}
}
private static boolean addClass(
DexProgramClass clazz,
- LinkedHashSet<StartupClass<DexType, DexMethod>> rewrittenStartupClasses) {
- return rewrittenStartupClasses.add(
+ LinkedHashSet<StartupItem<DexType, DexMethod, ?>> rewrittenStartupItems) {
+ return rewrittenStartupItems.add(
StartupClass.dexBuilder().setClassReference(clazz.getType()).build());
}
private static void addClassAndParentClasses(
DexType type,
- LinkedHashSet<StartupClass<DexType, DexMethod>> rewrittenStartupClasses,
+ LinkedHashSet<StartupItem<DexType, DexMethod, ?>> rewrittenStartupItems,
AppView<?> appView) {
DexProgramClass definition = appView.app().programDefinitionFor(type);
if (definition != null) {
- addClassAndParentClasses(definition, rewrittenStartupClasses, appView);
+ addClassAndParentClasses(definition, rewrittenStartupItems, appView);
}
}
private static void addClassAndParentClasses(
DexProgramClass clazz,
- LinkedHashSet<StartupClass<DexType, DexMethod>> rewrittenStartupClasses,
+ LinkedHashSet<StartupItem<DexType, DexMethod, ?>> rewrittenStartupItems,
AppView<?> appView) {
- if (addClass(clazz, rewrittenStartupClasses)) {
- addParentClasses(clazz, rewrittenStartupClasses, appView);
+ if (addClass(clazz, rewrittenStartupItems)) {
+ addParentClasses(clazz, rewrittenStartupItems, appView);
}
}
private static void addParentClasses(
DexProgramClass clazz,
- LinkedHashSet<StartupClass<DexType, DexMethod>> rewrittenStartupClasses,
+ LinkedHashSet<StartupItem<DexType, DexMethod, ?>> rewrittenStartupItems,
AppView<?> appView) {
clazz.forEachImmediateSupertype(
- supertype -> addClassAndParentClasses(supertype, rewrittenStartupClasses, appView));
+ supertype -> addClassAndParentClasses(supertype, rewrittenStartupItems, appView));
+ }
+
+ private static void addAllMethods(
+ DexProgramClass clazz,
+ LinkedHashSet<StartupItem<DexType, DexMethod, ?>> rewrittenStartupItems) {
+ clazz.forEachProgramMethod(
+ method ->
+ rewrittenStartupItems.add(
+ StartupMethod.dexBuilder().setMethodReference(method.getReference()).build()));
}
@Override
public StartupOrder withoutPrunedItems(PrunedItems prunedItems, SyntheticItems syntheticItems) {
- LinkedHashSet<StartupClass<DexType, DexMethod>> rewrittenStartupClasses =
- new LinkedHashSet<>(startupClasses.size());
+ LinkedHashSet<StartupItem<DexType, DexMethod, ?>> rewrittenStartupItems =
+ new LinkedHashSet<>(startupItems.size());
LazyBox<Set<DexType>> contextsOfLiveSynthetics =
new LazyBox<>(
() -> computeContextsOfLiveSynthetics(prunedItems.getPrunedApp(), syntheticItems));
- for (StartupClass<DexType, DexMethod> startupClass : startupClasses) {
+ for (StartupItem<DexType, DexMethod, ?> startupItem : startupItems) {
// Only prune non-synthetic classes, since the pruning of a class does not imply that all
// classes synthesized from it have been pruned.
- if (startupClass.isSynthetic()) {
+ if (startupItem.isSynthetic()) {
+ assert startupItem.isStartupClass();
+ StartupClass<DexType, DexMethod> startupClass = startupItem.asStartupClass();
if (contextsOfLiveSynthetics.computeIfAbsent().contains(startupClass.getReference())) {
- rewrittenStartupClasses.add(startupClass);
+ rewrittenStartupItems.add(startupClass);
}
- } else if (!prunedItems.isRemoved(startupClass.getReference())) {
- rewrittenStartupClasses.add(startupClass);
+ } else {
+ DexReference reference =
+ startupItem.apply(StartupClass::getReference, StartupMethod::getReference);
+ if (!prunedItems.isRemoved(reference)) {
+ rewrittenStartupItems.add(startupItem);
+ }
}
}
- return createNonEmpty(rewrittenStartupClasses);
+ return createNonEmpty(rewrittenStartupItems);
}
private Set<DexType> computeContextsOfLiveSynthetics(
@@ -199,11 +265,11 @@
}
private StartupOrder createNonEmpty(
- LinkedHashSet<StartupClass<DexType, DexMethod>> startupClasses) {
- if (startupClasses.isEmpty()) {
+ LinkedHashSet<StartupItem<DexType, DexMethod, ?>> startupItems) {
+ if (startupItems.isEmpty()) {
assert false;
return empty();
}
- return new NonEmptyStartupOrder(startupClasses);
+ return new NonEmptyStartupOrder(startupItems);
}
}
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupClass.java b/src/main/java/com/android/tools/r8/experimental/startup/StartupClass.java
index 0f76036..7ae4d30 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/StartupClass.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/StartupClass.java
@@ -7,6 +7,10 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.MethodReference;
+import java.util.function.Consumer;
+import java.util.function.Function;
// TODO(b/238173796): When updating the compiler to have support for taking a list of startup
// methods, this class may likely be removed along with the StartupItem class, so that only
@@ -25,6 +29,23 @@
return new Builder<>();
}
+ public static Builder<ClassReference, MethodReference> referenceBuilder() {
+ return new Builder<>();
+ }
+
+ @Override
+ public void accept(
+ Consumer<StartupClass<C, M>> classConsumer, Consumer<StartupMethod<C, M>> methodConsumer) {
+ classConsumer.accept(this);
+ }
+
+ @Override
+ public <T> T apply(
+ Function<StartupClass<C, M>, T> classFunction,
+ Function<StartupMethod<C, M>, T> methodFunction) {
+ return classFunction.apply(this);
+ }
+
@Override
public boolean isStartupClass() {
return true;
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupConfiguration.java b/src/main/java/com/android/tools/r8/experimental/startup/StartupConfiguration.java
index 9731423..2f1a23d 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/StartupConfiguration.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/StartupConfiguration.java
@@ -21,10 +21,10 @@
public class StartupConfiguration {
- private final List<StartupClass<DexType, DexMethod>> startupClasses;
+ private final List<StartupItem<DexType, DexMethod, ?>> startupItems;
- public StartupConfiguration(List<StartupClass<DexType, DexMethod>> startupClasses) {
- this.startupClasses = startupClasses;
+ public StartupConfiguration(List<StartupItem<DexType, DexMethod, ?>> startupItems) {
+ this.startupItems = startupItems;
}
public static Builder builder() {
@@ -74,59 +74,43 @@
public static StartupConfiguration createStartupConfigurationFromLines(
DexItemFactory dexItemFactory, Reporter reporter, List<String> startupDescriptors) {
- List<StartupClass<DexType, DexMethod>> startupClasses = new ArrayList<>();
+ List<StartupItem<DexType, DexMethod, ?>> startupItems = new ArrayList<>();
StartupConfigurationParser.createDexParser(dexItemFactory)
.parseLines(
startupDescriptors,
- startupClasses::add,
- // TODO(b/238173796): Startup methods should be added as startup methods.
- startupMethod ->
- startupClasses.add(
- StartupClass.dexBuilder()
- .setClassReference(startupMethod.getReference().getHolderType())
- .setFlags(startupMethod.getFlags())
- .build()),
+ startupItems::add,
+ startupItems::add,
error ->
reporter.warning(
new StringDiagnostic(
"Invalid descriptor for startup class or method: " + error)));
- return new StartupConfiguration(startupClasses);
+ return new StartupConfiguration(startupItems);
}
- public boolean hasStartupClasses() {
- return !startupClasses.isEmpty();
+ public boolean hasStartupItems() {
+ return !startupItems.isEmpty();
}
- public List<StartupClass<DexType, DexMethod>> getStartupClasses() {
- return startupClasses;
+ public List<StartupItem<DexType, DexMethod, ?>> getStartupItems() {
+ return startupItems;
}
public static class Builder {
- private final ImmutableList.Builder<StartupClass<DexType, DexMethod>> startupClassesBuilder =
+ private final ImmutableList.Builder<StartupItem<DexType, DexMethod, ?>> startupItemsBuilder =
ImmutableList.builder();
public Builder addStartupItem(StartupItem<DexType, DexMethod, ?> startupItem) {
- if (startupItem.isStartupClass()) {
- return addStartupClass(startupItem.asStartupClass());
- } else {
- assert startupItem.isStartupMethod();
- return addStartupMethod(startupItem.asStartupMethod());
- }
- }
-
- public Builder addStartupClass(StartupClass<DexType, DexMethod> startupClass) {
- this.startupClassesBuilder.add(startupClass);
+ this.startupItemsBuilder.add(startupItem);
return this;
}
+ public Builder addStartupClass(StartupClass<DexType, DexMethod> startupClass) {
+ return addStartupItem(startupClass);
+ }
+
public Builder addStartupMethod(StartupMethod<DexType, DexMethod> startupMethod) {
- // TODO(b/238173796): Startup methods should be added as startup methods.
- return addStartupClass(
- StartupClass.dexBuilder()
- .setFlags(startupMethod.getFlags())
- .setClassReference(startupMethod.getReference().getHolderType())
- .build());
+ return addStartupItem(startupMethod);
}
public Builder apply(Consumer<Builder> consumer) {
@@ -135,7 +119,7 @@
}
public StartupConfiguration build() {
- return new StartupConfiguration(startupClassesBuilder.build());
+ return new StartupConfiguration(startupItemsBuilder.build());
}
}
}
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupInstrumentation.java b/src/main/java/com/android/tools/r8/experimental/startup/StartupInstrumentation.java
index 0678602..76abc9d 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/StartupInstrumentation.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/StartupInstrumentation.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.experimental.startup;
+import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
import static com.android.tools.r8.utils.PredicateUtils.not;
import com.android.tools.r8.androidapi.ComputedApiLevel;
@@ -18,6 +19,7 @@
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexValue.DexValueBoolean;
import com.android.tools.r8.graph.DexValue.DexValueString;
@@ -32,9 +34,12 @@
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.ir.conversion.IRToDexFinalizer;
+import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
import com.android.tools.r8.startup.generated.InstrumentationServerFactory;
import com.android.tools.r8.startup.generated.InstrumentationServerImplFactory;
import com.android.tools.r8.synthesis.SyntheticItems;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
import com.google.common.collect.ImmutableList;
@@ -48,15 +53,17 @@
private final AppView<AppInfo> appView;
private final IRConverter converter;
private final DexItemFactory dexItemFactory;
- private final StartupOptions options;
+ private final InternalOptions options;
private final StartupReferences references;
+ private final StartupOptions startupOptions;
private StartupInstrumentation(AppView<AppInfo> appView) {
this.appView = appView;
this.converter = new IRConverter(appView, Timing.empty());
this.dexItemFactory = appView.dexItemFactory();
- this.options = appView.options().getStartupOptions();
+ this.options = appView.options();
this.references = new StartupReferences(dexItemFactory);
+ this.startupOptions = options.getStartupOptions();
}
public static void run(AppView<AppInfo> appView, ExecutorService executorService)
@@ -74,6 +81,24 @@
private void injectStartupRuntimeLibrary(ExecutorService executorService)
throws ExecutionException {
+ // Only inject the startup instrumentation server if it is not already in the app.
+ if (appView.definitionFor(references.instrumentationServerImplType) != null) {
+ return;
+ }
+
+ // If the startup options has a synthetic context for the startup instrumentation server, then
+ // only inject the runtime library if the synthetic context exists in program to avoid injecting
+ // the runtime library multiple times when there is separate compilation.
+ if (startupOptions.hasStartupInstrumentationServerSyntheticContext()) {
+ DexType syntheticContext =
+ dexItemFactory.createType(
+ DescriptorUtils.javaTypeToDescriptor(
+ startupOptions.getStartupInstrumentationServerSyntheticContext()));
+ if (asProgramClassOrNull(appView.definitionFor(syntheticContext)) == null) {
+ return;
+ }
+ }
+
List<DexProgramClass> extraProgramClasses = createStartupRuntimeLibraryClasses();
converter.processClassesConcurrently(extraProgramClasses, executorService);
@@ -88,7 +113,7 @@
private List<DexProgramClass> createStartupRuntimeLibraryClasses() {
DexProgramClass instrumentationServerImplClass =
InstrumentationServerImplFactory.createClass(dexItemFactory);
- if (options.hasStartupInstrumentationTag()) {
+ if (startupOptions.hasStartupInstrumentationTag()) {
instrumentationServerImplClass
.lookupUniqueStaticFieldWithName(dexItemFactory.createString("writeToLogcat"))
.setStaticValue(DexValueBoolean.create(true));
@@ -96,7 +121,7 @@
.lookupUniqueStaticFieldWithName(dexItemFactory.createString("logcatTag"))
.setStaticValue(
new DexValueString(
- dexItemFactory.createString(options.getStartupInstrumentationTag())));
+ dexItemFactory.createString(startupOptions.getStartupInstrumentationTag())));
}
return ImmutableList.of(
@@ -104,57 +129,93 @@
}
private void instrumentClass(DexProgramClass clazz) {
- ensureClassInitializer(clazz);
- clazz.forEachProgramMethod(this::instrumentMethod);
- }
-
- private void ensureClassInitializer(DexProgramClass clazz) {
- if (!clazz.hasClassInitializer()) {
- ComputedApiLevel computedApiLevel =
- appView.apiLevelCompute().computeInitialMinApiLevel(appView.options());
- DexReturnVoid returnInstruction = new DexReturnVoid();
- returnInstruction.setOffset(0);
- clazz.addDirectMethod(
- DexEncodedMethod.syntheticBuilder()
- .setAccessFlags(MethodAccessFlags.createForClassInitializer())
- .setApiLevelForCode(computedApiLevel)
- .setApiLevelForDefinition(computedApiLevel)
- .setClassFileVersion(CfVersion.V1_6)
- .setCode(new DexCode(0, 0, 0, new DexInstruction[] {returnInstruction}))
- .setMethod(dexItemFactory.createClassInitializer(clazz.getType()))
- .build());
- }
- }
-
- private void instrumentMethod(ProgramMethod method) {
- DexMethod methodToInvoke;
- DexMethod methodToPrint;
- SyntheticItems syntheticItems = appView.getSyntheticItems();
- if (syntheticItems.isSyntheticClass(method.getHolder())) {
- Collection<DexType> synthesizingContexts =
- syntheticItems.getSynthesizingContextTypes(method.getHolderType());
- assert synthesizingContexts.size() == 1;
- DexType synthesizingContext = synthesizingContexts.iterator().next();
- methodToInvoke = references.addSyntheticMethod;
- methodToPrint = method.getReference().withHolder(synthesizingContext, dexItemFactory);
- } else {
- methodToInvoke = references.addNonSyntheticMethod;
- methodToPrint = method.getReference();
+ // Do not instrument the instrumentation server if it is already in the app.
+ if (clazz.getType() == references.instrumentationServerType
+ || clazz.getType() == references.instrumentationServerImplType) {
+ return;
}
- IRCode code = method.buildIR(appView);
+ boolean addedClassInitializer = ensureClassInitializer(clazz);
+ clazz.forEachProgramMethodMatching(
+ DexEncodedMethod::hasCode,
+ method ->
+ instrumentMethod(
+ method, method.getDefinition().isClassInitializer() && addedClassInitializer));
+ }
+
+ private boolean ensureClassInitializer(DexProgramClass clazz) {
+ if (clazz.hasClassInitializer()) {
+ return false;
+ }
+ ComputedApiLevel computedApiLevel =
+ appView.apiLevelCompute().computeInitialMinApiLevel(options);
+ DexReturnVoid returnInstruction = new DexReturnVoid();
+ returnInstruction.setOffset(0);
+ clazz.addDirectMethod(
+ DexEncodedMethod.syntheticBuilder()
+ .setAccessFlags(MethodAccessFlags.createForClassInitializer())
+ .setApiLevelForCode(computedApiLevel)
+ .setApiLevelForDefinition(computedApiLevel)
+ .setClassFileVersion(CfVersion.V1_6)
+ .setCode(new DexCode(0, 0, 0, new DexInstruction[] {returnInstruction}))
+ .setMethod(dexItemFactory.createClassInitializer(clazz.getType()))
+ .build());
+ return true;
+ }
+
+ private void instrumentMethod(ProgramMethod method, boolean skipMethodLogging) {
+ // Disable StringSwitch conversion to avoid having to run the StringSwitchRemover before
+ // finalizing the code.
+ MutableMethodConversionOptions conversionOptions =
+ new MutableMethodConversionOptions(options).disableStringSwitchConversion();
+ IRCode code = method.buildIR(appView, conversionOptions);
InstructionListIterator instructionIterator = code.entryBlock().listIterator(code);
instructionIterator.positionBeforeNextInstructionThatMatches(not(Instruction::isArgument));
- Value descriptorValue =
- instructionIterator.insertConstStringInstruction(
- appView, code, dexItemFactory.createString(methodToPrint.toSmaliString()));
- instructionIterator.add(
- InvokeStatic.builder()
- .setMethod(methodToInvoke)
- .setSingleArgument(descriptorValue)
- .setPosition(Position.syntheticNone())
- .build());
+ // Insert invoke to record that the enclosing class is a startup class.
+ SyntheticItems syntheticItems = appView.getSyntheticItems();
+ boolean isSyntheticClass = syntheticItems.isSyntheticClass(method.getHolder());
+ if (method.getDefinition().isClassInitializer() && !isSyntheticClass) {
+ DexMethod methodToInvoke = references.addNonSyntheticMethod;
+ DexType classToPrint = method.getHolderType();
+ Value descriptorValue =
+ instructionIterator.insertConstStringInstruction(
+ appView, code, dexItemFactory.createString(classToPrint.toSmaliString()));
+ instructionIterator.add(
+ InvokeStatic.builder()
+ .setMethod(methodToInvoke)
+ .setSingleArgument(descriptorValue)
+ .setPosition(Position.syntheticNone())
+ .build());
+ }
+
+ // Insert invoke to record the execution of the current method.
+ if (!skipMethodLogging) {
+ DexMethod methodToInvoke;
+ DexReference referenceToPrint;
+ if (isSyntheticClass) {
+ Collection<DexType> synthesizingContexts =
+ syntheticItems.getSynthesizingContextTypes(method.getHolderType());
+ assert synthesizingContexts.size() == 1;
+ DexType synthesizingContext = synthesizingContexts.iterator().next();
+ methodToInvoke = references.addSyntheticMethod;
+ referenceToPrint = synthesizingContext;
+ } else {
+ methodToInvoke = references.addNonSyntheticMethod;
+ referenceToPrint = method.getReference();
+ }
+
+ Value descriptorValue =
+ instructionIterator.insertConstStringInstruction(
+ appView, code, dexItemFactory.createString(referenceToPrint.toSmaliString()));
+ instructionIterator.add(
+ InvokeStatic.builder()
+ .setMethod(methodToInvoke)
+ .setSingleArgument(descriptorValue)
+ .setPosition(Position.syntheticNone())
+ .build());
+ }
+
DexCode instrumentedCode =
new IRToDexFinalizer(appView, converter.deadCodeRemover)
.finalizeCode(code, BytecodeMetadataProvider.empty(), Timing.empty());
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupItem.java b/src/main/java/com/android/tools/r8/experimental/startup/StartupItem.java
index c745c90..74e69e2 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/StartupItem.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/StartupItem.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import java.util.function.Consumer;
+import java.util.function.Function;
public abstract class StartupItem<C, M, R> {
@@ -20,6 +21,13 @@
this.reference = reference;
}
+ public abstract void accept(
+ Consumer<StartupClass<C, M>> classConsumer, Consumer<StartupMethod<C, M>> methodConsumer);
+
+ public abstract <T> T apply(
+ Function<StartupClass<C, M>, T> classFunction,
+ Function<StartupMethod<C, M>, T> methodFunction);
+
public boolean isStartupClass() {
return false;
}
@@ -118,6 +126,14 @@
return self();
}
+ public B setSynthetic(boolean synthetic) {
+ if (synthetic) {
+ return setSynthetic();
+ }
+ assert (flags & FLAG_SYNTHETIC) == 0;
+ return self();
+ }
+
public StartupItem<C, M, ?> build() {
if (classReference != null) {
return buildStartupClass();
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupMethod.java b/src/main/java/com/android/tools/r8/experimental/startup/StartupMethod.java
index 3109611..871e179 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/StartupMethod.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/StartupMethod.java
@@ -7,6 +7,8 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.MethodReference;
+import java.util.function.Consumer;
+import java.util.function.Function;
public class StartupMethod<C, M> extends StartupItem<C, M, M> {
@@ -19,6 +21,19 @@
}
@Override
+ public void accept(
+ Consumer<StartupClass<C, M>> classConsumer, Consumer<StartupMethod<C, M>> methodConsumer) {
+ methodConsumer.accept(this);
+ }
+
+ @Override
+ public <T> T apply(
+ Function<StartupClass<C, M>, T> classFunction,
+ Function<StartupMethod<C, M>, T> methodFunction) {
+ return methodFunction.apply(this);
+ }
+
+ @Override
public boolean isStartupMethod() {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupOptions.java b/src/main/java/com/android/tools/r8/experimental/startup/StartupOptions.java
index 15fbf6c..7ff913d 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/StartupOptions.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/StartupOptions.java
@@ -9,19 +9,76 @@
public class StartupOptions {
+ /**
+ * When enabled, all startup classes will be placed in the primary classes.dex file. All other
+ * (non-startup) classes will be placed in classes2.dex, ..., classesN.dex.
+ */
private boolean enableMinimalStartupDex =
parseSystemPropertyForDevelopmentOrDefault(
"com.android.tools.r8.startup.minimalstartupdex", false);
+
+ /**
+ * When enabled, each method that is not classified as a startup method at the end of compilation
+ * will be changed to have a throwing method body.
+ *
+ * <p>This is useful for testing if a given startup list is complete (and that R8 correctly
+ * rewrites the startup list in presence of optimizations).
+ */
private boolean enableStartupCompletenessCheckForTesting =
parseSystemPropertyForDevelopmentOrDefault(
"com.android.tools.r8.startup.completenesscheck", false);
+
+ /**
+ * When enabled, each method will be instrumented to notify the startup InstrumentationServer that
+ * it has been executed.
+ *
+ * <p>This will also inject the startup runtime library (i.e., the InstrumentationServer) into the
+ * app.
+ */
private boolean enableStartupInstrumentation =
parseSystemPropertyForDevelopmentOrDefault("com.android.tools.r8.startup.instrument", false);
+
+ /**
+ * Specifies the synthetic context of the startup runtime library. When this is set, the startup
+ * runtime library will only be injected into the app when the synthetic context is in the
+ * program. This can be used to avoid that the startup runtime library is injected multiple times
+ * in presence of separate compilation.
+ *
+ * <p>Example synthetic context: "app.tivi.home.MainActivity".
+ *
+ * <p>Note that this is only meaningful when {@link #enableStartupInstrumentation} is set to true.
+ */
+ private String startupInstrumentationServerSyntheticContext =
+ getSystemPropertyForDevelopment(
+ "com.android.tools.r8.startup.instrumentationserversyntheticcontext");
+
+ /**
+ * Specifies the logcat tag that should be used by the InstrumentationServer when logging events.
+ *
+ * <p>When a logcat tag is not specified, the InstrumentationServer will not print events to
+ * logcat. Instead, the startup events must be obtained by requesting the InstrumentationServer to
+ * write the events to a file.
+ */
private String startupInstrumentationTag =
getSystemPropertyForDevelopment("com.android.tools.r8.startup.instrumentationtag");
private StartupConfiguration startupConfiguration;
+ public boolean hasStartupInstrumentationServerSyntheticContext() {
+ return startupInstrumentationServerSyntheticContext != null;
+ }
+
+ public String getStartupInstrumentationServerSyntheticContext() {
+ return startupInstrumentationServerSyntheticContext;
+ }
+
+ public StartupOptions setStartupInstrumentationServerSyntheticContext(
+ String startupInstrumentationServerSyntheticContext) {
+ this.startupInstrumentationServerSyntheticContext =
+ startupInstrumentationServerSyntheticContext;
+ return this;
+ }
+
public boolean hasStartupInstrumentationTag() {
return startupInstrumentationTag != null;
}
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupOrder.java b/src/main/java/com/android/tools/r8/experimental/startup/StartupOrder.java
index 1676991..b1803eb 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/StartupOrder.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/StartupOrder.java
@@ -24,10 +24,10 @@
}
StartupConfiguration startupConfiguration =
options.getStartupOptions().getStartupConfiguration();
- if (!startupConfiguration.hasStartupClasses()) {
+ if (!startupConfiguration.hasStartupItems()) {
return empty();
}
- return new NonEmptyStartupOrder(new LinkedHashSet<>(startupConfiguration.getStartupClasses()));
+ return new NonEmptyStartupOrder(new LinkedHashSet<>(startupConfiguration.getStartupItems()));
}
public static StartupOrder empty() {
@@ -36,7 +36,7 @@
public abstract boolean contains(DexType type, SyntheticItems syntheticItems);
- public abstract Collection<StartupClass<DexType, DexMethod>> getClasses();
+ public abstract Collection<StartupItem<DexType, DexMethod, ?>> getItems();
public abstract boolean isEmpty();
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupReferences.java b/src/main/java/com/android/tools/r8/experimental/startup/StartupReferences.java
index 0c2de77..a041f1e 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/StartupReferences.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/StartupReferences.java
@@ -10,11 +10,14 @@
public class StartupReferences {
+ final DexType instrumentationServerType;
final DexType instrumentationServerImplType;
final DexMethod addNonSyntheticMethod;
final DexMethod addSyntheticMethod;
StartupReferences(DexItemFactory dexItemFactory) {
+ instrumentationServerType =
+ dexItemFactory.createType("Lcom/android/tools/r8/startup/InstrumentationServer;");
instrumentationServerImplType =
dexItemFactory.createType("Lcom/android/tools/r8/startup/InstrumentationServerImpl;");
addNonSyntheticMethod =
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java b/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
index 1a029f8..b891c89 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
@@ -28,6 +28,7 @@
import java.util.Collections;
import java.util.Deque;
import java.util.List;
+import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
@@ -556,6 +557,16 @@
.lookupMaximallySpecificTarget(clazz, method);
}
+ /**
+ * Helper methods used for emulated interface resolution (not in JVM specifications). Answers the
+ * abstract interface methods that the resolution could but does not necessarily resolve into.
+ */
+ public List<Entry<DexClass, DexEncodedMethod>> getAbstractInterfaceMethods(
+ DexClass clazz, DexMethod method) {
+ return MethodResolution.createLegacy(this::definitionFor, dexItemFactory())
+ .getAbstractInterfaceMethods(clazz, method);
+ }
+
MethodResolutionResult resolveMaximallySpecificTarget(DexClass clazz, DexMethod method) {
return MethodResolution.createLegacy(this::definitionFor, dexItemFactory())
.resolveMaximallySpecificTarget(clazz, method);
diff --git a/src/main/java/com/android/tools/r8/graph/DexApplication.java b/src/main/java/com/android/tools/r8/graph/DexApplication.java
index 5dd4a97..49bfc86 100644
--- a/src/main/java/com/android/tools/r8/graph/DexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/DexApplication.java
@@ -7,12 +7,10 @@
package com.android.tools.r8.graph;
import com.android.tools.r8.DataResourceProvider;
-import com.android.tools.r8.dex.ApplicationReader.ProgramClassConflictResolver;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.synthesis.SyntheticDefinitionsProvider;
import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.ProgramClassCollection;
import com.android.tools.r8.utils.Timing;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
@@ -265,13 +263,7 @@
}
public static LazyLoadedDexApplication.Builder builder(InternalOptions options, Timing timing) {
- return builder(
- options, timing, ProgramClassCollection.defaultConflictResolver(options.reporter));
- }
-
- public static LazyLoadedDexApplication.Builder builder(
- InternalOptions options, Timing timing, ProgramClassConflictResolver resolver) {
- return new LazyLoadedDexApplication.Builder(resolver, options, timing);
+ return new LazyLoadedDexApplication.Builder(options, timing);
}
public DirectMappedDexApplication asDirect() {
diff --git a/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java b/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
index 8859c3c..610a0c8 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
@@ -326,10 +326,6 @@
.withNullableItem(m -> m.rewrittenTarget);
}
- public Handle toAsmHandle() {
- return toAsmHandle(NamingLens.getIdentityLens());
- }
-
public Handle toAsmHandle(NamingLens lens) {
String owner;
String name;
diff --git a/src/main/java/com/android/tools/r8/graph/DexTypeList.java b/src/main/java/com/android/tools/r8/graph/DexTypeList.java
index afba589..880b51d 100644
--- a/src/main/java/com/android/tools/r8/graph/DexTypeList.java
+++ b/src/main/java/com/android/tools/r8/graph/DexTypeList.java
@@ -104,6 +104,12 @@
}
}
+ public void forEachReverse(Consumer<? super DexType> consumer) {
+ for (int i = values.length - 1; i >= 0; i--) {
+ consumer.accept(values[i]);
+ }
+ }
+
@Override
public int hashCode() {
return Arrays.hashCode(values);
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 73606cc..f648f90 100644
--- a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
+++ b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
@@ -534,6 +534,13 @@
return;
}
application.addRecordWitness(type, classKind);
+ if (classKind != ClassKind.PROGRAM) {
+ // Non program classes may just be headers, in which case instance fields and annotations
+ // may be partially stripped, leading to non matching record components and instance fields.
+ // Record desugaring does not need information beyond the record flag on non program class,
+ // so it's safe to compile even if there is a missmatch.
+ return;
+ }
// TODO(b/169645628): Change this logic if we start stripping the record components.
// Another approach would be to mark a bit in fields that are record components instead.
String message = "Records are expected to have one record component per instance field.";
diff --git a/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java b/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java
index 584729a..80bb4cf 100644
--- a/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java
@@ -271,18 +271,15 @@
private ClasspathClassCollection classpathClasses;
private LibraryClassCollection libraryClasses;
- private final ProgramClassConflictResolver resolver;
- Builder(ProgramClassConflictResolver resolver, InternalOptions options, Timing timing) {
+ Builder(InternalOptions options, Timing timing) {
super(options, timing);
- this.resolver = resolver;
this.classpathClasses = ClasspathClassCollection.empty();
this.libraryClasses = null;
}
private Builder(LazyLoadedDexApplication application) {
super(application);
- this.resolver = ProgramClassCollection.defaultConflictResolver(application.options.reporter);
this.classpathClasses = application.classpathClasses;
this.libraryClasses = application.libraryClasses;
}
@@ -311,6 +308,10 @@
@Override
public LazyLoadedDexApplication build() {
+ ProgramClassConflictResolver resolver =
+ options.programClassConflictResolver == null
+ ? ProgramClassCollection.defaultConflictResolver(options.reporter)
+ : options.programClassConflictResolver;
return new LazyLoadedDexApplication(
proguardMap,
flags,
diff --git a/src/main/java/com/android/tools/r8/graph/MethodResolution.java b/src/main/java/com/android/tools/r8/graph/MethodResolution.java
index f7271b7..11f9bd9 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodResolution.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodResolution.java
@@ -241,6 +241,12 @@
return resolveMaximallySpecificTargetHelper(clazz, method).lookup();
}
+ // Non-private method used for emulated interface only.
+ List<Entry<DexClass, DexEncodedMethod>> getAbstractInterfaceMethods(
+ DexClass clazz, DexMethod method) {
+ return resolveMaximallySpecificTargetHelper(clazz, method).getAbstractMethods();
+ }
+
private MaximallySpecificMethodsBuilder resolveMaximallySpecificTargetHelper(
DexClass clazz, DexMethod method) {
MaximallySpecificMethodsBuilder builder =
@@ -706,6 +712,28 @@
return nonAbstractMethods;
}
+ List<Entry<DexClass, DexEncodedMethod>> getAbstractMethods() {
+ List<Entry<DexClass, DexEncodedMethod>> abstractMethods = new ArrayList<>();
+ addAbstractMethods(abstractMethods, maximallySpecificMethodsOnCompletePaths);
+ addAbstractMethods(abstractMethods, maximallySpecificMethodsOnIncompletePaths);
+ return abstractMethods;
+ }
+
+ private void addAbstractMethods(
+ List<Entry<DexClass, DexEncodedMethod>> abstractMethods,
+ Map<DexClass, DexEncodedMethod> candidates) {
+ for (Entry<DexClass, DexEncodedMethod> entry : candidates.entrySet()) {
+ DexEncodedMethod method = entry.getValue();
+ if (method == null) {
+ // Ignore shadowed candidates.
+ continue;
+ }
+ if (method.isAbstract()) {
+ abstractMethods.add(entry);
+ }
+ }
+ }
+
private static SingleResolutionResult<?> singleResultHelper(
DexClass initialResolutionResult, Entry<DexClass, DexEncodedMethod> entry) {
return MethodResolutionResult.createSingleResolutionResult(
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
index 2154c8c..173161f 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
@@ -10,6 +10,7 @@
import static com.android.tools.r8.ir.code.Opcodes.CHECK_CAST;
import static com.android.tools.r8.ir.code.Opcodes.CONST_CLASS;
import static com.android.tools.r8.ir.code.Opcodes.CONST_METHOD_HANDLE;
+import static com.android.tools.r8.ir.code.Opcodes.CONST_METHOD_TYPE;
import static com.android.tools.r8.ir.code.Opcodes.INIT_CLASS;
import static com.android.tools.r8.ir.code.Opcodes.INSTANCE_GET;
import static com.android.tools.r8.ir.code.Opcodes.INSTANCE_OF;
@@ -46,6 +47,7 @@
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexMethodHandle;
+import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.GraphLens.FieldLookupResult;
@@ -70,6 +72,7 @@
import com.android.tools.r8.ir.code.CheckCast;
import com.android.tools.r8.ir.code.ConstClass;
import com.android.tools.r8.ir.code.ConstMethodHandle;
+import com.android.tools.r8.ir.code.ConstMethodType;
import com.android.tools.r8.ir.code.FieldInstruction;
import com.android.tools.r8.ir.code.FieldPut;
import com.android.tools.r8.ir.code.IRCode;
@@ -85,6 +88,7 @@
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.InvokeMultiNewArray;
import com.android.tools.r8.ir.code.InvokeNewArray;
+import com.android.tools.r8.ir.code.InvokePolymorphic;
import com.android.tools.r8.ir.code.MoveException;
import com.android.tools.r8.ir.code.NewArrayEmpty;
import com.android.tools.r8.ir.code.NewInstance;
@@ -99,6 +103,7 @@
import com.android.tools.r8.ir.code.UnusedArgument;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.code.ValueType;
+import com.android.tools.r8.ir.optimize.CodeRewriter;
import com.android.tools.r8.ir.optimize.enums.EnumUnboxer;
import com.android.tools.r8.optimize.MemberRebindingAnalysis;
import com.android.tools.r8.optimize.argumentpropagation.lenscoderewriter.NullCheckInserter;
@@ -220,6 +225,15 @@
assert graphLens.getPrevious() == codeLens;
affectedPhis.addAll(enumUnboxer.rewriteCode(code, methodProcessor, prototypeChanges));
}
+ if (!unusedArguments.isEmpty()) {
+ for (UnusedArgument unusedArgument : unusedArguments) {
+ if (unusedArgument.outValue().hasPhiUsers()) {
+ // See b/240282988: We can end up in situations where the second round of IR processing
+ // introduce phis for irreducible control flow, we need to resolve them.
+ CodeRewriter.replaceUnusedArgumentTrivialPhis(unusedArgument);
+ }
+ }
+ }
rewritePartialDefault(
code, method, graphLens, codeLens, prototypeChanges, affectedPhis, unusedArguments);
}
@@ -277,11 +291,18 @@
.computeIfAbsent()
.rewriteDexMethodHandle(handle, NOT_ARGUMENT_TO_LAMBDA_METAFACTORY, method);
if (newHandle != handle) {
- Value newOutValue = makeOutValue(current, code, graphLens, codeLens);
- iterator.replaceCurrentInstruction(new ConstMethodHandle(newOutValue, newHandle));
- if (newOutValue != null && newOutValue.getType() != current.getOutType()) {
- affectedPhis.addAll(newOutValue.uniquePhiUsers());
- }
+ iterator.replaceCurrentInstruction(
+ new ConstMethodHandle(current.outValue(), newHandle));
+ }
+ }
+ break;
+ case CONST_METHOD_TYPE:
+ {
+ ConstMethodType constType = current.asConstMethodType();
+ DexProto rewrittenProto = helper.computeIfAbsent().rewriteProto(constType.getValue());
+ if (constType.getValue() != rewrittenProto) {
+ iterator.replaceCurrentInstruction(
+ new ConstMethodType(constType.outValue(), rewrittenProto));
}
}
break;
@@ -298,9 +319,25 @@
}
break;
+ case INVOKE_POLYMORPHIC:
+ {
+ InvokePolymorphic invoke = current.asInvokePolymorphic();
+ // The invoked method is on java.lang.invoke.MethodHandle and always remains as is.
+ assert factory.polymorphicMethods.isPolymorphicInvoke(invoke.getInvokedMethod());
+ // Rewrite the signature of the handles actual target.
+ DexProto rewrittenProto = helper.computeIfAbsent().rewriteProto(invoke.getProto());
+ if (invoke.getProto() != rewrittenProto) {
+ iterator.replaceCurrentInstruction(
+ new InvokePolymorphic(
+ invoke.getInvokedMethod(),
+ rewrittenProto,
+ invoke.outValue(),
+ invoke.arguments()));
+ }
+ }
+ break;
case INVOKE_DIRECT:
case INVOKE_INTERFACE:
- case INVOKE_POLYMORPHIC:
case INVOKE_STATIC:
case INVOKE_SUPER:
case INVOKE_VIRTUAL:
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/MethodConversionOptions.java b/src/main/java/com/android/tools/r8/ir/conversion/MethodConversionOptions.java
index 038a277..adf0102 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/MethodConversionOptions.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/MethodConversionOptions.java
@@ -35,8 +35,9 @@
enablePeepholeOptimizations = false;
}
- public void disableStringSwitchConversion() {
+ public MutableMethodConversionOptions disableStringSwitchConversion() {
enableStringSwitchConversion = false;
+ return this;
}
public MutableMethodConversionOptions setIsGeneratingClassFiles(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
index c48cd2f..3e41a70 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
@@ -137,9 +137,7 @@
if (recordRewriter != null) {
desugarings.add(recordRewriter);
}
- if (appView.options().enableUnrepresentableInDexInstructionRemoval) {
- yieldingDesugarings.add(new UnrepresentableInDexInstructionRemover(appView));
- }
+ yieldingDesugarings.add(new UnrepresentableInDexInstructionRemover(appView));
}
static NonEmptyCfInstructionDesugaringCollection createForCfToCfNonDesugar(AppView<?> appView) {
@@ -160,10 +158,8 @@
new NonEmptyCfInstructionDesugaringCollection(appView, noAndroidApiLevelCompute());
desugaringCollection.desugarings.add(new InvokeSpecialToSelfDesugaring(appView));
desugaringCollection.desugarings.add(new InvokeToPrivateRewriter());
- if (appView.options().enableUnrepresentableInDexInstructionRemoval) {
- desugaringCollection.yieldingDesugarings.add(
- new UnrepresentableInDexInstructionRemover(appView));
- }
+ desugaringCollection.yieldingDesugarings.add(
+ new UnrepresentableInDexInstructionRemover(appView));
return desugaringCollection;
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/UnrepresentableInDexInstructionRemover.java b/src/main/java/com/android/tools/r8/ir/desugar/UnrepresentableInDexInstructionRemover.java
index 6d6dc0f..24cd2b5 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/UnrepresentableInDexInstructionRemover.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/UnrepresentableInDexInstructionRemover.java
@@ -28,6 +28,7 @@
import com.android.tools.r8.graph.DexCallSite;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.code.ValueType;
@@ -79,7 +80,7 @@
makeDiagnostic(context.getOrigin(), MethodPosition.create(context));
assert (diagnostic.getSupportedApiLevel() == -1 && supportedApiLevel == null)
|| (diagnostic.getSupportedApiLevel() == supportedApiLevel.getLevel());
- appView.reporter().error(diagnostic);
+ appView.reporter().warning(diagnostic);
}
}
@@ -114,8 +115,9 @@
builder.add(new CfStackInstruction(type.isWideType() ? Opcode.Pop2 : Opcode.Pop));
}
- static void pop(Iterable<DexType> types, Builder<CfInstruction> builder) {
- types.forEach(t -> pop(t, builder));
+ static void pop(DexProto proto, Builder<CfInstruction> builder) {
+ // Pop arguments in reverse order from the stack.
+ proto.getParameters().forEachReverse(t -> pop(t, builder));
}
static Builder<CfInstruction> pushReturnValue(DexType type, Builder<CfInstruction> builder) {
@@ -169,7 +171,7 @@
report(context);
Builder<CfInstruction> replacement = ImmutableList.builder();
DexCallSite callSite = invokeDynamic.getCallSite();
- pop(callSite.getMethodProto().getParameters(), replacement);
+ pop(callSite.getMethodProto(), replacement);
localStackAllocator.allocateLocalStack(1);
invokeThrowingStub(methodProcessingContext, eventConsumer, context, replacement);
pushReturnValue(callSite.getMethodProto().getReturnType(), replacement);
@@ -217,10 +219,10 @@
dexItemFactory) -> {
report(context);
Builder<CfInstruction> replacement = ImmutableList.builder();
+ pop(invoke.getMethod().getProto(), replacement);
if (!invoke.isInvokeStatic()) {
pop(dexItemFactory.objectType, replacement);
}
- pop(invoke.getMethod().getParameters(), replacement);
localStackAllocator.allocateLocalStack(1);
invokeThrowingStub(methodProcessingContext, eventConsumer, context, replacement);
pushReturnValue(invoke.getMethod().getReturnType(), replacement);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/ApiLevelRange.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/ApiLevelRange.java
index 66a980a..e13fb7c 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/ApiLevelRange.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/ApiLevelRange.java
@@ -50,7 +50,7 @@
}
ApiLevelRange that = (ApiLevelRange) o;
return apiLevelBelowOrEqual.equals(that.apiLevelBelowOrEqual)
- && apiLevelGreaterOrEqual.equals(that.apiLevelGreaterOrEqual);
+ && Objects.equals(apiLevelGreaterOrEqual, that.apiLevelGreaterOrEqual);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibrarySpecification.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibrarySpecification.java
index ba352b1..233374b 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibrarySpecification.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibrarySpecification.java
@@ -7,11 +7,8 @@
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Timing;
import java.io.IOException;
-import java.nio.file.Path;
-import java.util.Collection;
import java.util.List;
public interface DesugaredLibrarySpecification {
@@ -38,17 +35,4 @@
MachineDesugaredLibrarySpecification toMachineSpecification(DexApplication app, Timing timing)
throws IOException;
-
- MachineDesugaredLibrarySpecification toMachineSpecification(
- InternalOptions options,
- Collection<Path> library,
- Timing timing,
- Collection<Path> desugaredJDKLib)
- throws IOException;
-
- default MachineDesugaredLibrarySpecification toMachineSpecification(
- InternalOptions options, Collection<Path> library, Timing timing) throws IOException {
- assert !isLibraryCompilation();
- return toMachineSpecification(options, library, timing, null);
- }
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibrarySpecificationParser.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibrarySpecificationParser.java
index 83ee3ec..bc61d60 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibrarySpecificationParser.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibrarySpecificationParser.java
@@ -8,7 +8,9 @@
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser;
import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecificationParser;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecificationParser;
import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.synthesis.SyntheticNaming;
import com.android.tools.r8.utils.ExceptionDiagnostic;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.StringDiagnostic;
@@ -20,6 +22,7 @@
public static final String CONFIGURATION_FORMAT_VERSION_KEY = "configuration_format_version";
private static final int MIN_HUMAN_CONFIGURATION_FORMAT_VERSION = 100;
+ private static final int MIN_MACHINE_CONFIGURATION_FORMAT_VERSION = 200;
public static DesugaredLibrarySpecification parseDesugaredLibrarySpecification(
StringResource stringResource,
@@ -27,26 +30,8 @@
Reporter reporter,
boolean libraryCompilation,
int minAPILevel) {
- Origin origin = stringResource.getOrigin();
- assert origin != null;
- String jsonConfigString;
- JsonObject jsonConfig;
- try {
- jsonConfigString = stringResource.getString();
- JsonParser parser = new JsonParser();
- jsonConfig = parser.parse(jsonConfigString).getAsJsonObject();
- } catch (Exception e) {
- throw reporter.fatalError(new ExceptionDiagnostic(e, origin));
- }
-
- if (isHumanSpecification(jsonConfig, reporter, origin)) {
- return new HumanDesugaredLibrarySpecificationParser(
- dexItemFactory, reporter, libraryCompilation, minAPILevel)
- .parse(origin, jsonConfigString, jsonConfig);
- }
- return new LegacyDesugaredLibrarySpecificationParser(
- dexItemFactory, reporter, libraryCompilation, minAPILevel)
- .parse(origin, jsonConfigString, jsonConfig);
+ return parseDesugaredLibrarySpecificationforTesting(
+ stringResource, dexItemFactory, reporter, libraryCompilation, minAPILevel, flags -> {});
}
public static DesugaredLibrarySpecification parseDesugaredLibrarySpecificationforTesting(
@@ -67,18 +52,53 @@
} catch (Exception e) {
throw reporter.fatalError(new ExceptionDiagnostic(e, origin));
}
+ // Machine Specification is the shippable format released in Maven. D8/R8 has to be *very*
+ // backward compatible to any machine specification, and raise proper error messages for
+ // compatibility issues. The format is also exhaustive (Very limited pattern matching, if any).
+ // It can hardly be written by hand and is always generated.
+ if (isMachineSpecification(jsonConfig, reporter, origin)) {
+ return new MachineDesugaredLibrarySpecificationParser(
+ dexItemFactory, reporter, libraryCompilation, minAPILevel, new SyntheticNaming())
+ .parse(origin, jsonConfigString, jsonConfig);
+ }
+ // Human Specification is the easy to write format for developers and allows one to widely use
+ // pattern matching. This format is mainly used for development and to generate the machine
+ // specification. D8/R8 is *not* backward compatible with any previous version of human
+ // specification, which is therefore not suited to be shipped for external users. It can be
+ // shipped to internal users where we can easily update the D8/R8 compiler and the
+ // desugared library specification at the same time.
if (isHumanSpecification(jsonConfig, reporter, origin)) {
return new HumanDesugaredLibrarySpecificationParser(
dexItemFactory, reporter, libraryCompilation, minAPILevel)
.parse(origin, jsonConfigString, jsonConfig, topLevelFlagsAmender);
}
+ // Legacy specification is the legacy format, as was shipped desugared library JDK8.
+ // Hopefully the day will come where this format is no longer supported, and the other formats
+ // shall always be preferred+.
return new LegacyDesugaredLibrarySpecificationParser(
dexItemFactory, reporter, libraryCompilation, minAPILevel)
.parse(origin, jsonConfigString, jsonConfig, topLevelFlagsAmender);
}
+ public static boolean isMachineSpecification(
+ JsonObject jsonConfig, Reporter reporter, Origin origin) {
+ ensureConfigurationFormatVersion(jsonConfig, reporter, origin);
+
+ int formatVersion = jsonConfig.get(CONFIGURATION_FORMAT_VERSION_KEY).getAsInt();
+ return formatVersion >= MIN_MACHINE_CONFIGURATION_FORMAT_VERSION;
+ }
+
public static boolean isHumanSpecification(
JsonObject jsonConfig, Reporter reporter, Origin origin) {
+ ensureConfigurationFormatVersion(jsonConfig, reporter, origin);
+
+ int formatVersion = jsonConfig.get(CONFIGURATION_FORMAT_VERSION_KEY).getAsInt();
+ return formatVersion >= MIN_HUMAN_CONFIGURATION_FORMAT_VERSION
+ && formatVersion < MIN_MACHINE_CONFIGURATION_FORMAT_VERSION;
+ }
+
+ private static void ensureConfigurationFormatVersion(
+ JsonObject jsonConfig, Reporter reporter, Origin origin) {
if (!jsonConfig.has(CONFIGURATION_FORMAT_VERSION_KEY)) {
throw reporter.fatalError(
new StringDiagnostic(
@@ -87,8 +107,5 @@
+ "'",
origin));
}
-
- return jsonConfig.get(CONFIGURATION_FORMAT_VERSION_KEY).getAsInt()
- >= MIN_HUMAN_CONFIGURATION_FORMAT_VERSION;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecification.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecification.java
index e3696af..49cc5f6 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecification.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecification.java
@@ -8,11 +8,7 @@
import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
import com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion.HumanToMachineSpecificationConverter;
import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Timing;
-import java.io.IOException;
-import java.nio.file.Path;
-import java.util.Collection;
import java.util.List;
public class HumanDesugaredLibrarySpecification implements DesugaredLibrarySpecification {
@@ -95,15 +91,4 @@
DexApplication app, Timing timing) {
return new HumanToMachineSpecificationConverter(timing).convert(this, app);
}
-
- @Override
- public MachineDesugaredLibrarySpecification toMachineSpecification(
- InternalOptions options,
- Collection<Path> library,
- Timing timing,
- Collection<Path> desugaredJDKLib)
- throws IOException {
- return new HumanToMachineSpecificationConverter(timing)
- .convertForTesting(this, desugaredJDKLib, library, options);
- }
}
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 1aa5f62..5f8e35a 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
@@ -13,8 +13,8 @@
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.ir.desugar.desugaredlibrary.memberparser.HumanFieldParser;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.memberparser.HumanMethodParser;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.DescriptorUtils;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyDesugaredLibrarySpecification.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyDesugaredLibrarySpecification.java
index e669bfb..cd236c0 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyDesugaredLibrarySpecification.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyDesugaredLibrarySpecification.java
@@ -14,11 +14,8 @@
import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
import com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion.LegacyToHumanSpecificationConverter;
import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Timing;
import java.io.IOException;
-import java.nio.file.Path;
-import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -125,16 +122,4 @@
.convert(this, app)
.toMachineSpecification(app, timing);
}
-
- @Override
- public MachineDesugaredLibrarySpecification toMachineSpecification(
- InternalOptions options,
- Collection<Path> library,
- Timing timing,
- Collection<Path> desugaredJDKLib)
- throws IOException {
- return new LegacyToHumanSpecificationConverter(timing)
- .convertForTesting(this, desugaredJDKLib, library, options)
- .toMachineSpecification(options, library, timing, desugaredJDKLib);
- }
}
diff --git a/src/main/java/com/android/tools/r8/GenerateLintFiles.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/GenerateLintFiles.java
similarity index 95%
rename from src/main/java/com/android/tools/r8/GenerateLintFiles.java
rename to src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/GenerateLintFiles.java
index af340d4..3de3ac0 100644
--- a/src/main/java/com/android/tools/r8/GenerateLintFiles.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/GenerateLintFiles.java
@@ -1,9 +1,11 @@
-// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// 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;
+package com.android.tools.r8.ir.desugar.desugaredlibrary.lint;
+import com.android.tools.r8.ClassFileConsumer;
+import com.android.tools.r8.StringResource;
import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.cf.code.CfConstNull;
import com.android.tools.r8.cf.code.CfInstruction;
@@ -47,10 +49,12 @@
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
import java.io.File;
+import java.io.IOException;
import java.io.PrintStream;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -64,6 +68,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.ExecutorService;
import java.util.function.BiPredicate;
import java.util.function.Predicate;
import java.util.stream.StreamSupport;
@@ -104,9 +109,8 @@
DesugaredLibrarySpecification specification =
readDesugaredLibraryConfiguration(desugarConfigurationPath);
Path androidJarPath = getAndroidJarPath(specification.getRequiredCompilationApiLevel());
- this.desugaredLibrarySpecification =
- specification.toMachineSpecification(
- options, ImmutableList.of(androidJarPath), Timing.empty());
+ DexApplication app = createApp(androidJarPath, options);
+ this.desugaredLibrarySpecification = specification.toMachineSpecification(app, Timing.empty());
this.desugaredLibraryImplementation = desugarImplementationPath;
this.outputDirectory = outputDirectory;
@@ -250,8 +254,7 @@
Set<DexClass> classesWithAllMethodsSupported = Sets.newIdentityHashSet();
Map<DexClass, List<DexEncodedMethod>> supportedMethods = new LinkedHashMap<>();
for (DexProgramClass clazz : dexApplication.classes()) {
- if (clazz.accessFlags.isPublic()
- && desugaredLibrarySpecification.getRewriteType().containsKey(clazz.type)) {
+ if (clazz.accessFlags.isPublic() && desugaredLibrarySpecification.isSupported(clazz.type)) {
DexProgramClass implementationClass =
implementationApplication.programDefinitionFor(clazz.getType());
if (implementationClass == null) {
@@ -420,6 +423,19 @@
});
}
+ private static DexApplication createApp(Path androidLib, InternalOptions options)
+ throws IOException {
+ AndroidApp.Builder builder = AndroidApp.builder();
+ AndroidApp inputApp = builder.addLibraryFiles(androidLib).build();
+ ApplicationReader applicationReader = new ApplicationReader(inputApp, options, Timing.empty());
+ ExecutorService executorService = ThreadUtils.getExecutorService(options);
+ assert !options.ignoreJavaLibraryOverride;
+ options.ignoreJavaLibraryOverride = true;
+ DexApplication app = applicationReader.read(executorService);
+ options.ignoreJavaLibraryOverride = false;
+ return app;
+ }
+
private static class StringBuilderWithIndent {
String NL = System.lineSeparator();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/CustomConversionDescriptor.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/CustomConversionDescriptor.java
index e8c3ce3..6579f8f 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/CustomConversionDescriptor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/CustomConversionDescriptor.java
@@ -5,8 +5,9 @@
package com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification;
import com.android.tools.r8.graph.DexMethod;
+import java.util.Objects;
-public class CustomConversionDescriptor {
+public class CustomConversionDescriptor implements SpecificationDescriptor {
private final DexMethod to;
private final DexMethod from;
@@ -24,4 +25,27 @@
public DexMethod getFrom() {
return from;
}
+
+ @Override
+ public Object[] toJsonStruct(
+ MultiAPILevelMachineDesugaredLibrarySpecificationJsonExporter exporter) {
+ return exporter.exportCustomConversionDescriptor(this);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof CustomConversionDescriptor)) {
+ return false;
+ }
+ CustomConversionDescriptor that = (CustomConversionDescriptor) o;
+ return to == that.to && from == that.from;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(to, from);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/DerivedMethod.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/DerivedMethod.java
index c383dcd..dde1f48 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/DerivedMethod.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/DerivedMethod.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
+import java.util.Objects;
/**
* A derived method is: - if the holderKind is null, a normal dexMethod; - if the holderKind is
@@ -16,7 +17,7 @@
* context to generate the holder type. The method may however differ (for example the method name
* may be different).
*/
-public class DerivedMethod {
+public class DerivedMethod implements SpecificationDescriptor {
private final DexMethod method;
private final SyntheticKind holderKind;
@@ -49,4 +50,27 @@
public DexProto getProto() {
return method.getProto();
}
+
+ @Override
+ public Object[] toJsonStruct(
+ MultiAPILevelMachineDesugaredLibrarySpecificationJsonExporter exporter) {
+ return exporter.exportDerivedMethod(this);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof DerivedMethod)) {
+ return false;
+ }
+ DerivedMethod that = (DerivedMethod) o;
+ return method == that.method && Objects.equals(holderKind, that.holderKind);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(method, holderKind);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/EmulatedDispatchMethodDescriptor.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/EmulatedDispatchMethodDescriptor.java
index 6eb2424..9737d09 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/EmulatedDispatchMethodDescriptor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/EmulatedDispatchMethodDescriptor.java
@@ -6,8 +6,9 @@
import com.android.tools.r8.graph.DexType;
import java.util.LinkedHashMap;
+import java.util.Objects;
-public class EmulatedDispatchMethodDescriptor {
+public class EmulatedDispatchMethodDescriptor implements SpecificationDescriptor {
/**
* When resolving into the descriptor, if the resolution is used for a super-invoke or to generate
@@ -67,4 +68,30 @@
public LinkedHashMap<DexType, DerivedMethod> getDispatchCases() {
return dispatchCases;
}
+
+ @Override
+ public Object[] toJsonStruct(
+ MultiAPILevelMachineDesugaredLibrarySpecificationJsonExporter exporter) {
+ return exporter.exportEmulatedDispatchMethodDescriptor(this);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof EmulatedDispatchMethodDescriptor)) {
+ return false;
+ }
+ EmulatedDispatchMethodDescriptor that = (EmulatedDispatchMethodDescriptor) o;
+ return Objects.equals(interfaceMethod, that.interfaceMethod)
+ && Objects.equals(emulatedDispatchMethod, that.emulatedDispatchMethod)
+ && Objects.equals(forwardingMethod, that.forwardingMethod)
+ && Objects.equals(dispatchCases, that.dispatchCases);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(interfaceMethod, emulatedDispatchMethod, forwardingMethod, dispatchCases);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/EmulatedInterfaceDescriptor.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/EmulatedInterfaceDescriptor.java
index 0f9a520..fb96fe6 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/EmulatedInterfaceDescriptor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/EmulatedInterfaceDescriptor.java
@@ -7,8 +7,9 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import java.util.Map;
+import java.util.Objects;
-public class EmulatedInterfaceDescriptor {
+public class EmulatedInterfaceDescriptor implements SpecificationDescriptor {
private final DexType rewrittenType;
private final Map<DexMethod, EmulatedDispatchMethodDescriptor> emulatedMethods;
@@ -25,4 +26,27 @@
public Map<DexMethod, EmulatedDispatchMethodDescriptor> getEmulatedMethods() {
return emulatedMethods;
}
+
+ @Override
+ public Object[] toJsonStruct(
+ MultiAPILevelMachineDesugaredLibrarySpecificationJsonExporter exporter) {
+ return exporter.exportEmulatedInterfaceDescriptor(this);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof EmulatedInterfaceDescriptor)) {
+ return false;
+ }
+ EmulatedInterfaceDescriptor that = (EmulatedInterfaceDescriptor) o;
+ return rewrittenType == that.rewrittenType && emulatedMethods.equals(that.emulatedMethods);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(rewrittenType, emulatedMethods);
+ }
}
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 4aa99b6..f4a6d2a 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,20 +4,24 @@
package com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification;
+import com.android.tools.r8.graph.DexApplication;
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.ir.desugar.desugaredlibrary.DesugaredLibrarySpecification;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.SemanticVersion;
+import com.android.tools.r8.utils.Timing;
+import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
-public class MachineDesugaredLibrarySpecification {
+public class MachineDesugaredLibrarySpecification implements DesugaredLibrarySpecification {
private final boolean libraryCompilation;
private final MachineTopLevelFlags topLevelFlags;
@@ -52,18 +56,30 @@
this.rewritingFlags = rewritingFlags;
}
+ @Override
public boolean isEmpty() {
return rewritingFlags.isEmpty();
}
+ @Override
public boolean isLibraryCompilation() {
return libraryCompilation;
}
- public AndroidApiLevel getRequiredCompilationAPILevel() {
- return topLevelFlags.getRequiredCompilationAPILevel();
+ public MachineTopLevelFlags getTopLevelFlags() {
+ return topLevelFlags;
}
+ public MachineRewritingFlags getRewritingFlags() {
+ return rewritingFlags;
+ }
+
+ @Override
+ public AndroidApiLevel getRequiredCompilationApiLevel() {
+ return topLevelFlags.getRequiredCompilationApiLevel();
+ }
+
+ @Override
public String getSynthesizedLibraryClassesPackagePrefix() {
return topLevelFlags.getSynthesizedLibraryClassesPackagePrefix();
}
@@ -72,6 +88,7 @@
return topLevelFlags.getIdentifier();
}
+ @Override
public String getJsonSource() {
return topLevelFlags.getJsonSource();
}
@@ -80,6 +97,7 @@
return topLevelFlags.supportAllCallbacksFromLibrary();
}
+ @Override
public List<String> getExtraKeepRules() {
return topLevelFlags.getExtraKeepRules();
}
@@ -204,8 +222,10 @@
return false;
}
- public AndroidApiLevel getRequiredCompilationApiLevel() {
- return topLevelFlags.getRequiredCompilationAPILevel();
+ @Override
+ public MachineDesugaredLibrarySpecification toMachineSpecification(
+ DexApplication app, Timing timing) throws IOException {
+ return this;
}
public boolean requiresTypeRewriting() {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecificationParser.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecificationParser.java
new file mode 100644
index 0000000..031ca29
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecificationParser.java
@@ -0,0 +1,479 @@
+// 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.machinespecification;
+
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecificationParser.CONFIGURATION_FORMAT_VERSION_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.AMEND_LIBRARY_FIELD_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.AMEND_LIBRARY_METHOD_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.API_GENERIC_TYPES_CONVERSION_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.API_LEVEL_BELOW_OR_EQUAL_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.API_LEVEL_GREATER_OR_EQUAL_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.COMMON_FLAGS_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.COVARIANT_RETARGET_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.CUSTOM_CONVERSION_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.DONT_RETARGET_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.EMULATED_INTERFACE_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.EMULATED_VIRTUAL_RETARGET_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.EMULATED_VIRTUAL_RETARGET_THROUGH_EMULATED_INTERFACE_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.IDENTIFIER_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.LEGACY_BACKPORT_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.LIBRARY_FLAGS_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.MAINTAIN_TYPE_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.NON_EMULATED_VIRTUAL_RETARGET_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.PACKAGE_MAP_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.PROGRAM_FLAGS_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.REQUIRED_COMPILATION_API_LEVEL_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.REWRITE_DERIVED_TYPE_ONLY_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.REWRITE_TYPE_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.SHRINKER_CONFIG_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.STATIC_FIELD_RETARGET_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.STATIC_RETARGET_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.SUPPORT_ALL_CALLBACKS_FROM_LIBRARY_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.SYNTHESIZED_LIBRARY_CLASSES_PACKAGE_PREFIX_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.WRAPPER_KEY;
+
+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.memberparser.MachineFieldParser;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.memberparser.MachineMethodParser;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.synthesis.SyntheticNaming;
+import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.ExceptionDiagnostic;
+import com.android.tools.r8.utils.Reporter;
+import com.android.tools.r8.utils.StringDiagnostic;
+import com.google.common.collect.ImmutableBiMap;
+import com.google.common.collect.ImmutableList;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import java.util.ArrayList;
+import java.util.IdentityHashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+public class MachineDesugaredLibrarySpecificationParser {
+
+ private static final int MIN_SUPPORTED_VERSION = 200;
+ private static final int MAX_SUPPORTED_VERSION = 200;
+
+ private static final String ERROR_MESSAGE_PREFIX = "Invalid desugared library specification: ";
+
+ private final DexItemFactory dexItemFactory;
+ private final MachineMethodParser methodParser;
+ private final MachineFieldParser fieldParser;
+ private final Reporter reporter;
+ private final boolean libraryCompilation;
+ private final int minAPILevel;
+ private final SyntheticNaming syntheticNaming;
+
+ private Origin origin;
+ private JsonObject jsonConfig;
+ private Map<String, String> packageMap;
+
+ public MachineDesugaredLibrarySpecificationParser(
+ DexItemFactory dexItemFactory,
+ Reporter reporter,
+ boolean libraryCompilation,
+ int minAPILevel,
+ SyntheticNaming syntheticNaming) {
+ this.dexItemFactory = dexItemFactory;
+ this.methodParser = new MachineMethodParser(dexItemFactory, this::stringDescriptorToDexType);
+ this.fieldParser = new MachineFieldParser(dexItemFactory, this::stringDescriptorToDexType);
+ this.reporter = reporter;
+ this.minAPILevel = minAPILevel;
+ this.syntheticNaming = syntheticNaming;
+ this.libraryCompilation = libraryCompilation;
+ }
+
+ public DexItemFactory dexItemFactory() {
+ return dexItemFactory;
+ }
+
+ public Reporter reporter() {
+ return reporter;
+ }
+
+ public JsonObject getJsonConfig() {
+ return jsonConfig;
+ }
+
+ public Origin getOrigin() {
+ assert origin != null;
+ return origin;
+ }
+
+ JsonElement required(JsonObject json, String key) {
+ if (!json.has(key)) {
+ throw reporter.fatalError(
+ new StringDiagnostic(
+ "Invalid desugared library configuration. Expected required key '" + key + "'",
+ origin));
+ }
+ return json.get(key);
+ }
+
+ public MachineDesugaredLibrarySpecification parse(StringResource stringResource) {
+ String jsonConfigString = parseJson(stringResource);
+ return parse(origin, jsonConfigString, jsonConfig);
+ }
+
+ public MachineDesugaredLibrarySpecification parse(
+ Origin origin, String jsonConfigString, JsonObject jsonConfig) {
+ this.origin = origin;
+ this.jsonConfig = jsonConfig;
+ int machineVersion = required(jsonConfig, CONFIGURATION_FORMAT_VERSION_KEY).getAsInt();
+ if (machineVersion < MIN_SUPPORTED_VERSION || machineVersion > MAX_SUPPORTED_VERSION) {
+ throw reporter.fatalError(
+ new StringDiagnostic(
+ "Unsupported machine version number "
+ + machineVersion
+ + " not in ["
+ + MIN_SUPPORTED_VERSION
+ + ","
+ + MAX_SUPPORTED_VERSION
+ + "]",
+ origin));
+ }
+ MachineTopLevelFlags topLevelFlags = parseTopLevelFlags(jsonConfigString);
+ parsePackageMap();
+ MachineRewritingFlags rewritingFlags = parseRewritingFlags();
+ MachineDesugaredLibrarySpecification config =
+ new MachineDesugaredLibrarySpecification(libraryCompilation, topLevelFlags, rewritingFlags);
+ this.origin = null;
+ return config;
+ }
+
+ private void parsePackageMap() {
+ JsonObject packageMapJson = required(jsonConfig, PACKAGE_MAP_KEY).getAsJsonObject();
+ ImmutableBiMap.Builder<String, String> builder = ImmutableBiMap.builder();
+ packageMapJson.entrySet().forEach((e) -> builder.put(e.getValue().getAsString(), e.getKey()));
+ packageMap = builder.build();
+ }
+
+ String parseJson(StringResource stringResource) {
+ setOrigin(stringResource);
+ String jsonConfigString;
+ try {
+ jsonConfigString = stringResource.getString();
+ JsonParser parser = new JsonParser();
+ jsonConfig = parser.parse(jsonConfigString).getAsJsonObject();
+ } catch (Exception e) {
+ throw reporter.fatalError(new ExceptionDiagnostic(e, origin));
+ }
+ return jsonConfigString;
+ }
+
+ void setOrigin(StringResource stringResource) {
+ origin = stringResource.getOrigin();
+ assert origin != null;
+ }
+
+ private MachineRewritingFlags parseRewritingFlags() {
+ MachineRewritingFlags.Builder builder = MachineRewritingFlags.builder();
+ JsonElement commonFlags = required(jsonConfig, COMMON_FLAGS_KEY);
+ JsonElement libraryFlags = required(jsonConfig, LIBRARY_FLAGS_KEY);
+ JsonElement programFlags = required(jsonConfig, PROGRAM_FLAGS_KEY);
+ parseFlagsList(commonFlags.getAsJsonArray(), builder);
+ parseFlagsList(
+ libraryCompilation ? libraryFlags.getAsJsonArray() : programFlags.getAsJsonArray(),
+ builder);
+ return builder.build();
+ }
+
+ MachineTopLevelFlags parseTopLevelFlags(String jsonConfigString) {
+ String identifier = required(jsonConfig, IDENTIFIER_KEY).getAsString();
+ String prefix =
+ required(jsonConfig, SYNTHESIZED_LIBRARY_CLASSES_PACKAGE_PREFIX_KEY).getAsString();
+ int required_compilation_api_level =
+ required(jsonConfig, REQUIRED_COMPILATION_API_LEVEL_KEY).getAsInt();
+ String keepRules = required(jsonConfig, SHRINKER_CONFIG_KEY).getAsString();
+ boolean supportAllCallbacksFromLibrary =
+ jsonConfig.get(SUPPORT_ALL_CALLBACKS_FROM_LIBRARY_KEY).getAsBoolean();
+
+ return new MachineTopLevelFlags(
+ AndroidApiLevel.getAndroidApiLevel(required_compilation_api_level),
+ prefix,
+ identifier,
+ jsonConfigString,
+ supportAllCallbacksFromLibrary,
+ ImmutableList.of(keepRules));
+ }
+
+ private void parseFlagsList(JsonArray jsonFlags, MachineRewritingFlags.Builder builder) {
+ for (JsonElement jsonFlagSet : jsonFlags) {
+ JsonObject flag = jsonFlagSet.getAsJsonObject();
+ int api_level_below_or_equal = required(flag, API_LEVEL_BELOW_OR_EQUAL_KEY).getAsInt();
+ if (minAPILevel <= api_level_below_or_equal) {
+ if (flag.has(API_LEVEL_GREATER_OR_EQUAL_KEY)) {
+ if (minAPILevel >= flag.get(API_LEVEL_GREATER_OR_EQUAL_KEY).getAsInt()) {
+ parseFlags(flag, builder);
+ }
+ } else {
+ parseFlags(flag, builder);
+ }
+ }
+ }
+ }
+
+ void parseFlags(JsonObject jsonFlagSet, MachineRewritingFlags.Builder builder) {
+ if (jsonFlagSet.has(REWRITE_TYPE_KEY)) {
+ for (Map.Entry<String, JsonElement> rewritePrefix :
+ jsonFlagSet.get(REWRITE_TYPE_KEY).getAsJsonObject().entrySet()) {
+ builder.rewriteType(
+ stringDescriptorToDexType(rewritePrefix.getKey()),
+ stringDescriptorToDexType(rewritePrefix.getValue().getAsString()));
+ }
+ }
+ if (jsonFlagSet.has(MAINTAIN_TYPE_KEY)) {
+ for (JsonElement maintainPrefix : jsonFlagSet.get(MAINTAIN_TYPE_KEY).getAsJsonArray()) {
+ builder.maintainType(stringDescriptorToDexType(maintainPrefix.getAsString()));
+ }
+ }
+ if (jsonFlagSet.has(REWRITE_DERIVED_TYPE_ONLY_KEY)) {
+ for (Map.Entry<String, JsonElement> rewriteDerivedTypeOnly :
+ jsonFlagSet.get(REWRITE_DERIVED_TYPE_ONLY_KEY).getAsJsonObject().entrySet()) {
+ builder.rewriteDerivedTypeOnly(
+ stringDescriptorToDexType(rewriteDerivedTypeOnly.getKey()),
+ stringDescriptorToDexType(rewriteDerivedTypeOnly.getValue().getAsString()));
+ }
+ }
+ if (jsonFlagSet.has(STATIC_FIELD_RETARGET_KEY)) {
+ for (Map.Entry<String, JsonElement> staticFieldRetarget :
+ jsonFlagSet.get(STATIC_FIELD_RETARGET_KEY).getAsJsonObject().entrySet()) {
+ builder.putStaticFieldRetarget(
+ parseField(staticFieldRetarget.getKey()),
+ parseField(staticFieldRetarget.getValue().getAsString()));
+ }
+ }
+ if (jsonFlagSet.has(COVARIANT_RETARGET_KEY)) {
+ for (Map.Entry<String, JsonElement> covariantRetarget :
+ jsonFlagSet.get(COVARIANT_RETARGET_KEY).getAsJsonObject().entrySet()) {
+ builder.putCovariantRetarget(
+ parseMethod(covariantRetarget.getKey()),
+ parseMethod(covariantRetarget.getValue().getAsString()));
+ }
+ }
+ if (jsonFlagSet.has(STATIC_RETARGET_KEY)) {
+ for (Map.Entry<String, JsonElement> staticRetarget :
+ jsonFlagSet.get(STATIC_RETARGET_KEY).getAsJsonObject().entrySet()) {
+ builder.putStaticRetarget(
+ parseMethod(staticRetarget.getKey()),
+ parseMethod(staticRetarget.getValue().getAsString()));
+ }
+ }
+ if (jsonFlagSet.has(NON_EMULATED_VIRTUAL_RETARGET_KEY)) {
+ for (Map.Entry<String, JsonElement> virtualRetarget :
+ jsonFlagSet.get(NON_EMULATED_VIRTUAL_RETARGET_KEY).getAsJsonObject().entrySet()) {
+ builder.putNonEmulatedVirtualRetarget(
+ parseMethod(virtualRetarget.getKey()),
+ parseMethod(virtualRetarget.getValue().getAsString()));
+ }
+ }
+ if (jsonFlagSet.has(EMULATED_VIRTUAL_RETARGET_KEY)) {
+ for (Map.Entry<String, JsonElement> virtualRetarget :
+ jsonFlagSet.get(EMULATED_VIRTUAL_RETARGET_KEY).getAsJsonObject().entrySet()) {
+ builder.putEmulatedVirtualRetarget(
+ parseMethod(virtualRetarget.getKey()),
+ parseEmulatedDispatchDescriptor(virtualRetarget.getValue().getAsJsonArray()));
+ }
+ }
+ if (jsonFlagSet.has(EMULATED_VIRTUAL_RETARGET_THROUGH_EMULATED_INTERFACE_KEY)) {
+ for (Map.Entry<String, JsonElement> virtualRetarget :
+ jsonFlagSet
+ .get(EMULATED_VIRTUAL_RETARGET_THROUGH_EMULATED_INTERFACE_KEY)
+ .getAsJsonObject()
+ .entrySet()) {
+ builder.putEmulatedVirtualRetargetThroughEmulatedInterface(
+ parseMethod(virtualRetarget.getKey()),
+ parseMethod(virtualRetarget.getValue().getAsString()));
+ }
+ }
+ if (jsonFlagSet.has(API_GENERIC_TYPES_CONVERSION_KEY)) {
+ for (Map.Entry<String, JsonElement> apiGenericType :
+ jsonFlagSet.get(API_GENERIC_TYPES_CONVERSION_KEY).getAsJsonObject().entrySet()) {
+ builder.addApiGenericTypesConversion(
+ parseMethod(apiGenericType.getKey()),
+ parseMethodArray(apiGenericType.getValue().getAsJsonArray()));
+ }
+ }
+ if (jsonFlagSet.has(EMULATED_INTERFACE_KEY)) {
+ for (Map.Entry<String, JsonElement> emulatedInterface :
+ jsonFlagSet.get(EMULATED_INTERFACE_KEY).getAsJsonObject().entrySet()) {
+ builder.putEmulatedInterface(
+ stringDescriptorToDexType(emulatedInterface.getKey()),
+ parseEmulatedInterfaceDescriptor(emulatedInterface.getValue().getAsJsonArray()));
+ }
+ }
+ if (jsonFlagSet.has(WRAPPER_KEY)) {
+ for (Map.Entry<String, JsonElement> wrapper :
+ jsonFlagSet.get(WRAPPER_KEY).getAsJsonObject().entrySet()) {
+ builder.addWrapper(
+ stringDescriptorToDexType(wrapper.getKey()),
+ parseWrapperDescriptor(wrapper.getValue().getAsJsonArray()));
+ }
+ }
+ if (jsonFlagSet.has(LEGACY_BACKPORT_KEY)) {
+ for (Map.Entry<String, JsonElement> legacyBackport :
+ jsonFlagSet.get(LEGACY_BACKPORT_KEY).getAsJsonObject().entrySet()) {
+ builder.putLegacyBackport(
+ stringDescriptorToDexType(legacyBackport.getKey()),
+ stringDescriptorToDexType(legacyBackport.getValue().getAsString()));
+ }
+ }
+ if (jsonFlagSet.has(DONT_RETARGET_KEY)) {
+ for (JsonElement dontRetarget : jsonFlagSet.get(DONT_RETARGET_KEY).getAsJsonArray()) {
+ builder.addDontRetarget(stringDescriptorToDexType(dontRetarget.getAsString()));
+ }
+ }
+ if (jsonFlagSet.has(CUSTOM_CONVERSION_KEY)) {
+ for (Map.Entry<String, JsonElement> customConversion :
+ jsonFlagSet.get(CUSTOM_CONVERSION_KEY).getAsJsonObject().entrySet()) {
+ builder.putCustomConversion(
+ stringDescriptorToDexType(customConversion.getKey()),
+ parseCustomConversionDescriptor(customConversion.getValue().getAsJsonArray()));
+ }
+ }
+ if (jsonFlagSet.has(AMEND_LIBRARY_METHOD_KEY)) {
+ JsonArray amendLibraryMember = jsonFlagSet.get(AMEND_LIBRARY_METHOD_KEY).getAsJsonArray();
+ for (JsonElement amend : amendLibraryMember) {
+ methodParser.parseMethod(amend.getAsString());
+ 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 CustomConversionDescriptor parseCustomConversionDescriptor(JsonArray jsonArray) {
+ return new CustomConversionDescriptor(
+ parseMethod(jsonArray.get(0).getAsString()), parseMethod(jsonArray.get(1).getAsString()));
+ }
+
+ private WrapperDescriptor parseWrapperDescriptor(JsonArray jsonArray) {
+ List<DexMethod> methods = parseMethodList(jsonArray.get(0).getAsJsonArray());
+ boolean nonPublicAccess = jsonArray.get(1).getAsBoolean();
+ List<DexType> subwrappers = parseTypeList(jsonArray.get(2).getAsJsonArray());
+ return new WrapperDescriptor(methods, subwrappers, nonPublicAccess);
+ }
+
+ private void require(JsonArray jsonArray, int size, String elementString) {
+ if (jsonArray.size() != size) {
+ throw reporter.fatalError(
+ ERROR_MESSAGE_PREFIX + elementString + "(Json array of size " + jsonArray.size() + ")");
+ }
+ }
+
+ private EmulatedInterfaceDescriptor parseEmulatedInterfaceDescriptor(JsonArray jsonArray) {
+ require(jsonArray, 2, "emulated interface descriptor");
+ DexType rewrittenType = stringDescriptorToDexType(jsonArray.get(0).getAsString());
+ Map<DexMethod, EmulatedDispatchMethodDescriptor> methods =
+ parseEmulatedInterfaceMap(jsonArray.get(1).getAsJsonObject());
+ return new EmulatedInterfaceDescriptor(rewrittenType, methods);
+ }
+
+ private Map<DexMethod, EmulatedDispatchMethodDescriptor> parseEmulatedInterfaceMap(
+ JsonObject jsonObject) {
+ Map<DexMethod, EmulatedDispatchMethodDescriptor> map = new IdentityHashMap<>();
+ for (Map.Entry<String, JsonElement> entry : jsonObject.entrySet()) {
+ map.put(
+ parseMethod(entry.getKey()),
+ parseEmulatedDispatchDescriptor(entry.getValue().getAsJsonArray()));
+ }
+ return map;
+ }
+
+ private LinkedHashMap<DexType, DerivedMethod> parseEmulatedDispatchMap(JsonObject jsonObject) {
+ LinkedHashMap<DexType, DerivedMethod> map = new LinkedHashMap<>();
+ for (Map.Entry<String, JsonElement> entry : jsonObject.entrySet()) {
+ map.put(
+ stringDescriptorToDexType(entry.getKey()),
+ parseDerivedMethod(entry.getValue().getAsJsonArray()));
+ }
+ return map;
+ }
+
+ private EmulatedDispatchMethodDescriptor parseEmulatedDispatchDescriptor(JsonArray jsonArray) {
+ require(jsonArray, 4, "emulated dispatch descriptor");
+ DerivedMethod interfaceMethod = parseDerivedMethod(jsonArray.get(0).getAsJsonArray());
+ DerivedMethod emulatedDispatchMethod = parseDerivedMethod(jsonArray.get(1).getAsJsonArray());
+ DerivedMethod forwardingMethod = parseDerivedMethod(jsonArray.get(2).getAsJsonArray());
+ LinkedHashMap<DexType, DerivedMethod> dispatchCases =
+ parseEmulatedDispatchMap(jsonArray.get(3).getAsJsonObject());
+ return new EmulatedDispatchMethodDescriptor(
+ interfaceMethod, emulatedDispatchMethod, forwardingMethod, dispatchCases);
+ }
+
+ private DerivedMethod parseDerivedMethod(JsonArray jsonArray) {
+ require(jsonArray, 2, "derived method");
+ DexMethod dexMethod = parseMethod(jsonArray.get(0).getAsString());
+ int kind = jsonArray.get(1).getAsInt();
+ if (kind == -1) {
+ return new DerivedMethod(dexMethod);
+ }
+ SyntheticKind syntheticKind = syntheticNaming.fromId(kind);
+ return new DerivedMethod(dexMethod, syntheticKind);
+ }
+
+ private List<DexMethod> parseMethodList(JsonArray array) {
+ List<DexMethod> methods = new ArrayList<>();
+ for (JsonElement method : array) {
+ methods.add(parseMethod(method.getAsString()));
+ }
+ return methods;
+ }
+
+ private List<DexType> parseTypeList(JsonArray array) {
+ List<DexType> types = new ArrayList<>();
+ for (JsonElement typeString : array) {
+ types.add(stringDescriptorToDexType(typeString.getAsString()));
+ }
+ return types;
+ }
+
+ private DexMethod[] parseMethodArray(JsonArray array) {
+ DexMethod[] dexMethods = new DexMethod[array.size()];
+ for (int i = 0; i < array.size(); i++) {
+ String str = array.get(i).getAsString();
+ dexMethods[i] = str.isEmpty() ? null : parseMethod(str);
+ }
+ return dexMethods;
+ }
+
+ private DexMethod parseMethod(String signature) {
+ methodParser.parseMethod(signature);
+ return methodParser.getMethod();
+ }
+
+ private DexField parseField(String signature) {
+ fieldParser.parseField(signature);
+ return fieldParser.getField();
+ }
+
+ public DexType stringDescriptorToDexType(String stringClass) {
+ if (stringClass.charAt(1) != '$') {
+ return dexItemFactory.createType(DescriptorUtils.javaTypeToDescriptor(stringClass));
+ }
+ String type = stringClass.substring(2);
+ String prefix = packageMap.get(stringClass.substring(0, 2));
+ if (prefix == null) {
+ throw reporter.fatalError(
+ ERROR_MESSAGE_PREFIX + "Missing package mapping for " + stringClass.substring(0, 2));
+ }
+ return dexItemFactory.createType(DescriptorUtils.javaTypeToDescriptor(prefix + "." + type));
+ }
+}
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 5ad734b..523042f 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
@@ -159,7 +159,7 @@
return emulatedInterfaces;
}
- public Map<DexType, WrapperDescriptor> getWrappers() {
+ public LinkedHashMap<DexType, WrapperDescriptor> getWrappers() {
return wrappers;
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineSpecificationJsonPool.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineSpecificationJsonPool.java
new file mode 100644
index 0000000..105a18b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineSpecificationJsonPool.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.machinespecification;
+
+public class MachineSpecificationJsonPool {
+
+ static final String REQUIRED_COMPILATION_API_LEVEL_KEY = "required_compilation_api_level";
+ static final String SYNTHESIZED_LIBRARY_CLASSES_PACKAGE_PREFIX_KEY =
+ "synthesized_library_classes_package_prefix";
+ static final String IDENTIFIER_KEY = "identifier";
+ static final String SUPPORT_ALL_CALLBACKS_FROM_LIBRARY_KEY = "support_all_callbacks_from_library";
+ static final String SHRINKER_CONFIG_KEY = "shrinker_config";
+
+ static final String COMMON_FLAGS_KEY = "common_flags";
+ static final String LIBRARY_FLAGS_KEY = "library_flags";
+ static final String PROGRAM_FLAGS_KEY = "program_flags";
+
+ static final String API_LEVEL_BELOW_OR_EQUAL_KEY = "api_level_below_or_equal";
+ static final String API_LEVEL_GREATER_OR_EQUAL_KEY = "api_level_greater_or_equal";
+
+ static final String REWRITE_TYPE_KEY = "rewrite_type";
+ static final String MAINTAIN_TYPE_KEY = "maintain_type";
+ static final String REWRITE_DERIVED_TYPE_ONLY_KEY = "rewrite_derived_type_only";
+ static final String STATIC_FIELD_RETARGET_KEY = "static_field_retarget";
+ static final String COVARIANT_RETARGET_KEY = "covariant_retarget";
+ static final String STATIC_RETARGET_KEY = "static_retarget";
+ static final String NON_EMULATED_VIRTUAL_RETARGET_KEY = "non_emulated_virtual_retarget";
+ static final String EMULATED_VIRTUAL_RETARGET_KEY = "emulated_virtual_retarget";
+ static final String EMULATED_VIRTUAL_RETARGET_THROUGH_EMULATED_INTERFACE_KEY =
+ "emulated_virtual_retarget_through_emulated_interface";
+ static final String API_GENERIC_TYPES_CONVERSION_KEY = "api_generic_types_conversion";
+ static final String EMULATED_INTERFACE_KEY = "emulated_interface";
+ static final String WRAPPER_KEY = "wrapper";
+ static final String LEGACY_BACKPORT_KEY = "legacy_backport";
+ static final String DONT_RETARGET_KEY = "dont_retarget";
+ static final String CUSTOM_CONVERSION_KEY = "custom_conversion";
+ static final String AMEND_LIBRARY_METHOD_KEY = "amend_library_method";
+ static final String AMEND_LIBRARY_FIELD_KEY = "amend_library_field";
+
+ static final String PACKAGE_MAP_KEY = "package_map";
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineTopLevelFlags.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineTopLevelFlags.java
index 0c6a88a..c5c1b4b 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineTopLevelFlags.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineTopLevelFlags.java
@@ -44,7 +44,7 @@
this.extraKeepRules = extraKeepRules;
}
- public AndroidApiLevel getRequiredCompilationAPILevel() {
+ public AndroidApiLevel getRequiredCompilationApiLevel() {
return requiredCompilationAPILevel;
}
@@ -67,4 +67,8 @@
public List<String> getExtraKeepRules() {
return extraKeepRules;
}
+
+ public String getExtraKeepRulesConcatenated() {
+ return String.join("\n", extraKeepRules);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MultiAPILevelMachineDesugaredLibrarySpecification.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MultiAPILevelMachineDesugaredLibrarySpecification.java
new file mode 100644
index 0000000..04ddb16
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MultiAPILevelMachineDesugaredLibrarySpecification.java
@@ -0,0 +1,51 @@
+// 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.machinespecification;
+
+import com.android.tools.r8.ir.desugar.desugaredlibrary.ApiLevelRange;
+import com.android.tools.r8.origin.Origin;
+import java.util.Map;
+
+public class MultiAPILevelMachineDesugaredLibrarySpecification {
+
+ private final Origin origin;
+ private final MachineTopLevelFlags topLevelFlags;
+ private final Map<ApiLevelRange, MachineRewritingFlags> commonFlags;
+ private final Map<ApiLevelRange, MachineRewritingFlags> libraryFlags;
+ private final Map<ApiLevelRange, MachineRewritingFlags> programFlags;
+
+ public MultiAPILevelMachineDesugaredLibrarySpecification(
+ Origin origin,
+ MachineTopLevelFlags topLevelFlags,
+ Map<ApiLevelRange, MachineRewritingFlags> commonFlags,
+ Map<ApiLevelRange, MachineRewritingFlags> libraryFlags,
+ Map<ApiLevelRange, MachineRewritingFlags> programFlags) {
+ this.origin = origin;
+ this.topLevelFlags = topLevelFlags;
+ this.commonFlags = commonFlags;
+ this.libraryFlags = libraryFlags;
+ this.programFlags = programFlags;
+ }
+
+ public Origin getOrigin() {
+ return origin;
+ }
+
+ public MachineTopLevelFlags getTopLevelFlags() {
+ return topLevelFlags;
+ }
+
+ public Map<ApiLevelRange, MachineRewritingFlags> getCommonFlags() {
+ return commonFlags;
+ }
+
+ public Map<ApiLevelRange, MachineRewritingFlags> getLibraryFlags() {
+ return libraryFlags;
+ }
+
+ public Map<ApiLevelRange, MachineRewritingFlags> getProgramFlags() {
+ return programFlags;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MultiAPILevelMachineDesugaredLibrarySpecificationJsonExporter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MultiAPILevelMachineDesugaredLibrarySpecificationJsonExporter.java
new file mode 100644
index 0000000..f5d81fa
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MultiAPILevelMachineDesugaredLibrarySpecificationJsonExporter.java
@@ -0,0 +1,350 @@
+// 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.machinespecification;
+
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecificationParser.CONFIGURATION_FORMAT_VERSION_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.AMEND_LIBRARY_FIELD_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.AMEND_LIBRARY_METHOD_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.API_GENERIC_TYPES_CONVERSION_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.API_LEVEL_BELOW_OR_EQUAL_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.API_LEVEL_GREATER_OR_EQUAL_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.COMMON_FLAGS_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.COVARIANT_RETARGET_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.CUSTOM_CONVERSION_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.DONT_RETARGET_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.EMULATED_INTERFACE_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.EMULATED_VIRTUAL_RETARGET_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.EMULATED_VIRTUAL_RETARGET_THROUGH_EMULATED_INTERFACE_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.IDENTIFIER_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.LEGACY_BACKPORT_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.LIBRARY_FLAGS_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.MAINTAIN_TYPE_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.NON_EMULATED_VIRTUAL_RETARGET_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.PACKAGE_MAP_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.PROGRAM_FLAGS_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.REQUIRED_COMPILATION_API_LEVEL_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.REWRITE_DERIVED_TYPE_ONLY_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.REWRITE_TYPE_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.SHRINKER_CONFIG_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.STATIC_FIELD_RETARGET_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.STATIC_RETARGET_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.SUPPORT_ALL_CALLBACKS_FROM_LIBRARY_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.SYNTHESIZED_LIBRARY_CLASSES_PACKAGE_PREFIX_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.WRAPPER_KEY;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.StringConsumer;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AccessFlags;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItem;
+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.ApiLevelRange;
+import com.google.gson.Gson;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+public class MultiAPILevelMachineDesugaredLibrarySpecificationJsonExporter {
+
+ private static final int MACHINE_VERSION_NUMBER = 200;
+
+ private final DexItemFactory factory;
+ private final Map<String, String> packageMap = new TreeMap<>();
+ private static final String chars =
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789æÆøØ";
+ private int next = 0;
+
+ public MultiAPILevelMachineDesugaredLibrarySpecificationJsonExporter(DexItemFactory factory) {
+ this.factory = factory;
+ }
+
+ public static void export(
+ MultiAPILevelMachineDesugaredLibrarySpecification specification,
+ StringConsumer output,
+ DexItemFactory factory) {
+ new MultiAPILevelMachineDesugaredLibrarySpecificationJsonExporter(factory)
+ .internalExport(specification, output);
+ }
+
+ private void internalExport(
+ MultiAPILevelMachineDesugaredLibrarySpecification machineSpec, StringConsumer output) {
+ HashMap<String, Object> toJson = new LinkedHashMap<>();
+
+ exportTopLevelFlags(machineSpec.getTopLevelFlags(), toJson);
+ toJson.put(CONFIGURATION_FORMAT_VERSION_KEY, MACHINE_VERSION_NUMBER);
+
+ toJson.put(COMMON_FLAGS_KEY, rewritingFlagsToString(machineSpec.getCommonFlags()));
+ toJson.put(PROGRAM_FLAGS_KEY, rewritingFlagsToString(machineSpec.getProgramFlags()));
+ toJson.put(LIBRARY_FLAGS_KEY, rewritingFlagsToString(machineSpec.getLibraryFlags()));
+
+ toJson.put(PACKAGE_MAP_KEY, packageMap);
+
+ Gson gson = new Gson();
+ String export = gson.toJson(toJson);
+ output.accept(export, new DiagnosticsHandler() {});
+ }
+
+ private void exportTopLevelFlags(MachineTopLevelFlags topLevelFlags, Map<String, Object> toJson) {
+ toJson.put(IDENTIFIER_KEY, topLevelFlags.getIdentifier());
+ toJson.put(
+ REQUIRED_COMPILATION_API_LEVEL_KEY,
+ topLevelFlags.getRequiredCompilationApiLevel().getLevel());
+ toJson.put(
+ SYNTHESIZED_LIBRARY_CLASSES_PACKAGE_PREFIX_KEY,
+ topLevelFlags.getSynthesizedLibraryClassesPackagePrefix());
+ toJson.put(
+ SUPPORT_ALL_CALLBACKS_FROM_LIBRARY_KEY, topLevelFlags.supportAllCallbacksFromLibrary());
+ toJson.put(SHRINKER_CONFIG_KEY, topLevelFlags.getExtraKeepRulesConcatenated());
+ }
+
+ private List<Object> rewritingFlagsToString(
+ Map<ApiLevelRange, MachineRewritingFlags> rewritingFlagsMap) {
+ ArrayList<Object> list = new ArrayList<>();
+ ArrayList<ApiLevelRange> apis = new ArrayList<>(rewritingFlagsMap.keySet());
+ apis.sort((x, y) -> -x.deterministicOrder(y));
+ for (ApiLevelRange range : apis) {
+ MachineRewritingFlags flags = rewritingFlagsMap.get(range);
+ HashMap<String, Object> toJson = new LinkedHashMap<>();
+ toJson.put(API_LEVEL_BELOW_OR_EQUAL_KEY, range.getApiLevelBelowOrEqualAsInt());
+ if (range.hasApiLevelGreaterOrEqual()) {
+ toJson.put(API_LEVEL_GREATER_OR_EQUAL_KEY, range.getApiLevelGreaterOrEqualAsInt());
+ }
+ writeFlags(flags, toJson);
+ list.add(toJson);
+ }
+ return list;
+ }
+
+ private void writeFlagCollection(
+ String key, Collection<? extends DexItem> collection, Map<String, Object> toJson) {
+ if (!collection.isEmpty()) {
+ toJson.put(key, collectionToJsonStruct(collection));
+ }
+ }
+
+ private void writeFlagMap(
+ String key, Map<? extends DexItem, ? extends DexItem> map, Map<String, Object> toJson) {
+ if (!map.isEmpty()) {
+ toJson.put(key, mapToJsonStruct(map));
+ }
+ }
+
+ private void writeFlagMapToSpecificationDescriptor(
+ String key,
+ Map<? extends DexItem, ? extends SpecificationDescriptor> map,
+ Map<String, Object> toJson) {
+ if (!map.isEmpty()) {
+ toJson.put(key, specificationDescriptorMapToJsonStruct(map));
+ }
+ }
+
+ private void writeFlagMapToMethodArray(
+ String key, Map<? extends DexItem, DexMethod[]> map, Map<String, Object> toJson) {
+ if (!map.isEmpty()) {
+ TreeMap<String, Object> stringMap = new TreeMap<>();
+ map.forEach((k, v) -> stringMap.put(toString(k), methodArrayToJsonStruct(v)));
+ toJson.put(key, stringMap);
+ }
+ }
+
+ private void writeFlagLinkedHashMapToSpecificationDescriptor(
+ String key,
+ LinkedHashMap<? extends DexItem, ? extends SpecificationDescriptor> map,
+ Map<String, Object> toJson) {
+ if (!map.isEmpty()) {
+ toJson.put(key, specificationDescriptorLinkedHashMapToJsonStruct(map));
+ }
+ }
+
+ private void writeMembersWithFlags(
+ String key,
+ Map<? extends DexItem, ? extends AccessFlags<?>> membersWithFlags,
+ Map<String, Object> toJson) {
+ if (!membersWithFlags.isEmpty()) {
+ List<String> stringSet = new ArrayList<>();
+ membersWithFlags.forEach(
+ (member, flags) -> stringSet.add(flags.toString() + " " + toString(member)));
+ toJson.put(key, stringSet);
+ }
+ }
+
+ private void writeFlags(MachineRewritingFlags flags, Map<String, Object> toJson) {
+ writeFlagMap(REWRITE_TYPE_KEY, flags.getRewriteType(), toJson);
+ writeFlagCollection(MAINTAIN_TYPE_KEY, flags.getMaintainType(), toJson);
+ writeFlagMap(REWRITE_DERIVED_TYPE_ONLY_KEY, flags.getRewriteDerivedTypeOnly(), toJson);
+ writeFlagMap(STATIC_FIELD_RETARGET_KEY, flags.getStaticFieldRetarget(), toJson);
+ writeFlagMap(COVARIANT_RETARGET_KEY, flags.getCovariantRetarget(), toJson);
+ writeFlagMap(STATIC_RETARGET_KEY, flags.getStaticRetarget(), toJson);
+ writeFlagMap(NON_EMULATED_VIRTUAL_RETARGET_KEY, flags.getNonEmulatedVirtualRetarget(), toJson);
+ writeFlagMapToSpecificationDescriptor(
+ EMULATED_VIRTUAL_RETARGET_KEY, flags.getEmulatedVirtualRetarget(), toJson);
+ writeFlagMap(
+ EMULATED_VIRTUAL_RETARGET_THROUGH_EMULATED_INTERFACE_KEY,
+ flags.getEmulatedVirtualRetargetThroughEmulatedInterface(),
+ toJson);
+ writeFlagMapToMethodArray(
+ API_GENERIC_TYPES_CONVERSION_KEY, flags.getApiGenericConversion(), toJson);
+ writeFlagMapToSpecificationDescriptor(
+ EMULATED_INTERFACE_KEY, flags.getEmulatedInterfaces(), toJson);
+ writeFlagLinkedHashMapToSpecificationDescriptor(WRAPPER_KEY, flags.getWrappers(), toJson);
+ writeFlagMap(LEGACY_BACKPORT_KEY, flags.getLegacyBackport(), toJson);
+ writeFlagCollection(DONT_RETARGET_KEY, flags.getDontRetarget(), toJson);
+ writeFlagMapToSpecificationDescriptor(
+ CUSTOM_CONVERSION_KEY, flags.getCustomConversions(), toJson);
+ writeMembersWithFlags(AMEND_LIBRARY_METHOD_KEY, flags.getAmendLibraryMethod(), toJson);
+ writeMembersWithFlags(AMEND_LIBRARY_FIELD_KEY, flags.getAmendLibraryField(), toJson);
+ }
+
+ private LinkedHashMap<String, ?> specificationDescriptorLinkedHashMapToJsonStruct(
+ LinkedHashMap<? extends DexItem, ? extends SpecificationDescriptor> map) {
+ // Already sorted with custom advanced deterministic sort, maintain the order.
+ LinkedHashMap<String, Object> stringMap = new LinkedHashMap<>();
+ map.forEach((k, v) -> stringMap.put(toString(k), v.toJsonStruct(this)));
+ return stringMap;
+ }
+
+ private TreeMap<String, String> mapToJsonStruct(Map<? extends DexItem, ? extends DexItem> map) {
+ TreeMap<String, String> stringMap = new TreeMap<>();
+ map.forEach((k, v) -> stringMap.put(toString(k), toString(v)));
+ return stringMap;
+ }
+
+ private TreeMap<String, ?> specificationDescriptorMapToJsonStruct(
+ Map<? extends DexItem, ? extends SpecificationDescriptor> map) {
+ TreeMap<String, Object> stringMap = new TreeMap<>();
+ map.forEach((k, v) -> stringMap.put(toString(k), v.toJsonStruct(this)));
+ return stringMap;
+ }
+
+ private List<String> collectionToJsonStruct(Collection<? extends DexItem> col) {
+ List<String> stringCol = new ArrayList<>();
+ col.forEach(e -> stringCol.add(toString(e)));
+ stringCol.sort(Comparator.naturalOrder());
+ return stringCol;
+ }
+
+ private String[] methodArrayToJsonStruct(DexMethod[] methodArray) {
+ String[] strings = new String[methodArray.length];
+ for (int i = 0; i < methodArray.length; i++) {
+ strings[i] = methodArray[i] == null ? "" : toString(methodArray[i]);
+ }
+ return strings;
+ }
+
+ private String toString(DexItem o) {
+ if (o instanceof DexType) {
+ return typeToString((DexType) o);
+ }
+ if (o instanceof DexField) {
+ DexField field = (DexField) o;
+ return typeToString(field.getType())
+ + " "
+ + typeToString(field.getHolderType())
+ + "#"
+ + field.getName();
+ }
+ if (o instanceof DexMethod) {
+ DexMethod method = (DexMethod) o;
+ StringBuilder sb =
+ new StringBuilder()
+ .append(typeToString(method.getReturnType()))
+ .append(" ")
+ .append(typeToString(method.getHolderType()))
+ .append("#")
+ .append(method.getName())
+ .append("(");
+ for (int i = 0; i < method.getParameters().size(); i++) {
+ sb.append(typeToString(method.getParameter(i)));
+ if (i != method.getParameters().size() - 1) {
+ sb.append(", ");
+ }
+ }
+ sb.append(")");
+ return sb.toString();
+ }
+ throw new Unreachable();
+ }
+
+ private String typeToString(DexType type) {
+ if (type.isPrimitiveType() || type.isPrimitiveArrayType() || type.isVoidType()) {
+ return type.toString();
+ }
+ if (type.isArrayType()) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(typeToString(type.toBaseType(factory)));
+ for (int i = 0; i < type.getNumberOfLeadingSquareBrackets(); i++) {
+ sb.append("[]");
+ }
+ return sb.toString();
+ }
+ String pack =
+ packageMap.computeIfAbsent(type.getPackageName(), k -> nextMinifiedPackagePrefix());
+ return pack + type.getSimpleName();
+ }
+
+ private String nextMinifiedPackagePrefix() {
+ if (next >= chars.length()) {
+ // This should happen only when the R8 team release machine specifications (not in user
+ // compilations).
+ throw new RuntimeException(
+ "MultiAPILevelMachineDesugaredLibrarySpecificationJsonExporter "
+ + "cannot encode the next package because the encoding ran out of characters."
+ + " Extend the chars sequence or improve the encoding to fix this.");
+ }
+ return chars.charAt(next++) + "$";
+ }
+
+ public Object[] exportCustomConversionDescriptor(
+ CustomConversionDescriptor customConversionDescriptor) {
+ String toString = toString(customConversionDescriptor.getTo());
+ String fromString = toString(customConversionDescriptor.getFrom());
+ return new Object[] {toString, fromString};
+ }
+
+ public Object[] exportDerivedMethod(DerivedMethod derivedMethod) {
+ String methodString = toString(derivedMethod.getMethod());
+ String holderKindString =
+ Integer.toString(
+ derivedMethod.getHolderKind() == null ? -1 : derivedMethod.getHolderKind().getId());
+ return new Object[] {methodString, holderKindString};
+ }
+
+ public Object[] exportEmulatedDispatchMethodDescriptor(
+ EmulatedDispatchMethodDescriptor descriptor) {
+ Object interfaceMethodJsonStruct = exportDerivedMethod(descriptor.getInterfaceMethod());
+ Object emulatedDispatchMethodJsonStruct =
+ exportDerivedMethod(descriptor.getEmulatedDispatchMethod());
+ Object forwardingMethodJsonStruct = exportDerivedMethod(descriptor.getForwardingMethod());
+ Object dispatchCasesJsonStruct =
+ specificationDescriptorLinkedHashMapToJsonStruct(descriptor.getDispatchCases());
+ return new Object[] {
+ interfaceMethodJsonStruct,
+ emulatedDispatchMethodJsonStruct,
+ forwardingMethodJsonStruct,
+ dispatchCasesJsonStruct
+ };
+ }
+
+ public Object[] exportEmulatedInterfaceDescriptor(EmulatedInterfaceDescriptor descriptor) {
+ Object rewrittenTypeString = toString(descriptor.getRewrittenType());
+ Object emulatedMethodsJsonStruct =
+ specificationDescriptorMapToJsonStruct(descriptor.getEmulatedMethods());
+ return new Object[] {rewrittenTypeString, emulatedMethodsJsonStruct};
+ }
+
+ public Object[] exportWrapperDescriptor(WrapperDescriptor descriptor) {
+ Object methodStruct = collectionToJsonStruct(descriptor.getMethods());
+ Object subwrappersStruct = collectionToJsonStruct(descriptor.getSubwrappers());
+ return new Object[] {methodStruct, descriptor.hasNonPublicAccess(), subwrappersStruct};
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/SpecificationDescriptor.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/SpecificationDescriptor.java
new file mode 100644
index 0000000..0d37b32
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/SpecificationDescriptor.java
@@ -0,0 +1,10 @@
+// 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.machinespecification;
+
+public interface SpecificationDescriptor {
+
+ Object[] toJsonStruct(MultiAPILevelMachineDesugaredLibrarySpecificationJsonExporter exporter);
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/WrapperDescriptor.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/WrapperDescriptor.java
index 2403b90..156547e 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/WrapperDescriptor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/WrapperDescriptor.java
@@ -7,8 +7,9 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import java.util.List;
+import java.util.Objects;
-public class WrapperDescriptor {
+public class WrapperDescriptor implements SpecificationDescriptor {
private final List<DexMethod> methods;
private final List<DexType> subwrappers;
private final boolean nonPublicAccess;
@@ -31,4 +32,29 @@
public boolean hasNonPublicAccess() {
return nonPublicAccess;
}
+
+ @Override
+ public Object[] toJsonStruct(
+ MultiAPILevelMachineDesugaredLibrarySpecificationJsonExporter exporter) {
+ return exporter.exportWrapperDescriptor(this);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof WrapperDescriptor)) {
+ return false;
+ }
+ WrapperDescriptor that = (WrapperDescriptor) o;
+ return nonPublicAccess == that.nonPublicAccess
+ && Objects.equals(methods, that.methods)
+ && Objects.equals(subwrappers, that.subwrappers);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(methods, subwrappers, nonPublicAccess);
+ }
}
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/memberparser/AbstractFieldParser.java
similarity index 94%
rename from src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/memberparser/AbstractFieldParser.java
rename to src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/memberparser/AbstractFieldParser.java
index 8062063..ba3a060 100644
--- 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/memberparser/AbstractFieldParser.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.memberparser;
+package com.android.tools.r8.ir.desugar.desugaredlibrary.memberparser;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.graph.DexItemFactory;
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/memberparser/AbstractMemberParser.java
similarity index 94%
rename from src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/memberparser/AbstractMemberParser.java
rename to src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/memberparser/AbstractMemberParser.java
index 99475b2..1848ed8 100644
--- 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/memberparser/AbstractMemberParser.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.memberparser;
+package com.android.tools.r8.ir.desugar.desugaredlibrary.memberparser;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.DexItemFactory;
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/memberparser/AbstractMethodParser.java
similarity index 94%
rename from src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/memberparser/AbstractMethodParser.java
rename to src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/memberparser/AbstractMethodParser.java
index 1afff88..c10882d 100644
--- 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/memberparser/AbstractMethodParser.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.memberparser;
+package com.android.tools.r8.ir.desugar.desugaredlibrary.memberparser;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.graph.DexItemFactory;
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/memberparser/HumanFieldParser.java
similarity index 95%
rename from src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/memberparser/HumanFieldParser.java
rename to src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/memberparser/HumanFieldParser.java
index 9a5a44d..7f1b03f 100644
--- 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/memberparser/HumanFieldParser.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.memberparser;
+package com.android.tools.r8.ir.desugar.desugaredlibrary.memberparser;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItemFactory;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/memberparser/HumanMethodParser.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/memberparser/HumanMethodParser.java
similarity index 95%
rename from src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/memberparser/HumanMethodParser.java
rename to src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/memberparser/HumanMethodParser.java
index d78267b..b7bfd33 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/memberparser/HumanMethodParser.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/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.memberparser;
+package com.android.tools.r8.ir.desugar.desugaredlibrary.memberparser;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/memberparser/MachineFieldParser.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/memberparser/MachineFieldParser.java
new file mode 100644
index 0000000..002a8d3
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/memberparser/MachineFieldParser.java
@@ -0,0 +1,23 @@
+// 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.memberparser;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
+import java.util.function.Function;
+
+public class MachineFieldParser extends HumanFieldParser {
+ private final Function<String, DexType> typeParser;
+
+ public MachineFieldParser(DexItemFactory factory, Function<String, DexType> typeParser) {
+ super(factory);
+ this.typeParser = typeParser;
+ }
+
+ @Override
+ DexType stringTypeToDexType(String stringType) {
+ return typeParser.apply(stringType);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/memberparser/MachineMethodParser.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/memberparser/MachineMethodParser.java
new file mode 100644
index 0000000..6b88d87
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/memberparser/MachineMethodParser.java
@@ -0,0 +1,23 @@
+// 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.memberparser;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
+import java.util.function.Function;
+
+public class MachineMethodParser extends HumanMethodParser {
+ private final Function<String, DexType> typeParser;
+
+ public MachineMethodParser(DexItemFactory factory, Function<String, DexType> typeParser) {
+ super(factory);
+ this.typeParser = typeParser;
+ }
+
+ @Override
+ DexType stringTypeToDexType(String stringType) {
+ return typeParser.apply(stringType);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/AppForSpecConversion.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/AppForSpecConversion.java
deleted file mode 100644
index 74c31b4..0000000
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/AppForSpecConversion.java
+++ /dev/null
@@ -1,51 +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.specificationconversion;
-
-import com.android.tools.r8.dex.ApplicationReader;
-import com.android.tools.r8.graph.DexApplication;
-import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.ThreadUtils;
-import com.android.tools.r8.utils.Timing;
-import java.io.IOException;
-import java.nio.file.Path;
-import java.util.Collection;
-import java.util.concurrent.ExecutorService;
-
-public class AppForSpecConversion {
-
- public static DexApplication readAppForTesting(
- Collection<Path> desugaredJDKLib,
- Collection<Path> androidLib,
- InternalOptions options,
- boolean libraryCompilation,
- Timing timing)
- throws IOException {
- timing.begin("Read App for testing");
- assert !libraryCompilation || desugaredJDKLib != null;
- AndroidApp.Builder builder = AndroidApp.builder();
- if (libraryCompilation) {
- builder.addProgramFiles(desugaredJDKLib);
- }
- AndroidApp inputApp = builder.addLibraryFiles(androidLib).build();
- DexApplication app = internalReadApp(inputApp, options, timing);
- timing.end();
- return app;
- }
-
- private static DexApplication internalReadApp(
- AndroidApp inputApp, InternalOptions options, Timing timing) throws IOException {
- timing.begin("Internal Read App");
- ApplicationReader applicationReader = new ApplicationReader(inputApp, options, timing);
- ExecutorService executorService = ThreadUtils.getExecutorService(options);
- assert !options.ignoreJavaLibraryOverride;
- options.ignoreJavaLibraryOverride = true;
- DexApplication app = applicationReader.read(executorService);
- options.ignoreJavaLibraryOverride = false;
- timing.end();
- return app;
- }
-}
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 473cfda..51262ef 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
@@ -8,7 +8,6 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
-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.machinespecification.MachineRewritingFlags;
import com.android.tools.r8.utils.DescriptorUtils;
@@ -34,12 +33,13 @@
public HumanToMachinePrefixConverter(
AppInfoWithClassHierarchy appInfo,
MachineRewritingFlags.Builder builder,
- HumanDesugaredLibrarySpecification humanSpec,
+ String synthesizedPrefix,
+ boolean libraryCompilation,
HumanRewritingFlags rewritingFlags) {
this.appInfo = appInfo;
this.builder = builder;
- this.synthesizedPrefix = humanSpec.getSynthesizedLibraryClassesPackagePrefix();
- this.libraryCompilation = humanSpec.isLibraryCompilation();
+ this.synthesizedPrefix = synthesizedPrefix;
+ this.libraryCompilation = libraryCompilation;
this.descriptorPrefix = convertRewritePrefix(rewritingFlags.getRewritePrefix());
this.descriptorDontRewritePrefix = convertPrefixSet(rewritingFlags.getDontRewritePrefix());
this.descriptorMaintainPrefix = convertPrefixSet(rewritingFlags.getMaintainPrefix());
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 1097b06..eb333a7 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
@@ -13,24 +13,26 @@
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.ApiLevelRange;
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAmender;
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.ir.desugar.desugaredlibrary.humanspecification.MultiAPILevelHumanDesugaredLibrarySpecification;
import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.CustomConversionDescriptor;
import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineRewritingFlags;
import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineTopLevelFlags;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MultiAPILevelMachineDesugaredLibrarySpecification;
import com.android.tools.r8.synthesis.SyntheticItems.GlobalSyntheticsStrategy;
-import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.Timing;
import com.google.common.collect.Sets;
import java.io.IOException;
-import java.nio.file.Path;
import java.util.ArrayList;
-import java.util.Collection;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Set;
public class HumanToMachineSpecificationConverter {
@@ -44,16 +46,42 @@
this.timing = timing;
}
- public MachineDesugaredLibrarySpecification convertForTesting(
- HumanDesugaredLibrarySpecification humanSpec,
- Collection<Path> desugaredJDKLib,
- Collection<Path> androidLib,
- InternalOptions options)
+ public MultiAPILevelMachineDesugaredLibrarySpecification convertAllAPILevels(
+ MultiAPILevelHumanDesugaredLibrarySpecification humanSpec, DexApplication app)
throws IOException {
- DexApplication app =
- AppForSpecConversion.readAppForTesting(
- desugaredJDKLib, androidLib, options, humanSpec.isLibraryCompilation(), timing);
- return convert(humanSpec, app);
+ timing.begin("Legacy to human all API convert");
+ reporter = app.options.reporter;
+ appInfo =
+ AppInfoWithClassHierarchy.createForDesugaring(
+ AppInfo.createInitialAppInfo(app, GlobalSyntheticsStrategy.forNonSynthesizing()));
+
+ MachineTopLevelFlags machineTopLevelFlags = convertTopLevelFlags(humanSpec.getTopLevelFlags());
+ String synthesizedPrefix = machineTopLevelFlags.getSynthesizedLibraryClassesPackagePrefix();
+ Map<ApiLevelRange, MachineRewritingFlags> commonFlags =
+ convertRewritingFlagMap(humanSpec.getCommonFlags(), synthesizedPrefix, true);
+ Map<ApiLevelRange, MachineRewritingFlags> programFlags =
+ convertRewritingFlagMap(humanSpec.getProgramFlags(), synthesizedPrefix, false);
+ Map<ApiLevelRange, MachineRewritingFlags> libraryFlags =
+ convertRewritingFlagMap(humanSpec.getLibraryFlags(), synthesizedPrefix, true);
+
+ MultiAPILevelMachineDesugaredLibrarySpecification machineSpec =
+ new MultiAPILevelMachineDesugaredLibrarySpecification(
+ humanSpec.getOrigin(), machineTopLevelFlags, commonFlags, libraryFlags, programFlags);
+ timing.end();
+ return machineSpec;
+ }
+
+ private Map<ApiLevelRange, MachineRewritingFlags> convertRewritingFlagMap(
+ Map<ApiLevelRange, HumanRewritingFlags> libFlags,
+ String synthesizedPrefix,
+ boolean interpretAsLibraryCompilation) {
+ Map<ApiLevelRange, MachineRewritingFlags> map = new HashMap<>();
+ libFlags.forEach(
+ (range, flags) ->
+ map.put(
+ range,
+ convertRewritingFlags(flags, synthesizedPrefix, interpretAsLibraryCompilation)));
+ return map;
}
public MachineDesugaredLibrarySpecification convert(
@@ -67,7 +95,11 @@
app,
humanSpec.isLibraryCompilation(),
humanSpec.getTopLevelFlags().getRequiredCompilationAPILevel());
- MachineRewritingFlags machineRewritingFlags = convertRewritingFlags(humanSpec);
+ MachineRewritingFlags machineRewritingFlags =
+ convertRewritingFlags(
+ humanSpec.getRewritingFlags(),
+ humanSpec.getSynthesizedLibraryClassesPackagePrefix(),
+ humanSpec.isLibraryCompilation());
MachineTopLevelFlags topLevelFlags = convertTopLevelFlags(humanSpec.getTopLevelFlags());
timing.end();
return new MachineDesugaredLibrarySpecification(
@@ -85,9 +117,8 @@
}
private MachineRewritingFlags convertRewritingFlags(
- HumanDesugaredLibrarySpecification humanSpec) {
+ HumanRewritingFlags rewritingFlags, String synthesizedPrefix, boolean libraryCompilation) {
timing.begin("convert rewriting flags");
- HumanRewritingFlags rewritingFlags = humanSpec.getRewritingFlags();
MachineRewritingFlags.Builder builder = MachineRewritingFlags.builder();
DesugaredLibraryAmender.run(
rewritingFlags.getAmendLibraryMethod(),
@@ -102,7 +133,8 @@
.convertRetargetFlags(rewritingFlags, builder, this::warnMissingReferences);
new HumanToMachineEmulatedInterfaceConverter(appInfo)
.convertEmulatedInterfaces(rewritingFlags, appInfo, builder, this::warnMissingReferences);
- new HumanToMachinePrefixConverter(appInfo, builder, humanSpec, rewritingFlags)
+ new HumanToMachinePrefixConverter(
+ appInfo, builder, synthesizedPrefix, libraryCompilation, rewritingFlags)
.convertPrefixFlags(rewritingFlags, this::warnMissingDexString);
new HumanToMachineWrapperConverter(appInfo)
.convertWrappers(rewritingFlags, builder, this::warnMissingReferences);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LegacyToHumanSpecificationConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LegacyToHumanSpecificationConverter.java
index 9ac3369..c0b124b 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LegacyToHumanSpecificationConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LegacyToHumanSpecificationConverter.java
@@ -4,8 +4,6 @@
package com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion;
-import com.android.tools.r8.StringConsumer;
-import com.android.tools.r8.StringResource;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexClass;
@@ -23,15 +21,12 @@
import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanTopLevelFlags;
import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.MultiAPILevelHumanDesugaredLibrarySpecification;
import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.MultiAPILevelHumanDesugaredLibrarySpecificationFlagDeduplicator;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.MultiAPILevelHumanDesugaredLibrarySpecificationJsonExporter;
import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyRewritingFlags;
import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyTopLevelFlags;
import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.MultiAPILevelLegacyDesugaredLibrarySpecification;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.MultiAPILevelLegacyDesugaredLibrarySpecificationParser;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Pair;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.Timing;
@@ -39,9 +34,7 @@
import com.google.common.collect.ImmutableMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import java.io.IOException;
-import java.nio.file.Path;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -60,32 +53,11 @@
this.timing = timing;
}
- public void convertAllAPILevels(
- StringResource inputSpecification,
- Collection<Path> desugaredJDKLib,
- Collection<Path> androidLib,
- StringConsumer output)
- throws IOException {
- InternalOptions options = new InternalOptions();
- MultiAPILevelLegacyDesugaredLibrarySpecification legacySpec =
- new MultiAPILevelLegacyDesugaredLibrarySpecificationParser(
- options.dexItemFactory(), options.reporter)
- .parseMultiLevelConfiguration(inputSpecification);
- MultiAPILevelHumanDesugaredLibrarySpecification humanSpec =
- convertAllAPILevels(legacySpec, desugaredJDKLib, androidLib, options);
- MultiAPILevelHumanDesugaredLibrarySpecificationJsonExporter.export(humanSpec, output);
- }
-
public MultiAPILevelHumanDesugaredLibrarySpecification convertAllAPILevels(
- MultiAPILevelLegacyDesugaredLibrarySpecification legacySpec,
- Collection<Path> desugaredJDKLib,
- Collection<Path> androidLib,
- InternalOptions options)
+ MultiAPILevelLegacyDesugaredLibrarySpecification legacySpec, DexApplication app)
throws IOException {
timing.begin("Legacy to human all API convert");
Origin origin = legacySpec.getOrigin();
- DexApplication app =
- AppForSpecConversion.readAppForTesting(desugaredJDKLib, androidLib, options, true, timing);
HumanTopLevelFlags humanTopLevelFlags = convertTopLevelFlags(legacySpec.getTopLevelFlags());
Map<ApiLevelRange, HumanRewritingFlags> commonFlags =
@@ -102,23 +74,11 @@
new MultiAPILevelHumanDesugaredLibrarySpecification(
origin, humanTopLevelFlags, commonFlags, libraryFlags, programFlags);
MultiAPILevelHumanDesugaredLibrarySpecificationFlagDeduplicator.deduplicateFlags(
- humanSpec, options.reporter);
+ humanSpec, app.options.reporter);
timing.end();
return humanSpec;
}
- public HumanDesugaredLibrarySpecification convertForTesting(
- LegacyDesugaredLibrarySpecification legacySpec,
- Collection<Path> desugaredJDKLib,
- Collection<Path> androidLib,
- InternalOptions options)
- throws IOException {
- DexApplication app =
- AppForSpecConversion.readAppForTesting(
- desugaredJDKLib, androidLib, options, legacySpec.isLibraryCompilation(), timing);
- return convert(legacySpec, app);
- }
-
public HumanDesugaredLibrarySpecification convert(
LegacyDesugaredLibrarySpecification legacySpec, DexApplication app) throws IOException {
timing.begin("Legacy to Human convert");
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 92467a3..16821510 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
@@ -49,6 +49,7 @@
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiConsumer;
@@ -656,6 +657,27 @@
&& !emulatedInterfaceInfo.contains(resolvedHolder.type)) {
return true;
}
+ // If the method overrides an abstract method which does not match the criteria, then
+ // forwarding methods are required so the invokes on the top level holder can resolve at
+ // runtime. See b/202188674.
+ if (overridesAbstractNonLibraryInterfaceMethod(
+ clazz, signature.get(), emulatedInterfaceInfo)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean overridesAbstractNonLibraryInterfaceMethod(
+ DexClass clazz, DexMethod dexMethod, EmulatedInterfaceInfo emulatedInterfaceInfo) {
+ List<Entry<DexClass, DexEncodedMethod>> abstractInterfaceMethods =
+ appView.appInfoForDesugaring().getAbstractInterfaceMethods(clazz, dexMethod);
+ for (Entry<DexClass, DexEncodedMethod> classAndMethod : abstractInterfaceMethods) {
+ DexClass foundMethodClass = classAndMethod.getKey();
+ if (!foundMethodClass.isLibraryClass()
+ && !emulatedInterfaceInfo.contains(foundMethodClass.type)) {
+ return true;
+ }
}
return false;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index 38afb5d..85802c1 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -95,6 +95,7 @@
import com.android.tools.r8.ir.code.StaticGet;
import com.android.tools.r8.ir.code.Switch;
import com.android.tools.r8.ir.code.Throw;
+import com.android.tools.r8.ir.code.UnusedArgument;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.ir.code.Xor;
@@ -3738,6 +3739,14 @@
iterator.add(new InvokeVirtual(printLn, null, ImmutableList.of(out, empty)));
}
+ public static void replaceUnusedArgumentTrivialPhis(UnusedArgument unusedArgument) {
+ replaceTrivialPhis(unusedArgument.outValue());
+ for (Phi phiUser : unusedArgument.outValue().uniquePhiUsers()) {
+ phiUser.removeTrivialPhi();
+ }
+ assert !unusedArgument.outValue().hasPhiUsers();
+ }
+
public static void ensureDirectStringNewToInit(IRCode code, DexItemFactory dexItemFactory) {
for (Instruction instruction : code.instructions()) {
if (instruction.isInvokeDirect()) {
@@ -3747,7 +3756,7 @@
&& method.holder == dexItemFactory.stringType
&& invoke.getReceiver().isPhi()) {
NewInstance newInstance = findNewInstance(invoke.getReceiver().asPhi());
- replaceTrivialNewInstancePhis(newInstance.outValue());
+ replaceTrivialPhis(newInstance.outValue());
if (invoke.getReceiver().isPhi()) {
throw new CompilationError(
"Failed to remove trivial phis between new-instance and <init>");
@@ -3810,18 +3819,15 @@
}
}
- // If an <init> call takes place on a phi the code must contain an irreducible loop between the
- // new-instance and the <init>. Assuming the code is verifiable, new-instance must flow to a
- // unique <init>. Here we compute the set of strongly connected phis making use of the
- // new-instance value and replace all trivial ones by the new-instance value.
+ // We compute the set of strongly connected phis making use of the out value and replace all
+ // trivial ones by the out value.
// This is a simplified variant of the removeRedundantPhis algorithm in Section 3.2 of:
// http://compilers.cs.uni-saarland.de/papers/bbhlmz13cc.pdf
- private static void replaceTrivialNewInstancePhis(Value newInstanceValue) {
- List<Set<Value>> components =
- new SCC<Value>(Value::uniquePhiUsers).computeSCC(newInstanceValue);
+ private static void replaceTrivialPhis(Value outValue) {
+ List<Set<Value>> components = new SCC<Value>(Value::uniquePhiUsers).computeSCC(outValue);
for (int i = components.size() - 1; i >= 0; i--) {
Set<Value> component = components.get(i);
- if (component.size() == 1 && component.iterator().next() == newInstanceValue) {
+ if (component.size() == 1 && component.iterator().next() == outValue) {
continue;
}
Set<Phi> trivialPhis = Sets.newIdentityHashSet();
@@ -3829,7 +3835,7 @@
boolean isTrivial = true;
Phi p = value.asPhi();
for (Value op : p.getOperands()) {
- if (op != newInstanceValue && !component.contains(op)) {
+ if (op != outValue && !component.contains(op)) {
isTrivial = false;
break;
}
@@ -3842,7 +3848,7 @@
for (Value op : trivialPhi.getOperands()) {
op.removePhiUser(trivialPhi);
}
- trivialPhi.replaceUsers(newInstanceValue);
+ trivialPhi.replaceUsers(outValue);
trivialPhi.getBlock().removePhi(trivialPhi);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
index 4867829..525041d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
@@ -837,7 +837,7 @@
reportFailure(
enumClass,
new MissingInstanceFieldValueForEnumInstanceReason(
- staticField.getReference(), factory.enumMembers.ordinalField));
+ factory.enumMembers.ordinalField, staticField.getReference()));
return null;
}
int ordinal = optionalOrdinal.getAsInt();
@@ -926,7 +926,14 @@
Int2ReferenceMap<ObjectState> ordinalToObjectState) {
DexEncodedField encodedInstanceField =
appView.appInfo().resolveFieldOn(enumClass, instanceField).getResolvedField();
- assert encodedInstanceField != null;
+ if (encodedInstanceField == null) {
+ // This seems to be happening in b/238911016 but we do not have a reproduction.
+ // If this assert fails, it would be nice to understand what is going on and potentially
+ // fix the code below to do something more appropriate than bailing out.
+ assert false;
+ reportFailure(enumClass, new MissingInstanceFieldValueForEnumInstanceReason(instanceField));
+ return EnumInstanceFieldUnknownData.getInstance();
+ }
boolean canBeOrdinal = instanceField.type.isIntType();
ImmutableInt2ReferenceSortedMap.Builder<AbstractValue> data =
ImmutableInt2ReferenceSortedMap.builder();
@@ -935,7 +942,7 @@
AbstractValue fieldValue = state.getAbstractFieldValue(encodedInstanceField);
if (!fieldValue.isSingleValue()) {
reportFailure(
- enumClass, new MissingInstanceFieldValueForEnumInstanceReason(ordinal, instanceField));
+ enumClass, new MissingInstanceFieldValueForEnumInstanceReason(instanceField, ordinal));
return EnumInstanceFieldUnknownData.getInstance();
}
if (!(fieldValue.isSingleNumberValue() || fieldValue.isSingleStringValue())) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/eligibility/Reason.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/eligibility/Reason.java
index bac0275..63fc7fd 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/eligibility/Reason.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/eligibility/Reason.java
@@ -120,14 +120,20 @@
private final int ordinal;
private final DexField instanceField;
+ public MissingInstanceFieldValueForEnumInstanceReason(DexField instanceField) {
+ this.enumField = null;
+ this.ordinal = -1;
+ this.instanceField = instanceField;
+ }
+
public MissingInstanceFieldValueForEnumInstanceReason(
- DexField enumField, DexField instanceField) {
+ DexField instanceField, DexField enumField) {
this.enumField = enumField;
this.ordinal = -1;
this.instanceField = instanceField;
}
- public MissingInstanceFieldValueForEnumInstanceReason(int ordinal, DexField instanceField) {
+ public MissingInstanceFieldValueForEnumInstanceReason(DexField instanceField, int ordinal) {
this.enumField = null;
this.ordinal = ordinal;
this.instanceField = instanceField;
@@ -147,6 +153,11 @@
+ instanceField.toSourceString()
+ ")";
}
+ if (ordinal == -1) {
+ return "MissingInstanceFieldValueForEnumInstance(Cannot resolve instance field="
+ + instanceField.toSourceString()
+ + ")";
+ }
assert ordinal >= 0;
return "MissingInstanceFieldValueForEnumInstance(ordinal="
+ ordinal
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java b/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java
index aae3f42..f9266b0 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java
@@ -35,6 +35,8 @@
*/
public class ClassNamingForNameMapper implements ClassNaming {
+ private static final List<MappingInformation> EMPTY_MAPPING_INFORMATION = Collections.emptyList();
+
public static class Builder extends ClassNaming.Builder {
private final String originalName;
@@ -43,7 +45,7 @@
private final Map<FieldSignature, MemberNaming> fieldMembers = Maps.newHashMap();
private final Map<String, List<MappedRange>> mappedRangesByName = Maps.newHashMap();
private final Map<String, List<MemberNaming>> mappedFieldNamingsByName = Maps.newHashMap();
- private final List<MappingInformation> additionalMappingInfo = new ArrayList<>();
+ private List<MappingInformation> additionalMappingInfo = EMPTY_MAPPING_INFORMATION;
private final BiConsumer<String, String> originalSourceFileConsumer;
private Builder(
@@ -107,6 +109,9 @@
@Override
public void addMappingInformation(
MappingInformation info, Consumer<MappingInformation> onProhibitedAddition) {
+ if (additionalMappingInfo == EMPTY_MAPPING_INFORMATION) {
+ additionalMappingInfo = new ArrayList<>();
+ }
for (MappingInformation existing : additionalMappingInfo) {
if (!existing.allowOther(info)) {
onProhibitedAddition.accept(existing);
@@ -497,7 +502,7 @@
*/
private final int sequenceNumber = getNextSequenceNumber();
- private List<MappingInformation> additionalMappingInfo = new ArrayList<>();
+ private List<MappingInformation> additionalMappingInfo = EMPTY_MAPPING_INFORMATION;
private MappedRange(
Range minifiedRange, MethodSignature signature, Range originalRange, String renamedName) {
@@ -509,6 +514,9 @@
public void addMappingInformation(
MappingInformation info, Consumer<MappingInformation> onProhibitedAddition) {
+ if (additionalMappingInfo == EMPTY_MAPPING_INFORMATION) {
+ additionalMappingInfo = new ArrayList<>();
+ }
for (MappingInformation existing : additionalMappingInfo) {
if (!existing.allowOther(info)) {
onProhibitedAddition.accept(existing);
@@ -630,8 +638,7 @@
}
public List<MappingInformation> getAdditionalMappingInfo() {
- return additionalMappingInfo;
+ return Collections.unmodifiableList(additionalMappingInfo);
}
}
}
-
diff --git a/src/main/java/com/android/tools/r8/naming/MapVersion.java b/src/main/java/com/android/tools/r8/naming/MapVersion.java
index ae93504..8d8c9e0 100644
--- a/src/main/java/com/android/tools/r8/naming/MapVersion.java
+++ b/src/main/java/com/android/tools/r8/naming/MapVersion.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.naming;
import com.android.tools.r8.Keep;
+import com.android.tools.r8.naming.mappinginformation.MapVersionMappingInformation;
import com.android.tools.r8.utils.structural.Ordered;
@Keep
@@ -35,6 +36,10 @@
return null;
}
+ public MapVersionMappingInformation toMapVersionMappingInformation() {
+ return new MapVersionMappingInformation(this, this.getName());
+ }
+
public boolean isUnknown() {
return this == MAP_VERSION_UNKNOWN;
}
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java b/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java
index bdd2b9b..6bde14bf 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java
@@ -11,7 +11,7 @@
import com.android.tools.r8.naming.mappinginformation.MapVersionMappingInformation;
import com.android.tools.r8.naming.mappinginformation.MappingInformation;
import com.android.tools.r8.naming.mappinginformation.MappingInformationDiagnostics;
-import com.android.tools.r8.position.TextPosition;
+import com.android.tools.r8.position.Position;
import com.android.tools.r8.utils.BooleanBox;
import com.android.tools.r8.utils.IdentifierUtils;
import com.android.tools.r8.utils.StringUtils;
@@ -23,6 +23,7 @@
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
+import java.util.function.Function;
/**
* Parses a Proguard mapping file and produces mappings from obfuscated class names to the original
@@ -425,8 +426,37 @@
}
}
- private TextPosition getPosition() {
- return new TextPosition(0, lineNo, 1);
+ private Position getPosition() {
+ return new LinePosition(lineNo);
+ }
+
+ private static final class LinePosition implements Position {
+ private final int lineNo;
+
+ LinePosition(int lineNo) {
+ this.lineNo = lineNo;
+ }
+
+ @Override
+ public String getDescription() {
+ return "line " + lineNo;
+ }
+
+ @Override
+ public int hashCode() {
+ return lineNo;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (o instanceof LinePosition) {
+ return lineNo == ((LinePosition) o).lineNo;
+ }
+ return false;
+ }
}
// Parsing of components
@@ -460,17 +490,42 @@
}
}
+ // Small direct-mapped cache for computing String.substring.
+ //
+ // Due to inlining, the same method names and parameter types will occur repeatedly on multiple
+ // lines. String.substring ends up allocating a lot of garbage, so we use this cache to find
+ // String objects without having to allocate memory.
+ //
+ // "Direct-mapped" is inspired from computer architecture, where having a lookup policy in
+ // which entries can only ever map to one cache line is often faster than a fancy LRU cache.
+ private static final int SUBSTRING_CACHE_SIZE = 64;
+ private final String[] substringCache = new String[SUBSTRING_CACHE_SIZE];
// Cache for canonicalizing strings.
// This saves 10% of heap space for large programs.
- final HashMap<String, String> cache = new HashMap<>();
+ private final HashMap<String, String> identifierCache = new HashMap<>();
+
+ // Cache for canonicalizing signatures.
+ //
+ // Due to inlining, the same MethodSignature will come up many times in a ProguardMap.
+ // This happens to help a bit for FieldSignature too, so lump those in.
+ private final HashMap<Signature, Signature> signatureCache = new HashMap<>();
private String substring(int start) {
- String result = line.substring(start, lineOffset);
- if (cache.containsKey(result)) {
- return cache.get(result);
+ int cacheIdx;
+ {
+ // Check if there was a recent String accessed which matches the substring.
+ int len = lineOffset - start;
+ cacheIdx = len % SUBSTRING_CACHE_SIZE;
+ String candidate = substringCache[cacheIdx];
+ if (candidate != null
+ && candidate.length() == len
+ && line.regionMatches(start, candidate, 0, len)) {
+ return candidate;
+ }
}
- cache.put(result, result);
- return result;
+
+ String result = line.substring(start, lineOffset);
+ return substringCache[cacheIdx] = identifierCache.computeIfAbsent(result, Function.identity());
}
private String parseMethodName() {
@@ -510,7 +565,7 @@
skipWhitespace();
String[] arguments;
if (peekChar(0) == ')') {
- arguments = new String[0];
+ arguments = StringUtils.EMPTY_ARRAY;
} else {
List<String> items = new ArrayList<>();
items.add(parseType(true));
@@ -528,7 +583,7 @@
} else {
signature = new FieldSignature(name, type);
}
- return signature;
+ return signatureCache.computeIfAbsent(signature, Function.identity());
}
private void skipArrow() {
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java b/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java
index 63c2dfc..af66d9e 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java
@@ -9,7 +9,6 @@
import com.android.tools.r8.StringConsumer;
import com.android.tools.r8.Version;
import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.naming.mappinginformation.MapVersionMappingInformation;
import com.android.tools.r8.utils.ChainableStringConsumer;
import com.android.tools.r8.utils.ExceptionUtils;
import com.android.tools.r8.utils.InternalOptions;
@@ -121,7 +120,7 @@
if (mapVersion.isGreaterThan(MapVersion.MAP_VERSION_NONE)) {
builder
.append("# ")
- .append(new MapVersionMappingInformation(mapVersion, mapVersion.getName()).serialize())
+ .append(mapVersion.toMapVersionMappingInformation().serialize())
.append("\n");
}
builder.append("# " + MARKER_KEY_PG_MAP_ID + ": " + id.getId() + "\n");
diff --git a/src/main/java/com/android/tools/r8/retrace/MappingSupplier.java b/src/main/java/com/android/tools/r8/retrace/MappingSupplier.java
index 707023d..add5db3 100644
--- a/src/main/java/com/android/tools/r8/retrace/MappingSupplier.java
+++ b/src/main/java/com/android/tools/r8/retrace/MappingSupplier.java
@@ -6,12 +6,12 @@
import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.Keep;
+import com.android.tools.r8.naming.mappinginformation.MapVersionMappingInformation;
import com.android.tools.r8.references.ClassReference;
-import com.android.tools.r8.retrace.internal.MappingSupplierInternal;
+import java.util.Set;
@Keep
-public abstract class MappingSupplier<T extends MappingSupplier<T>>
- extends MappingSupplierInternal {
+public abstract class MappingSupplier<T extends MappingSupplier<T>> {
/***
* Register an allowed mapping lookup to allow for prefetching of resources.
@@ -22,4 +22,9 @@
DiagnosticsHandler diagnosticsHandler, ClassReference classReference);
public abstract void verifyMappingFileHash(DiagnosticsHandler diagnosticsHandler);
+
+ public abstract Set<MapVersionMappingInformation> getMapVersions(
+ DiagnosticsHandler diagnosticsHandler);
+
+ public abstract Retracer createRetracer(DiagnosticsHandler diagnosticsHandler);
}
diff --git a/src/main/java/com/android/tools/r8/retrace/ProguardMappingSupplier.java b/src/main/java/com/android/tools/r8/retrace/ProguardMappingSupplier.java
index 74cd98f..28b95a0 100644
--- a/src/main/java/com/android/tools/r8/retrace/ProguardMappingSupplier.java
+++ b/src/main/java/com/android/tools/r8/retrace/ProguardMappingSupplier.java
@@ -19,5 +19,7 @@
extends MappingSupplierBuilder<ProguardMappingSupplier, Builder> {
public abstract Builder setProguardMapProducer(ProguardMapProducer proguardMapProducer);
+
+ public abstract Builder setLoadAllDefinitions(boolean loadAllDefinitions);
}
}
diff --git a/src/main/java/com/android/tools/r8/retrace/Retrace.java b/src/main/java/com/android/tools/r8/retrace/Retrace.java
index 29b68ab..a3cf547 100644
--- a/src/main/java/com/android/tools/r8/retrace/Retrace.java
+++ b/src/main/java/com/android/tools/r8/retrace/Retrace.java
@@ -15,7 +15,6 @@
import com.android.tools.r8.retrace.RetraceCommand.Builder;
import com.android.tools.r8.retrace.internal.ResultWithContextImpl;
import com.android.tools.r8.retrace.internal.RetraceAbortException;
-import com.android.tools.r8.retrace.internal.RetracerImpl;
import com.android.tools.r8.retrace.internal.StackTraceElementStringProxy;
import com.android.tools.r8.retrace.internal.StackTraceRegularExpressionParser;
import com.android.tools.r8.utils.Box;
@@ -132,7 +131,7 @@
return builder;
}
- private static MappingSupplier getMappingSupplier(
+ private static MappingSupplier<?> getMappingSupplier(
String mappingPath, DiagnosticsHandler diagnosticsHandler) {
Path path = Paths.get(mappingPath);
if (!Files.exists(path)) {
@@ -145,6 +144,7 @@
return ProguardMappingSupplier.builder()
.setProguardMapProducer(ProguardMapProducer.fromPath(Paths.get(mappingPath)))
.setAllowExperimental(allowExperimentalMapVersion)
+ .setLoadAllDefinitions(false)
.build();
}
@@ -318,28 +318,6 @@
DiagnosticsHandler diagnosticsHandler = options.getDiagnosticsHandler();
StackTraceRegularExpressionParser stackTraceLineParser =
new StackTraceRegularExpressionParser(options.getRegularExpression());
- timing.begin("Read proguard map");
- RetracerImpl retracer =
- RetracerImpl.builder()
- .setMappingSupplier(mappingSupplier)
- .setDiagnosticsHandler(diagnosticsHandler)
- .build();
- retracer
- .getMapVersions()
- .forEach(
- mapVersionInfo -> {
- if (mapVersionInfo.getMapVersion().isUnknown()) {
- diagnosticsHandler.warning(
- RetraceUnknownMapVersionDiagnostic.create(mapVersionInfo.getValue()));
- }
- });
- StringRetrace stringRetracer =
- new StringRetrace(
- stackTraceLineParser,
- StackTraceElementProxyRetracer.createDefault(retracer),
- diagnosticsHandler,
- options.isVerbose());
- timing.end();
StackTraceSupplier stackTraceSupplier = command.getStacktraceSupplier();
int lineNumber = 0;
RetraceStackTraceContext context = RetraceStackTraceContext.empty();
@@ -374,6 +352,15 @@
mappingSupplier, proxy.getFieldOrReturnType(), diagnosticsHandler);
}
});
+ timing.begin("Read proguard map");
+ StringRetrace stringRetracer =
+ new StringRetrace(
+ stackTraceLineParser,
+ StackTraceElementProxyRetracer.createDefault(
+ mappingSupplier.createRetracer(diagnosticsHandler)),
+ diagnosticsHandler,
+ options.isVerbose());
+ timing.end();
timing.begin("Retracing");
ResultWithContext<String> result = stringRetracer.retraceParsed(parsedStackTrace, context);
timing.end();
@@ -387,6 +374,15 @@
if (command.printTimes()) {
timing.report();
}
+ mappingSupplier
+ .getMapVersions(diagnosticsHandler)
+ .forEach(
+ mapVersionInfo -> {
+ if (mapVersionInfo.getMapVersion().isUnknown()) {
+ diagnosticsHandler.warning(
+ RetraceUnknownMapVersionDiagnostic.create(mapVersionInfo.getValue()));
+ }
+ });
} catch (InvalidMappingFileException e) {
command.getOptions().getDiagnosticsHandler().error(new ExceptionDiagnostic(e));
throw e;
diff --git a/src/main/java/com/android/tools/r8/retrace/Retracer.java b/src/main/java/com/android/tools/r8/retrace/Retracer.java
index 650883b..633b143 100644
--- a/src/main/java/com/android/tools/r8/retrace/Retracer.java
+++ b/src/main/java/com/android/tools/r8/retrace/Retracer.java
@@ -10,7 +10,6 @@
import com.android.tools.r8.references.FieldReference;
import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.references.TypeReference;
-import com.android.tools.r8.retrace.internal.RetracerImpl;
import java.util.OptionalInt;
/** This is the main api interface for retrace. */
@@ -73,18 +72,12 @@
static Retracer createDefault(
ProguardMapProducer proguardMapProducer, DiagnosticsHandler diagnosticsHandler) {
try {
- ProguardMappingSupplier mappingSupplier =
- ProguardMappingSupplier.builder().setProguardMapProducer(proguardMapProducer).build();
- return Retracer.builder()
- .setMappingSupplier(mappingSupplier)
- .setDiagnosticsHandler(diagnosticsHandler)
- .build();
+ return ProguardMappingSupplier.builder()
+ .setProguardMapProducer(proguardMapProducer)
+ .build()
+ .createRetracer(diagnosticsHandler);
} catch (Exception e) {
throw new InvalidMappingFileException(e);
}
}
-
- static RetracerBuilder builder() {
- return RetracerImpl.builder();
- }
}
diff --git a/src/main/java/com/android/tools/r8/retrace/RetracerBuilder.java b/src/main/java/com/android/tools/r8/retrace/RetracerBuilder.java
deleted file mode 100644
index 21eb8e0..0000000
--- a/src/main/java/com/android/tools/r8/retrace/RetracerBuilder.java
+++ /dev/null
@@ -1,18 +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.retrace;
-
-import com.android.tools.r8.DiagnosticsHandler;
-import com.android.tools.r8.Keep;
-
-@Keep
-public interface RetracerBuilder {
-
- RetracerBuilder setMappingSupplier(MappingSupplier mappingSupplier);
-
- RetracerBuilder setDiagnosticsHandler(DiagnosticsHandler diagnosticsHandler);
-
- Retracer build();
-}
diff --git a/src/main/java/com/android/tools/r8/retrace/StringRetrace.java b/src/main/java/com/android/tools/r8/retrace/StringRetrace.java
index 164a208..5a7a797 100644
--- a/src/main/java/com/android/tools/r8/retrace/StringRetrace.java
+++ b/src/main/java/com/android/tools/r8/retrace/StringRetrace.java
@@ -9,7 +9,6 @@
import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.Keep;
import com.android.tools.r8.retrace.internal.ResultWithContextImpl;
-import com.android.tools.r8.retrace.internal.RetracerImpl;
import com.android.tools.r8.retrace.internal.StackTraceElementStringProxy;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.StringUtils;
@@ -42,10 +41,7 @@
*/
public static StringRetrace create(RetraceOptions command) {
return create(
- RetracerImpl.builder()
- .setMappingSupplier(command.getMappingSupplier())
- .setDiagnosticsHandler(command.getDiagnosticsHandler())
- .build(),
+ command.getMappingSupplier().createRetracer(command.getDiagnosticsHandler()),
command.getDiagnosticsHandler(),
command.getRegularExpression(),
command.isVerbose());
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/MappingSupplierInternal.java b/src/main/java/com/android/tools/r8/retrace/internal/MappingSupplierInternal.java
index c47c467..b2e8508 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/MappingSupplierInternal.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/MappingSupplierInternal.java
@@ -4,17 +4,15 @@
package com.android.tools.r8.retrace.internal;
-import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.naming.ClassNamingForNameMapper;
import com.android.tools.r8.naming.mappinginformation.MapVersionMappingInformation;
import java.util.Set;
public abstract class MappingSupplierInternal {
- abstract ClassNamingForNameMapper getClassNaming(
- DiagnosticsHandler diagnosticsHandler, String typeName);
+ abstract ClassNamingForNameMapper getClassNaming(String typeName);
- abstract String getSourceFileForClass(DiagnosticsHandler diagnosticsHandler, String typeName);
+ abstract String getSourceFileForClass(String typeName);
- abstract Set<MapVersionMappingInformation> getMapVersions(DiagnosticsHandler diagnosticsHandler);
+ abstract Set<MapVersionMappingInformation> getMapVersions();
}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/MappingSupplierInternalImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/MappingSupplierInternalImpl.java
new file mode 100644
index 0000000..277ad7b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/internal/MappingSupplierInternalImpl.java
@@ -0,0 +1,38 @@
+// 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.retrace.internal;
+
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.naming.ClassNamingForNameMapper;
+import com.android.tools.r8.naming.mappinginformation.MapVersionMappingInformation;
+import java.util.Set;
+
+public class MappingSupplierInternalImpl extends MappingSupplierInternal {
+
+ private final ClassNameMapper classNameMapper;
+
+ MappingSupplierInternalImpl(ClassNameMapper classNameMapper) {
+ this.classNameMapper = classNameMapper;
+ }
+
+ @Override
+ ClassNamingForNameMapper getClassNaming(String typeName) {
+ return classNameMapper.getClassNaming(typeName);
+ }
+
+ @Override
+ String getSourceFileForClass(String typeName) {
+ return classNameMapper.getSourceFile(typeName);
+ }
+
+ @Override
+ Set<MapVersionMappingInformation> getMapVersions() {
+ return classNameMapper.getMapVersions();
+ }
+
+ public static MappingSupplierInternal createInternal(ClassNameMapper classNameMapper) {
+ return new MappingSupplierInternalImpl(classNameMapper);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/PartitionMappingSupplierImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/PartitionMappingSupplierImpl.java
index dfddd37..d37d45c 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/PartitionMappingSupplierImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/PartitionMappingSupplierImpl.java
@@ -8,12 +8,10 @@
import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.naming.ClassNameMapper;
-import com.android.tools.r8.naming.ClassNamingForNameMapper;
import com.android.tools.r8.naming.LineReader;
import com.android.tools.r8.naming.MapVersion;
import com.android.tools.r8.naming.mappinginformation.MapVersionMappingInformation;
import com.android.tools.r8.references.ClassReference;
-import com.android.tools.r8.references.Reference;
import com.android.tools.r8.retrace.InvalidMappingFileException;
import com.android.tools.r8.retrace.MappingPartitionFromKeySupplier;
import com.android.tools.r8.retrace.PartitionMappingSupplier;
@@ -72,25 +70,33 @@
}
@Override
- Set<MapVersionMappingInformation> getMapVersions(DiagnosticsHandler diagnosticsHandler) {
+ public PartitionMappingSupplier registerClassUse(
+ DiagnosticsHandler diagnosticsHandler, ClassReference classReference) {
+ registerKeyUse(getMetadata(diagnosticsHandler).getKey(classReference));
+ return this;
+ }
+
+ private void registerKeyUse(String key) {
+ if (!builtKeys.contains(key) && pendingKeys.add(key)) {
+ registerPartitionCallback.register(key);
+ }
+ }
+
+ @Override
+ public void verifyMappingFileHash(DiagnosticsHandler diagnosticsHandler) {
+ String errorMessage = "Cannot verify map file hash for partitions";
+ diagnosticsHandler.error(new StringDiagnostic(errorMessage));
+ throw new RuntimeException(errorMessage);
+ }
+
+ @Override
+ public Set<MapVersionMappingInformation> getMapVersions(DiagnosticsHandler diagnosticsHandler) {
return Collections.singleton(
- new MapVersionMappingInformation(getMetadata(diagnosticsHandler).getMapVersion(), ""));
+ getMetadata(diagnosticsHandler).getMapVersion().toMapVersionMappingInformation());
}
@Override
- ClassNamingForNameMapper getClassNaming(DiagnosticsHandler diagnosticsHandler, String typeName) {
- registerClassUse(diagnosticsHandler, Reference.classFromTypeName(typeName));
- return getClassNameMapper(diagnosticsHandler).getClassNaming(typeName);
- }
-
- @Override
- String getSourceFileForClass(DiagnosticsHandler diagnosticsHandler, String typeName) {
- // Getting source file should not trigger new fetches of partitions so we are not calling
- // register here.
- return getClassNameMapper(diagnosticsHandler).getSourceFile(typeName);
- }
-
- private ClassNameMapper getClassNameMapper(DiagnosticsHandler diagnosticsHandler) {
+ public RetracerImpl createRetracer(DiagnosticsHandler diagnosticsHandler) {
MappingPartitionMetadataInternal metadata = getMetadata(diagnosticsHandler);
if (!pendingKeys.isEmpty()) {
prepare.prepare();
@@ -113,26 +119,7 @@
if (classNameMapper == null) {
classNameMapper = ClassNameMapper.builder().build();
}
- return classNameMapper;
- }
-
- @Override
- public PartitionMappingSupplier registerClassUse(
- DiagnosticsHandler diagnosticsHandler, ClassReference classReference) {
- registerKeyUse(getMetadata(diagnosticsHandler).getKey(classReference));
- return this;
- }
-
- private void registerKeyUse(String key) {
- if (!builtKeys.contains(key) && pendingKeys.add(key)) {
- registerPartitionCallback.register(key);
- }
- }
-
- @Override
- public void verifyMappingFileHash(DiagnosticsHandler diagnosticsHandler) {
- String errorMessage = "Cannot verify map file hash for partitions";
- diagnosticsHandler.error(new StringDiagnostic(errorMessage));
- throw new RuntimeException(errorMessage);
+ return RetracerImpl.createInternal(
+ MappingSupplierInternalImpl.createInternal(classNameMapper), diagnosticsHandler);
}
}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/ProguardMappingSupplierBuilderImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/ProguardMappingSupplierBuilderImpl.java
index 8cea599..ca8f08b 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/ProguardMappingSupplierBuilderImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/ProguardMappingSupplierBuilderImpl.java
@@ -11,6 +11,7 @@
private ProguardMapProducer proguardMapProducer;
private boolean allowExperimental = false;
+ private boolean loadAllDefinitions = true;
@Override
public ProguardMappingSupplier.Builder self() {
@@ -31,7 +32,14 @@
}
@Override
+ public ProguardMappingSupplier.Builder setLoadAllDefinitions(boolean loadAllDefinitions) {
+ this.loadAllDefinitions = loadAllDefinitions;
+ return self();
+ }
+
+ @Override
public ProguardMappingSupplier build() {
- return new ProguardMappingSupplierImpl(proguardMapProducer, allowExperimental);
+ return new ProguardMappingSupplierImpl(
+ proguardMapProducer, allowExperimental, loadAllDefinitions);
}
}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/ProguardMappingSupplierImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/ProguardMappingSupplierImpl.java
index 632ca66..14f41ed 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/ProguardMappingSupplierImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/ProguardMappingSupplierImpl.java
@@ -6,7 +6,6 @@
import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.naming.ClassNameMapper;
-import com.android.tools.r8.naming.ClassNamingForNameMapper;
import com.android.tools.r8.naming.LineReader;
import com.android.tools.r8.naming.MapVersion;
import com.android.tools.r8.naming.ProguardMapSupplier.ProguardMapChecker;
@@ -35,86 +34,32 @@
*/
public class ProguardMappingSupplierImpl extends ProguardMappingSupplier {
- private final ProguardMapProducer proguardMapProducer;
+ private ProguardMapProducer proguardMapProducer;
private final boolean allowExperimental;
+ private final boolean loadAllDefinitions;
private ClassNameMapper classNameMapper;
private final Set<String> pendingClassMappings = new HashSet<>();
- private final Set<String> builtClassMappings;
+ private final Set<String> builtClassMappings = new HashSet<>();
public ProguardMappingSupplierImpl(ClassNameMapper classNameMapper) {
this.classNameMapper = classNameMapper;
this.proguardMapProducer = null;
this.allowExperimental = true;
- builtClassMappings = null;
+ this.loadAllDefinitions = true;
}
- ProguardMappingSupplierImpl(ProguardMapProducer proguardMapProducer, boolean allowExperimental) {
+ ProguardMappingSupplierImpl(
+ ProguardMapProducer proguardMapProducer,
+ boolean allowExperimental,
+ boolean loadAllDefinitions) {
this.proguardMapProducer = proguardMapProducer;
this.allowExperimental = allowExperimental;
- builtClassMappings = proguardMapProducer.isFileBacked() ? new HashSet<>() : null;
+ this.loadAllDefinitions = loadAllDefinitions;
}
private boolean hasClassMappingFor(String typeName) {
- return builtClassMappings == null || builtClassMappings.contains(typeName);
- }
-
- @Override
- Set<MapVersionMappingInformation> getMapVersions(DiagnosticsHandler diagnosticsHandler) {
- return getClassNameMapper(diagnosticsHandler).getMapVersions();
- }
-
- @Override
- ClassNamingForNameMapper getClassNaming(DiagnosticsHandler diagnosticsHandler, String typeName) {
- if (!hasClassMappingFor(typeName)) {
- pendingClassMappings.add(typeName);
- }
- return getClassNameMapper(diagnosticsHandler).getClassNaming(typeName);
- }
-
- @Override
- String getSourceFileForClass(DiagnosticsHandler diagnosticsHandler, String typeName) {
- return getClassNameMapper(diagnosticsHandler).getSourceFile(typeName);
- }
-
- private ClassNameMapper getClassNameMapper(DiagnosticsHandler diagnosticsHandler) {
- if (classNameMapper != null && pendingClassMappings.isEmpty()) {
- return classNameMapper;
- }
- if (classNameMapper != null && !proguardMapProducer.isFileBacked()) {
- throw new RuntimeException("Cannot re-open a proguard map producer that is not file backed");
- }
- try {
- Predicate<String> buildForClass =
- builtClassMappings == null ? null : pendingClassMappings::contains;
- boolean readPreambleAndSourceFile = classNameMapper == null;
- LineReader reader =
- proguardMapProducer.isFileBacked()
- ? new ProguardMapReaderWithFilteringMappedBuffer(
- proguardMapProducer.getPath(), buildForClass, readPreambleAndSourceFile)
- : new ProguardMapReaderWithFilteringInputBuffer(
- proguardMapProducer.get(), buildForClass, readPreambleAndSourceFile);
- ClassNameMapper classNameMapper =
- ClassNameMapper.mapperFromLineReaderWithFiltering(
- reader, getMapVersion(), diagnosticsHandler, true, allowExperimental);
- this.classNameMapper = classNameMapper.combine(this.classNameMapper);
- if (builtClassMappings != null) {
- builtClassMappings.addAll(pendingClassMappings);
- }
- pendingClassMappings.clear();
- } catch (Exception e) {
- throw new InvalidMappingFileException(e);
- }
- return classNameMapper;
- }
-
- private MapVersion getMapVersion() {
- if (classNameMapper == null) {
- return MapVersion.MAP_VERSION_NONE;
- } else {
- MapVersionMappingInformation mapVersion = classNameMapper.getFirstMapVersionInformation();
- return mapVersion == null ? MapVersion.MAP_VERSION_UNKNOWN : mapVersion.getMapVersion();
- }
+ return loadAllDefinitions || builtClassMappings.contains(typeName);
}
@Override
@@ -144,4 +89,57 @@
throw new RuntimeException(e);
}
}
+
+ @Override
+ public Set<MapVersionMappingInformation> getMapVersions(DiagnosticsHandler diagnosticsHandler) {
+ if (classNameMapper == null) {
+ createRetracer(diagnosticsHandler);
+ }
+ assert classNameMapper != null;
+ return classNameMapper.getMapVersions();
+ }
+
+ @Override
+ public RetracerImpl createRetracer(DiagnosticsHandler diagnosticsHandler) {
+ if (proguardMapProducer == null) {
+ assert classNameMapper != null;
+ return RetracerImpl.createInternal(
+ MappingSupplierInternalImpl.createInternal(classNameMapper), diagnosticsHandler);
+ }
+ if (classNameMapper == null || !pendingClassMappings.isEmpty()) {
+ try {
+ Predicate<String> buildForClass =
+ loadAllDefinitions ? null : pendingClassMappings::contains;
+ boolean readPreambleAndSourceFile = classNameMapper == null;
+ LineReader reader =
+ proguardMapProducer.isFileBacked()
+ ? new ProguardMapReaderWithFilteringMappedBuffer(
+ proguardMapProducer.getPath(), buildForClass, readPreambleAndSourceFile)
+ : new ProguardMapReaderWithFilteringInputBuffer(
+ proguardMapProducer.get(), buildForClass, readPreambleAndSourceFile);
+ classNameMapper =
+ ClassNameMapper.mapperFromLineReaderWithFiltering(
+ reader, getMapVersion(), diagnosticsHandler, true, allowExperimental)
+ .combine(classNameMapper);
+ builtClassMappings.addAll(pendingClassMappings);
+ pendingClassMappings.clear();
+ } catch (Exception e) {
+ throw new InvalidMappingFileException(e);
+ }
+ }
+ if (loadAllDefinitions) {
+ proguardMapProducer = null;
+ }
+ return RetracerImpl.createInternal(
+ MappingSupplierInternalImpl.createInternal(classNameMapper), diagnosticsHandler);
+ }
+
+ private MapVersion getMapVersion() {
+ if (classNameMapper == null) {
+ return MapVersion.MAP_VERSION_NONE;
+ } else {
+ MapVersionMappingInformation mapVersion = classNameMapper.getFirstMapVersionInformation();
+ return mapVersion == null ? MapVersion.MAP_VERSION_UNKNOWN : mapVersion.getMapVersion();
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetracerImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetracerImpl.java
index daec745..5b93dec 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetracerImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetracerImpl.java
@@ -5,18 +5,14 @@
package com.android.tools.r8.retrace.internal;
import com.android.tools.r8.DiagnosticsHandler;
-import com.android.tools.r8.naming.mappinginformation.MapVersionMappingInformation;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.FieldReference;
import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.references.TypeReference;
-import com.android.tools.r8.retrace.MappingSupplier;
import com.android.tools.r8.retrace.RetraceFrameResult;
import com.android.tools.r8.retrace.RetraceStackTraceContext;
import com.android.tools.r8.retrace.Retracer;
-import com.android.tools.r8.retrace.RetracerBuilder;
import java.util.OptionalInt;
-import java.util.Set;
/** A default implementation for the retrace api using the ClassNameMapper defined in R8. */
public class RetracerImpl implements Retracer {
@@ -70,9 +66,7 @@
@Override
public RetraceClassResultImpl retraceClass(ClassReference classReference) {
return RetraceClassResultImpl.create(
- classReference,
- classNameMapperSupplier.getClassNaming(diagnosticsHandler, classReference.getTypeName()),
- this);
+ classReference, classNameMapperSupplier.getClassNaming(classReference.getTypeName()), this);
}
@Override
@@ -85,41 +79,12 @@
return retraceClass(exception).lookupThrownException(RetraceStackTraceContext.empty());
}
- public Set<MapVersionMappingInformation> getMapVersions() {
- return classNameMapperSupplier.getMapVersions(diagnosticsHandler);
- }
-
public String getSourceFile(ClassReference classReference) {
- return classNameMapperSupplier.getSourceFileForClass(
- diagnosticsHandler, classReference.getTypeName());
+ return classNameMapperSupplier.getSourceFileForClass(classReference.getTypeName());
}
- public static Builder builder() {
- return new Builder();
- }
-
- public static class Builder implements RetracerBuilder {
-
- private MappingSupplier mappingSupplier;
- private DiagnosticsHandler diagnosticsHandler = new DiagnosticsHandler() {};
-
- private Builder() {}
-
- @Override
- public Builder setMappingSupplier(MappingSupplier mappingSupplier) {
- this.mappingSupplier = mappingSupplier;
- return this;
- }
-
- @Override
- public Builder setDiagnosticsHandler(DiagnosticsHandler diagnosticsHandler) {
- this.diagnosticsHandler = diagnosticsHandler;
- return this;
- }
-
- @Override
- public RetracerImpl build() {
- return new RetracerImpl(mappingSupplier, diagnosticsHandler);
- }
+ public static RetracerImpl createInternal(
+ MappingSupplierInternal classNameMapperSupplier, DiagnosticsHandler diagnosticsHandler) {
+ return new RetracerImpl(classNameMapperSupplier, diagnosticsHandler);
}
}
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 1df8107..4d9598e 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -1346,24 +1346,17 @@
void traceMethodHandle(
DexMethodHandle methodHandle, MethodHandleUse use, ProgramMethod currentMethod) {
- // If a method handle is not an argument to a lambda metafactory it could flow to a
- // MethodHandle.invokeExact invocation. For that to work, the receiver type cannot have
- // changed and therefore we cannot perform member rebinding. For these handles, we maintain
- // the receiver for the method handle. Therefore, we have to make sure that the receiver
- // stays in the output (and is not class merged). To ensure that we treat the receiver
- // as instantiated.
if (methodHandle.isMethodHandle() && use != MethodHandleUse.ARGUMENT_TO_LAMBDA_METAFACTORY) {
- DexType type = methodHandle.asMethod().holder;
- DexProgramClass clazz = getProgramClassOrNull(type, currentMethod);
- if (clazz != null) {
- KeepReason reason = KeepReason.methodHandleReferencedIn(currentMethod);
- if (clazz.isAnnotation()) {
- markTypeAsLive(clazz, graphReporter.registerClass(clazz, reason));
- } else if (clazz.isInterface()) {
- markInterfaceAsInstantiated(clazz, graphReporter.registerInterface(clazz, reason));
- } else {
- workList.enqueueMarkInstantiatedAction(
- clazz, null, InstantiationReason.REFERENCED_IN_METHOD_HANDLE, reason);
+ KeepReason reason = KeepReason.methodHandleReferencedIn(currentMethod);
+ MethodResolutionResult result =
+ resolveMethod(methodHandle.asMethod(), currentMethod, reason, methodHandle.isInterface);
+ if (result.isSingleResolution()) {
+ DexClassAndMethod target = result.asSingleResolution().getResolutionPair();
+ if (target.isProgramMethod()) {
+ // If the method handle is targeting a program method then the structure of the method
+ // must remain, so that invoke/invokeExact dispatches will continue to match.
+ applyMinimumKeepInfoWhenLiveOrTargeted(
+ target.asProgramMethod(), KeepMethodInfo.newEmptyJoiner().disallowOptimization());
}
}
}
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 78547de..0c68c28 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -23,6 +23,7 @@
import com.android.tools.r8.androidapi.ComputedApiLevel;
import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.debuginfo.DebugRepresentation;
+import com.android.tools.r8.dex.ApplicationReader.ProgramClassConflictResolver;
import com.android.tools.r8.dex.Marker;
import com.android.tools.r8.dex.Marker.Backend;
import com.android.tools.r8.dex.Marker.Tool;
@@ -170,6 +171,8 @@
// TODO(zerny): Make this private-final once we have full program-consumer support.
public ProgramConsumer programConsumer = null;
+ public ProgramClassConflictResolver programClassConflictResolver = null;
+
private GlobalSyntheticsConsumer globalSyntheticsConsumer = null;
public DataResourceConsumer dataResourceConsumer;
@@ -372,6 +375,9 @@
public boolean enableRedundantConstNumberOptimization = false;
public boolean enableLoopUnrolling = true;
+ // TODO(b/237567012): Remove when resolved.
+ public boolean enableCheckAllInstructionsDuringStackMapVerification = false;
+
public String synthesizedClassPrefix = "";
// Number of threads to use while processing the dex files.
@@ -397,14 +403,15 @@
// Boolean value indicating that byte code pass through may be enabled.
public boolean enableCfByteCodePassThrough = false;
- // TODO(b/238175192): remove again when resolved
- public boolean enableUnrepresentableInDexInstructionRemoval = false;
-
// Flag to control the representation of stateless lambdas.
// See b/222081665 for context.
public boolean createSingletonsForStatelessLambdas =
System.getProperty("com.android.tools.r8.createSingletonsForStatelessLambdas") != null;
+ // Flag to allow record annotations in DEX. See b/231930852 for context.
+ public boolean emitRecordAnnotationsInDex =
+ System.getProperty("com.android.tools.r8.emitRecordAnnotationsInDex") != null;
+
// Flag to allow nest annotations in DEX. See b/231930852 for context.
public boolean emitNestAnnotationsInDex =
System.getProperty("com.android.tools.r8.emitNestAnnotationsInDex") != null;
@@ -2148,7 +2155,7 @@
}
public boolean canUseRecords() {
- return hasFeaturePresentFrom(null);
+ return hasFeaturePresentFrom(null) || emitRecordAnnotationsInDex;
}
public boolean canUseSealedClasses() {
diff --git a/src/main/keep.txt b/src/main/keep.txt
index deb8e6d..ced5b4a 100644
--- a/src/main/keep.txt
+++ b/src/main/keep.txt
@@ -24,7 +24,6 @@
-keepattributes SourceFile, LineNumberTable, InnerClasses, EnclosingMethod, Exceptions, Signature, RuntimeInvisibleAnnotations
-keepparameternames
--keeppackagenames com.android.tools.r8
-repackageclasses com.android.tools.r8.internal
# Compatibility command line program used by the Android Platform build.
diff --git a/src/test/examplesAndroidO/invokepolymorphic/InvokePolymorphic.java b/src/test/examplesAndroidO/invokepolymorphic/InvokePolymorphic.java
deleted file mode 100644
index 90cb064..0000000
--- a/src/test/examplesAndroidO/invokepolymorphic/InvokePolymorphic.java
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright (c) 2017, 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 invokepolymorphic;
-
-import java.lang.invoke.MethodHandle;
-import java.lang.invoke.MethodHandles;
-import java.lang.invoke.MethodType;
-
-class Data {
-}
-
-public class InvokePolymorphic {
-
- public String buildString(Integer i1, int i2, String s) {
- return (i1 == null ? "N" : "!N") + "-" + i2 + "-" + s;
- }
-
- public void testInvokePolymorphic() {
- MethodType mt = MethodType.methodType(String.class, Integer.class, int.class, String.class);
- MethodHandles.Lookup lk = MethodHandles.lookup();
-
- try {
- MethodHandle mh = lk.findVirtual(getClass(), "buildString", mt);
- System.out.println(mh.invoke(this, null, 1, "string"));
- } catch (Throwable t) {
- t.printStackTrace();
- }
- }
-
- public String buildString(
- byte b, char c, short s, float f, double d, long l, Integer i1, int i2, String str) {
- return b + "-" + c + "-" + s + "-" + f + "-" + d + "-" + l + "-" + (i1 == null ? "N" : "!N")
- + "-" + i2 + "-" + str;
- }
-
- public void testInvokePolymorphicRange() {
- MethodType mt = MethodType.methodType(String.class, byte.class, char.class, short.class,
- float.class, double.class, long.class, Integer.class, int.class, String.class);
- MethodHandles.Lookup lk = MethodHandles.lookup();
-
- try {
- MethodHandle mh = lk.findVirtual(getClass(), "buildString", mt);
- System.out.println(
- mh.invoke(this, (byte) 2, 'a', (short) 0xFFFF, 1.1f, 2.24d, 12345678L, null,
- 1, "string"));
- } catch (Throwable t) {
- t.printStackTrace();
- }
- }
-
- public static void testWithAllTypes(
- boolean z, char a, short b, int c, long d, float e, double f, String g, Object h) {
- System.out.println(z);
- System.out.println(a);
- System.out.println(b);
- System.out.println(c);
- System.out.println(d);
- System.out.println(e);
- System.out.println(f);
- System.out.println(g);
- System.out.println(h);
- }
-
- public void testInvokePolymorphicWithAllTypes() {
- try {
- MethodHandle mth =
- MethodHandles.lookup()
- .findStatic(
- InvokePolymorphic.class,
- "testWithAllTypes",
- MethodType.methodType(
- void.class, boolean.class, char.class, short.class, int.class, long.class,
- float.class, double.class, String.class, Object.class));
- mth.invokeExact(false,'h', (short) 56, 72, Integer.MAX_VALUE + 42l,
- 0.56f, 100.0d, "hello", (Object) "goodbye");
- } catch (Throwable t) {
- t.printStackTrace();
- }
- }
-
- public MethodHandle testInvokePolymorphicWithConstructor() {
- MethodHandle mh = null;
- MethodType mt = MethodType.methodType(void.class);
- MethodHandles.Lookup lk = MethodHandles.lookup();
-
- try {
- mh = lk.findConstructor(Data.class, mt);
- System.out.println(mh.invoke().getClass() == Data.class);
- } catch (Throwable t) {
- t.printStackTrace();
- }
-
- return mh;
- }
-
- public static void main(String[] args) {
- InvokePolymorphic invokePolymorphic = new InvokePolymorphic();
- invokePolymorphic.testInvokePolymorphic();
- invokePolymorphic.testInvokePolymorphicRange();
- invokePolymorphic.testInvokePolymorphicWithAllTypes();
- invokePolymorphic.testInvokePolymorphicWithConstructor();
- }
-}
diff --git a/src/test/java/com/android/tools/r8/CommandTestBase.java b/src/test/java/com/android/tools/r8/CommandTestBase.java
index 1c0c588..ed755f9 100644
--- a/src/test/java/com/android/tools/r8/CommandTestBase.java
+++ b/src/test/java/com/android/tools/r8/CommandTestBase.java
@@ -8,7 +8,6 @@
import static org.junit.Assert.fail;
import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion.AppForSpecConversion;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Timing;
import com.google.common.collect.ImmutableList;
@@ -313,13 +312,7 @@
InternalOptions options = command.getInternalOptions();
LibraryDesugaringSpecification spec = LibraryDesugaringSpecification.JDK11;
options.loadMachineDesugaredLibrarySpecification(
- Timing.empty(),
- AppForSpecConversion.readAppForTesting(
- libraryCompilation ? spec.getDesugarJdkLibs() : null,
- spec.getLibraryFiles(),
- options,
- libraryCompilation,
- Timing.empty()));
+ Timing.empty(), spec.getAppForTesting(options, libraryCompilation));
return options;
}
diff --git a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
index 7fe1ae0..c2c74a4 100644
--- a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
@@ -213,9 +213,7 @@
abstract void build(Path inputFile, Path out, OutputMode mode) throws Throwable;
}
- private static List<String> minSdkErrorExpected =
- ImmutableList.of(
- "invokepolymorphic-error-due-to-min-sdk", "invokecustom-error-due-to-min-sdk");
+ private static List<String> minSdkErrorExpected = ImmutableList.of();
private static Map<DexVm.Version, List<String>> failsOn;
@@ -229,7 +227,6 @@
"paramnames",
"repeat_annotations_new_api",
// Dex version not supported
- "invokepolymorphic",
"invokecustom",
"invokecustom2",
"DefaultMethodInAndroidJar25",
@@ -244,7 +241,6 @@
"paramnames",
"repeat_annotations_new_api",
// Dex version not supported
- "invokepolymorphic",
"invokecustom",
"invokecustom2",
"DefaultMethodInAndroidJar25",
@@ -259,7 +255,6 @@
"paramnames",
"repeat_annotations_new_api",
// Dex version not supported
- "invokepolymorphic",
"invokecustom",
"invokecustom2",
"DefaultMethodInAndroidJar25",
@@ -274,7 +269,6 @@
"paramnames",
"repeat_annotations_new_api",
// Dex version not supported
- "invokepolymorphic",
"invokecustom",
"invokecustom2",
"DefaultMethodInAndroidJar25",
@@ -288,7 +282,6 @@
// API not supported
"paramnames",
// Dex version not supported
- "invokepolymorphic",
"invokecustom",
"invokecustom2",
"testMissingInterfaceDesugared2AndroidO",
@@ -370,30 +363,6 @@
}
@Test
- public void invokeCustomErrorDueToMinSdk() throws Throwable {
- test("invokecustom-error-due-to-min-sdk", "invokecustom", "InvokeCustom")
- .withMinApiLevel(AndroidApiLevel.N_MR1)
- .withKeepAll()
- .run();
- }
-
- @Test
- public void invokePolymorphic() throws Throwable {
- test("invokepolymorphic", "invokepolymorphic", "InvokePolymorphic")
- .withMinApiLevel(AndroidApiLevel.O)
- .withKeepAll()
- .run();
- }
-
- @Test
- public void invokePolymorphicErrorDueToMinSdk() throws Throwable {
- test("invokepolymorphic-error-due-to-min-sdk", "invokepolymorphic", "InvokePolymorphic")
- .withMinApiLevel(AndroidApiLevel.N_MR1)
- .withKeepAll()
- .run();
- }
-
- @Test
public void lambdaDesugaring() throws Throwable {
test("lambdadesugaring", "lambdadesugaring", "LambdaDesugaring")
.withMinApiLevel(ToolHelper.getMinApiLevelForDexVmNoHigherThan(AndroidApiLevel.K))
diff --git a/src/test/java/com/android/tools/r8/RunExamplesAndroidPTest.java b/src/test/java/com/android/tools/r8/RunExamplesAndroidPTest.java
index caed567..c8b6cc4 100644
--- a/src/test/java/com/android/tools/r8/RunExamplesAndroidPTest.java
+++ b/src/test/java/com/android/tools/r8/RunExamplesAndroidPTest.java
@@ -176,8 +176,7 @@
}
private static List<String> minSdkErrorExpected =
- ImmutableList.of(
- "invokepolymorphic-error-due-to-min-sdk", "invokecustom-error-due-to-min-sdk");
+ ImmutableList.of("invokecustom-error-due-to-min-sdk");
private static Map<DexVm.Version, List<String>> failsOn =
ImmutableMap.<DexVm.Version, List<String>>builder()
diff --git a/src/test/java/com/android/tools/r8/TestBuilder.java b/src/test/java/com/android/tools/r8/TestBuilder.java
index 2b29085..4150236 100644
--- a/src/test/java/com/android/tools/r8/TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestBuilder.java
@@ -251,12 +251,6 @@
return self();
}
- public T mapUnsupportedFeaturesToWarnings() {
- return setDiagnosticsLevelModifier(
- (level, diagnostic) ->
- diagnostic instanceof UnsupportedFeatureDiagnostic ? DiagnosticsLevel.WARNING : level);
- }
-
public T allowStdoutMessages() {
// Default ignored.
return self();
diff --git a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
index 9072aa3..e1de2d3 100644
--- a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
@@ -105,6 +105,10 @@
return addKeepRules(Arrays.asList(rules));
}
+ public T addDontObfuscate() {
+ return addKeepRules("-dontobfuscate");
+ }
+
public T addDontObfuscate(Class<?> clazz) {
return addKeepRules(
"-keep,allowaccessmodification,allowannotationremoval,allowoptimization,allowshrinking"
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 69f2167..88b929a 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -168,9 +168,11 @@
Paths.get(LIBS_DIR, "r8_no_manifest_without_deps.jar");
public static final Path R8_WITH_RELOCATED_DEPS_JAR =
Paths.get(LIBS_DIR, "r8_with_relocated_deps.jar");
- public static final Path R8_WITH_DEPS_11_JAR = Paths.get(LIBS_DIR, "r8_with_deps_11.jar");
public static final Path R8_WITH_RELOCATED_DEPS_11_JAR =
Paths.get(LIBS_DIR, "r8_with_relocated_deps_11.jar");
+ public static final Path R8_WITH_DEPS_17_JAR = Paths.get(LIBS_DIR, "r8_with_deps_17.jar");
+ public static final Path R8_WITH_RELOCATED_DEPS_17_JAR =
+ Paths.get(LIBS_DIR, "r8_with_relocated_deps_17.jar");
public static final Path R8LIB_JAR = Paths.get(LIBS_DIR, "r8lib.jar");
public static final Path R8LIB_MAP = Paths.get(LIBS_DIR, "r8lib.jar.map");
public static final Path R8LIB_EXCLUDE_DEPS_JAR = Paths.get(LIBS_DIR, "r8lib-exclude-deps.jar");
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockExceptionTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockExceptionTest.java
new file mode 100644
index 0000000..4012ad7
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockExceptionTest.java
@@ -0,0 +1,243 @@
+// 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.apimodel;
+
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.OutputMode;
+import com.android.tools.r8.SingleTestRunResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestCompilerBuilder;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.synthesis.globals.GlobalSyntheticsTestingConsumer;
+import com.android.tools.r8.testing.AndroidBuildVersion;
+import com.android.tools.r8.utils.AndroidApiLevel;
+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;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ApiModelMockExceptionTest extends TestBase {
+
+ private final AndroidApiLevel mockSuperExceptionLevel = AndroidApiLevel.M;
+ private final AndroidApiLevel mockSubExceptionLevel = AndroidApiLevel.P;
+
+ @Parameter public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ private boolean isGreaterOrEqualToSuperMockLevel() {
+ return parameters.isDexRuntime()
+ && parameters.getApiLevel().isGreaterThanOrEqualTo(mockSuperExceptionLevel);
+ }
+
+ private boolean isGreaterOrEqualToSubMockLevel() {
+ return parameters.isDexRuntime()
+ && parameters.getApiLevel().isGreaterThanOrEqualTo(mockSubExceptionLevel);
+ }
+
+ private void setupTestCompileBuilder(TestCompilerBuilder<?, ?, ?, ?, ?> testBuilder) {
+ testBuilder
+ .addProgramClasses(Main.class)
+ .addLibraryClasses(LibrarySuperException.class, LibrarySubException.class, Thrower.class)
+ .addDefaultRuntimeLibrary(parameters)
+ .setMinApi(parameters.getApiLevel())
+ .apply(ApiModelingTestHelper::enableStubbingOfClasses)
+ .apply(setMockApiLevelForClass(LibrarySuperException.class, mockSuperExceptionLevel))
+ .apply(setMockApiLevelForClass(LibrarySubException.class, mockSubExceptionLevel));
+ }
+
+ private void setupTestRuntimeBuilder(TestCompilerBuilder<?, ?, ?, ?, ?> testBuilder) {
+ testBuilder.setMinApi(parameters.getApiLevel()).addAndroidBuildVersion();
+ }
+
+ private void setupTestBuilder(TestCompilerBuilder<?, ?, ?, ?, ?> testBuilder) {
+ setupTestCompileBuilder(testBuilder);
+ setupTestRuntimeBuilder(testBuilder);
+ }
+
+ private boolean addSuperToBootClasspath() {
+ return parameters.isCfRuntime()
+ || parameters
+ .getRuntime()
+ .maxSupportedApiLevel()
+ .isGreaterThanOrEqualTo(mockSuperExceptionLevel);
+ }
+
+ private boolean addSubToBootClasspath() {
+ return parameters.isCfRuntime()
+ || parameters
+ .getRuntime()
+ .maxSupportedApiLevel()
+ .isGreaterThanOrEqualTo(mockSubExceptionLevel);
+ }
+
+ @Test
+ public void testD8Debug() throws Exception {
+ assumeTrue(parameters.isDexRuntime());
+ testForD8()
+ .setMode(CompilationMode.DEBUG)
+ .apply(this::setupTestBuilder)
+ .compile()
+ .addBootClasspathClasses(Thrower.class)
+ .applyIf(
+ addSuperToBootClasspath(), b -> b.addBootClasspathClasses(LibrarySuperException.class))
+ .applyIf(addSubToBootClasspath(), b -> b.addBootClasspathClasses(LibrarySubException.class))
+ .run(parameters.getRuntime(), Main.class)
+ .apply(this::checkOutput);
+ }
+
+ @Test
+ public void testD8Release() throws Exception {
+ assumeTrue(parameters.isDexRuntime());
+ testForD8()
+ .setMode(CompilationMode.RELEASE)
+ .apply(this::setupTestBuilder)
+ .compile()
+ .addBootClasspathClasses(Thrower.class)
+ .applyIf(
+ addSuperToBootClasspath(), b -> b.addBootClasspathClasses(LibrarySuperException.class))
+ .applyIf(addSubToBootClasspath(), b -> b.addBootClasspathClasses(LibrarySubException.class))
+ .run(parameters.getRuntime(), Main.class)
+ .apply(this::checkOutput);
+ }
+
+ @Test
+ public void testD8MergeIndexed() throws Exception {
+ assumeTrue(parameters.isDexRuntime());
+ testD8Merge(OutputMode.DexIndexed);
+ }
+
+ @Test
+ public void testD8MergeFilePerClass() throws Exception {
+ assumeTrue(parameters.isDexRuntime());
+ testD8Merge(OutputMode.DexFilePerClass);
+ }
+
+ @Test
+ public void testD8MergeFilePerClassFile() throws Exception {
+ assumeTrue(parameters.isDexRuntime());
+ testD8Merge(OutputMode.DexFilePerClassFile);
+ }
+
+ public void testD8Merge(OutputMode outputMode) throws Exception {
+ GlobalSyntheticsTestingConsumer globals = new GlobalSyntheticsTestingConsumer();
+ Path incrementalOut =
+ testForD8()
+ .debug()
+ .setOutputMode(outputMode)
+ .setIntermediate(true)
+ .apply(b -> b.getBuilder().setGlobalSyntheticsConsumer(globals))
+ .apply(this::setupTestCompileBuilder)
+ .compile()
+ .writeToZip();
+
+ if (isGreaterOrEqualToSubMockLevel()) {
+ assertFalse(globals.hasGlobals());
+ } else if (outputMode == OutputMode.DexIndexed) {
+ assertTrue(globals.hasGlobals());
+ assertTrue(globals.isSingleGlobal());
+ } else {
+ assertTrue(globals.hasGlobals());
+ // The Main class reference the mock and should have globals.
+ assertNotNull(globals.getProvider(Reference.classFromClass(Main.class)));
+ }
+
+ testForD8()
+ .debug()
+ .addProgramFiles(incrementalOut)
+ .apply(b -> b.getBuilder().addGlobalSyntheticsResourceProviders(globals.getProviders()))
+ .apply(this::setupTestRuntimeBuilder)
+ .compile()
+ .addBootClasspathClasses(Thrower.class)
+ .applyIf(
+ addSuperToBootClasspath(), b -> b.addBootClasspathClasses(LibrarySuperException.class))
+ .applyIf(addSubToBootClasspath(), b -> b.addBootClasspathClasses(LibrarySubException.class))
+ .run(parameters.getRuntime(), Main.class)
+ .apply(this::checkOutput);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .apply(this::setupTestBuilder)
+ .addKeepMainRule(Main.class)
+ .compile()
+ .addBootClasspathClasses(Thrower.class)
+ .applyIf(
+ addSuperToBootClasspath(), b -> b.addBootClasspathClasses(LibrarySuperException.class))
+ .applyIf(addSubToBootClasspath(), b -> b.addBootClasspathClasses(LibrarySubException.class))
+ .run(parameters.getRuntime(), Main.class)
+ .apply(this::checkOutput)
+ .inspect(this::inspect);
+ }
+
+ private void inspect(CodeInspector inspector) {
+ verifyThat(inspector, parameters, LibrarySuperException.class)
+ .stubbedUntil(mockSuperExceptionLevel);
+ verifyThat(inspector, parameters, LibrarySubException.class)
+ .stubbedUntil(mockSubExceptionLevel);
+ }
+
+ private void checkOutput(SingleTestRunResult<?> runResult) {
+ if (isGreaterOrEqualToSubMockLevel()) {
+ runResult.assertSuccessWithOutputLines("Caught LibrarySubException");
+ } else if (isGreaterOrEqualToSuperMockLevel()) {
+ runResult.assertSuccessWithOutputLines("Caught LibrarySuperException");
+ } else {
+ runResult.assertSuccessWithOutputLines("Caught Exception");
+ }
+ }
+
+ // Only present from api level M.
+ public static class LibrarySuperException extends Exception {}
+
+ // Only present from api level P.
+ public static class LibrarySubException extends LibrarySuperException {}
+
+ // Only present from api level P.
+ public static class Thrower {
+
+ public static void test(int apiVersion) throws Exception {
+ if (apiVersion >= 28) {
+ throw new LibrarySubException();
+ } else if (apiVersion >= 23) {
+ throw new LibrarySuperException();
+ } else {
+ throw new Exception();
+ }
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ try {
+ Thrower.test(AndroidBuildVersion.VERSION);
+ } catch (LibrarySubException e) {
+ System.out.println("Caught LibrarySubException");
+ } catch (LibrarySuperException e) {
+ System.out.println("Caught LibrarySuperException");
+ } catch (Exception e) {
+ System.out.println("Caught Exception");
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/BridgeToPackagePrivateMethodTest.java b/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/BridgeToPackagePrivateMethodTest.java
new file mode 100644
index 0000000..10fc6da
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/BridgeToPackagePrivateMethodTest.java
@@ -0,0 +1,120 @@
+// 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.bridgeremoval.hoisting;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class BridgeToPackagePrivateMethodTest extends TestBase {
+
+ private static final List<String> EXPECTED_OUTPUT = ImmutableList.of("A", "B");
+ private static final String TRANSFORMED_A_DESCRIPTOR = "LA;";
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testRuntime() throws Exception {
+ testForRuntime(parameters)
+ .addProgramClassFileData(
+ transformer(Main.class)
+ .replaceClassDescriptorInMethodInstructions(
+ descriptor(A.class), TRANSFORMED_A_DESCRIPTOR)
+ .transform(),
+ transformer(A.class).setClassDescriptor(TRANSFORMED_A_DESCRIPTOR).transform(),
+ transformer(B.class)
+ .setSuper(TRANSFORMED_A_DESCRIPTOR)
+ .replaceClassDescriptorInMethodInstructions(
+ descriptor(A.class), TRANSFORMED_A_DESCRIPTOR)
+ .setBridge(B.class.getDeclaredMethod("bridge"))
+ .transform())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED_OUTPUT);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClassFileData(
+ transformer(Main.class)
+ .replaceClassDescriptorInMethodInstructions(
+ descriptor(A.class), TRANSFORMED_A_DESCRIPTOR)
+ .transform(),
+ transformer(A.class).setClassDescriptor(TRANSFORMED_A_DESCRIPTOR).transform(),
+ transformer(B.class)
+ .setSuper(TRANSFORMED_A_DESCRIPTOR)
+ .replaceClassDescriptorInMethodInstructions(
+ descriptor(A.class), TRANSFORMED_A_DESCRIPTOR)
+ .setBridge(B.class.getDeclaredMethod("bridge"))
+ .transform())
+ .addKeepMainRule(Main.class)
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .enableNoVerticalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ // TODO(b/233866639): Should succeed with "A", "B".
+ .applyIf(
+ parameters.isDexRuntime() && parameters.getDexRuntimeVersion().isDalvik(),
+ runResult -> runResult.assertSuccessWithOutputLines(EXPECTED_OUTPUT),
+ runResult -> runResult.assertSuccessWithOutputLines("A", "A"));
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ new A().callM();
+ new B().bridge();
+ }
+ }
+
+ @NeverClassInline
+ @NoVerticalClassMerging
+ public static class /*otherpackage.*/ A {
+
+ @NeverInline
+ void m() {
+ System.out.println("A");
+ }
+
+ @NeverInline
+ public void callM() {
+ m();
+ }
+ }
+
+ @NeverClassInline
+ static class B extends A {
+
+ @NeverInline
+ void m() {
+ System.out.println("B");
+ }
+
+ // Not eligible for bridge hoisting, as the bridge will then dispatch to A.m instead of B.m.
+ @NeverInline
+ public /*bridge*/ void bridge() {
+ this.m();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/cf/CfDebugLocalStackMapVerificationTest.java b/src/test/java/com/android/tools/r8/cf/CfDebugLocalStackMapVerificationTest.java
new file mode 100644
index 0000000..32ce06a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/CfDebugLocalStackMapVerificationTest.java
@@ -0,0 +1,996 @@
+// 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.cf;
+
+import static org.junit.Assert.assertThrows;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+@RunWith(Parameterized.class)
+/** This is a regresson test for b/237567012 */
+public class CfDebugLocalStackMapVerificationTest extends TestBase {
+
+ @Parameter() public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withCfRuntimes().build();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ // TODO(b/237567012): We should not fail compilation.
+ assertThrows(
+ CompilationFailedException.class,
+ () ->
+ testForR8(parameters.getBackend())
+ .addProgramClassFileData(CfDebugLocalStackMapVerificationTest$MainDump.dump())
+ .addKeepMainRule(Main.class)
+ .setMode(CompilationMode.DEBUG)
+ .addDontWarn("*")
+ // TODO(b/237567012): Remove option when resolved.
+ .addOptionsModification(
+ options -> options.enableCheckAllInstructionsDuringStackMapVerification = true)
+ .compile()
+ .run(parameters.getRuntime(), Main.class));
+ }
+
+ public static class Main {
+
+ // InvokeSuspend is taken from an input program.jar and copied verbatim in the dump below.
+ public Object invokeSuspend(Object o) {
+ return o;
+ }
+
+ public static void main(String[] args) {
+ new Main().invokeSuspend(null);
+ }
+ }
+
+ public static class CfDebugLocalStackMapVerificationTest$MainDump implements Opcodes {
+
+ public static byte[] dump() throws Exception {
+
+ ClassWriter classWriter = new ClassWriter(0);
+ FieldVisitor fieldVisitor;
+ MethodVisitor methodVisitor;
+ AnnotationVisitor annotationVisitor0;
+
+ classWriter.visit(
+ V1_8,
+ ACC_FINAL | ACC_SUPER,
+ binaryName(Main.class),
+ null,
+ "kotlin/coroutines/jvm/internal/SuspendLambda",
+ new String[] {"kotlin/jvm/functions/Function2"});
+
+ {
+ fieldVisitor = classWriter.visitField(0, "L$1", "Ljava/lang/Object;", null, null);
+ fieldVisitor.visitEnd();
+ }
+ {
+ fieldVisitor = classWriter.visitField(0, "L$2", "Ljava/lang/Object;", null, null);
+ fieldVisitor.visitEnd();
+ }
+ {
+ fieldVisitor = classWriter.visitField(0, "label", "I", null, null);
+ fieldVisitor.visitEnd();
+ }
+ {
+ fieldVisitor =
+ classWriter.visitField(
+ ACC_PRIVATE | ACC_SYNTHETIC, "L$0", "Ljava/lang/Object;", null, null);
+ fieldVisitor.visitEnd();
+ }
+ {
+ fieldVisitor =
+ classWriter.visitField(
+ ACC_FINAL | ACC_SYNTHETIC,
+ "this$0",
+ "Lio/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber;",
+ null,
+ null);
+ fieldVisitor.visitEnd();
+ }
+ {
+ methodVisitor =
+ classWriter.visitMethod(
+ 0,
+ "<init>",
+ "(Lio/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber;Lkotlin/coroutines/Continuation;)V",
+ "(Lio/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber;Lkotlin/coroutines/Continuation<-Lio/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1;>;)V",
+ null);
+ methodVisitor.visitCode();
+ Label label0 = new Label();
+ methodVisitor.visitLabel(label0);
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitVarInsn(ALOAD, 1);
+ methodVisitor.visitFieldInsn(
+ PUTFIELD,
+ "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
+ "this$0",
+ "Lio/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber;");
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitInsn(ICONST_2);
+ methodVisitor.visitVarInsn(ALOAD, 2);
+ methodVisitor.visitMethodInsn(
+ INVOKESPECIAL,
+ "kotlin/coroutines/jvm/internal/SuspendLambda",
+ "<init>",
+ "(ILkotlin/coroutines/Continuation;)V",
+ false);
+ methodVisitor.visitInsn(RETURN);
+ Label label1 = new Label();
+ methodVisitor.visitLabel(label1);
+ methodVisitor.visitLocalVariable(
+ "this",
+ "Lio/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1;",
+ null,
+ label0,
+ label1,
+ 0);
+ methodVisitor.visitLocalVariable(
+ "$receiver",
+ "Lio/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber;",
+ null,
+ label0,
+ label1,
+ 1);
+ methodVisitor.visitLocalVariable(
+ "$completion", "Lkotlin/coroutines/Continuation;", null, label0, label1, 2);
+ methodVisitor.visitMaxs(3, 3);
+ methodVisitor.visitEnd();
+ }
+ {
+ methodVisitor =
+ classWriter.visitMethod(
+ ACC_PUBLIC | ACC_FINAL,
+ "invokeSuspend",
+ "(Ljava/lang/Object;)Ljava/lang/Object;",
+ null,
+ null);
+ {
+ annotationVisitor0 =
+ methodVisitor.visitAnnotation("Lorg/jetbrains/annotations/Nullable;", false);
+ annotationVisitor0.visitEnd();
+ }
+ methodVisitor.visitAnnotableParameterCount(1, false);
+ {
+ annotationVisitor0 =
+ methodVisitor.visitParameterAnnotation(
+ 0, "Lorg/jetbrains/annotations/NotNull;", false);
+ annotationVisitor0.visitEnd();
+ }
+ methodVisitor.visitCode();
+ Label label0 = new Label();
+ Label label1 = new Label();
+ Label label2 = new Label();
+ methodVisitor.visitTryCatchBlock(label0, label1, label2, "java/lang/Throwable");
+ Label label3 = new Label();
+ Label label4 = new Label();
+ methodVisitor.visitTryCatchBlock(label3, label4, label2, "java/lang/Throwable");
+ Label label5 = new Label();
+ Label label6 = new Label();
+ methodVisitor.visitTryCatchBlock(label5, label6, label2, "java/lang/Throwable");
+ Label label7 = new Label();
+ methodVisitor.visitTryCatchBlock(label0, label1, label7, null);
+ methodVisitor.visitTryCatchBlock(label3, label4, label7, null);
+ methodVisitor.visitTryCatchBlock(label5, label6, label7, null);
+ methodVisitor.visitTryCatchBlock(label2, label7, label7, null);
+ Label label8 = new Label();
+ methodVisitor.visitTryCatchBlock(label7, label8, label7, null);
+ Label label9 = new Label();
+ Label label10 = new Label();
+ methodVisitor.visitTryCatchBlock(
+ label9, label1, label10, "kotlinx/coroutines/channels/ClosedReceiveChannelException");
+ methodVisitor.visitTryCatchBlock(
+ label3, label4, label10, "kotlinx/coroutines/channels/ClosedReceiveChannelException");
+ methodVisitor.visitTryCatchBlock(
+ label5, label10, label10, "kotlinx/coroutines/channels/ClosedReceiveChannelException");
+ methodVisitor.visitMethodInsn(
+ INVOKESTATIC,
+ "kotlin/coroutines/intrinsics/IntrinsicsKt",
+ "getCOROUTINE_SUSPENDED",
+ "()Ljava/lang/Object;",
+ false);
+ Label label11 = new Label();
+ methodVisitor.visitLabel(label11);
+ methodVisitor.visitLineNumber(60, label11);
+ methodVisitor.visitVarInsn(ASTORE, 10);
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitFieldInsn(
+ GETFIELD,
+ "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
+ "label",
+ "I");
+ Label label12 = new Label();
+ Label label13 = new Label();
+ Label label14 = new Label();
+ Label label15 = new Label();
+ methodVisitor.visitTableSwitchInsn(0, 2, label15, new Label[] {label12, label13, label14});
+ methodVisitor.visitLabel(label12);
+ methodVisitor.visitFrame(
+ Opcodes.F_FULL,
+ 11,
+ new Object[] {
+ "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
+ "java/lang/Object",
+ Opcodes.TOP,
+ Opcodes.TOP,
+ Opcodes.TOP,
+ Opcodes.TOP,
+ Opcodes.TOP,
+ Opcodes.TOP,
+ Opcodes.TOP,
+ Opcodes.TOP,
+ "java/lang/Object"
+ },
+ 0,
+ new Object[] {});
+ methodVisitor.visitVarInsn(ALOAD, 1);
+ methodVisitor.visitMethodInsn(
+ INVOKESTATIC, "kotlin/ResultKt", "throwOnFailure", "(Ljava/lang/Object;)V", false);
+ Label label16 = new Label();
+ methodVisitor.visitLabel(label16);
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitFieldInsn(
+ GETFIELD,
+ "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
+ "L$0",
+ "Ljava/lang/Object;");
+ methodVisitor.visitTypeInsn(CHECKCAST, "kotlinx/coroutines/CoroutineScope");
+ methodVisitor.visitVarInsn(ASTORE, 2);
+ methodVisitor.visitLabel(label9);
+ methodVisitor.visitLineNumber(61, label9);
+ methodVisitor.visitInsn(NOP);
+ Label label17 = new Label();
+ methodVisitor.visitLabel(label17);
+ methodVisitor.visitLineNumber(62, label17);
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitFieldInsn(
+ GETFIELD,
+ "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
+ "this$0",
+ "Lio/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber;");
+ methodVisitor.visitMethodInsn(
+ INVOKESTATIC,
+ "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber",
+ "access$getQueue$p",
+ "(Lio/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber;)Lkotlinx/coroutines/channels/Channel;",
+ false);
+ methodVisitor.visitTypeInsn(CHECKCAST, "kotlinx/coroutines/channels/ReceiveChannel");
+ methodVisitor.visitVarInsn(ASTORE, 3);
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitFieldInsn(
+ GETFIELD,
+ "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
+ "this$0",
+ "Lio/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber;");
+ methodVisitor.visitVarInsn(ASTORE, 4);
+ Label label18 = new Label();
+ methodVisitor.visitLabel(label18);
+ methodVisitor.visitInsn(ICONST_0);
+ methodVisitor.visitVarInsn(ISTORE, 5);
+ Label label19 = new Label();
+ methodVisitor.visitLabel(label19);
+ methodVisitor.visitLineNumber(146, label19);
+ methodVisitor.visitInsn(NOP);
+ Label label20 = new Label();
+ methodVisitor.visitLabel(label20);
+ methodVisitor.visitLineNumber(149, label20);
+ methodVisitor.visitInsn(ACONST_NULL);
+ methodVisitor.visitVarInsn(ASTORE, 6);
+ methodVisitor.visitLabel(label0);
+ methodVisitor.visitLineNumber(150, label0);
+ methodVisitor.visitInsn(NOP);
+ Label label21 = new Label();
+ methodVisitor.visitLabel(label21);
+ methodVisitor.visitLineNumber(151, label21);
+ methodVisitor.visitInsn(ICONST_0);
+ methodVisitor.visitVarInsn(ISTORE, 7);
+ Label label22 = new Label();
+ methodVisitor.visitLabel(label22);
+ methodVisitor.visitLineNumber(63, label22);
+ methodVisitor.visitFrame(
+ Opcodes.F_FULL,
+ 11,
+ new Object[] {
+ "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
+ "java/lang/Object",
+ "kotlinx/coroutines/CoroutineScope",
+ "kotlinx/coroutines/channels/ReceiveChannel",
+ "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber",
+ Opcodes.INTEGER,
+ Opcodes.NULL,
+ Opcodes.INTEGER,
+ Opcodes.TOP,
+ Opcodes.TOP,
+ "java/lang/Object"
+ },
+ 0,
+ new Object[] {});
+ methodVisitor.visitVarInsn(ALOAD, 2);
+ methodVisitor.visitMethodInsn(
+ INVOKESTATIC,
+ "kotlinx/coroutines/CoroutineScopeKt",
+ "isActive",
+ "(Lkotlinx/coroutines/CoroutineScope;)Z",
+ false);
+ Label label23 = new Label();
+ methodVisitor.visitJumpInsn(IFEQ, label23);
+ Label label24 = new Label();
+ methodVisitor.visitLabel(label24);
+ methodVisitor.visitLineNumber(64, label24);
+ methodVisitor.visitVarInsn(ALOAD, 4);
+ methodVisitor.visitMethodInsn(
+ INVOKESTATIC,
+ "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber",
+ "access$getQueue$p",
+ "(Lio/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber;)Lkotlinx/coroutines/channels/Channel;",
+ false);
+ methodVisitor.visitMethodInsn(
+ INVOKEINTERFACE,
+ "kotlinx/coroutines/channels/Channel",
+ "tryReceive-PtdJZtk",
+ "()Ljava/lang/Object;",
+ true);
+ methodVisitor.visitMethodInsn(
+ INVOKESTATIC,
+ "kotlinx/coroutines/channels/ChannelResult",
+ "getOrNull-impl",
+ "(Ljava/lang/Object;)Ljava/lang/Object;",
+ false);
+ methodVisitor.visitTypeInsn(CHECKCAST, "java/nio/ByteBuffer");
+ methodVisitor.visitVarInsn(ASTORE, 8);
+ Label label25 = new Label();
+ methodVisitor.visitLabel(label25);
+ methodVisitor.visitLineNumber(65, label25);
+ methodVisitor.visitVarInsn(ALOAD, 8);
+ Label label26 = new Label();
+ methodVisitor.visitJumpInsn(IFNONNULL, label26);
+ Label label27 = new Label();
+ methodVisitor.visitLabel(label27);
+ methodVisitor.visitLineNumber(66, label27);
+ methodVisitor.visitVarInsn(ALOAD, 4);
+ methodVisitor.visitFieldInsn(
+ GETFIELD,
+ "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber",
+ "subscription",
+ "Ljava/lang/Object;");
+ methodVisitor.visitTypeInsn(CHECKCAST, "java/util/concurrent/Flow$Subscription");
+ methodVisitor.visitInsn(DUP);
+ Label label28 = new Label();
+ methodVisitor.visitJumpInsn(IFNULL, label28);
+ methodVisitor.visitInsn(LCONST_1);
+ methodVisitor.visitMethodInsn(
+ INVOKEINTERFACE, "java/util/concurrent/Flow$Subscription", "request", "(J)V", true);
+ Label label29 = new Label();
+ methodVisitor.visitJumpInsn(GOTO, label29);
+ methodVisitor.visitLabel(label28);
+ methodVisitor.visitFrame(
+ Opcodes.F_FULL,
+ 11,
+ new Object[] {
+ "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
+ "java/lang/Object",
+ "kotlinx/coroutines/CoroutineScope",
+ "kotlinx/coroutines/channels/ReceiveChannel",
+ "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber",
+ Opcodes.INTEGER,
+ Opcodes.NULL,
+ Opcodes.INTEGER,
+ "java/nio/ByteBuffer",
+ Opcodes.TOP,
+ "java/lang/Object"
+ },
+ 1,
+ new Object[] {"java/util/concurrent/Flow$Subscription"});
+ methodVisitor.visitInsn(POP);
+ methodVisitor.visitLabel(label29);
+ methodVisitor.visitLineNumber(67, label29);
+ methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
+ methodVisitor.visitVarInsn(ALOAD, 4);
+ methodVisitor.visitMethodInsn(
+ INVOKESTATIC,
+ "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber",
+ "access$getQueue$p",
+ "(Lio/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber;)Lkotlinx/coroutines/channels/Channel;",
+ false);
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitVarInsn(ALOAD, 2);
+ methodVisitor.visitFieldInsn(
+ PUTFIELD,
+ "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
+ "L$0",
+ "Ljava/lang/Object;");
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitVarInsn(ALOAD, 3);
+ methodVisitor.visitFieldInsn(
+ PUTFIELD,
+ "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
+ "L$1",
+ "Ljava/lang/Object;");
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitVarInsn(ALOAD, 4);
+ methodVisitor.visitFieldInsn(
+ PUTFIELD,
+ "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
+ "L$2",
+ "Ljava/lang/Object;");
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitInsn(ICONST_1);
+ methodVisitor.visitFieldInsn(
+ PUTFIELD,
+ "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
+ "label",
+ "I");
+ methodVisitor.visitMethodInsn(
+ INVOKEINTERFACE,
+ "kotlinx/coroutines/channels/Channel",
+ "receive",
+ "(Lkotlin/coroutines/Continuation;)Ljava/lang/Object;",
+ true);
+ methodVisitor.visitLabel(label1);
+ methodVisitor.visitInsn(DUP);
+ methodVisitor.visitVarInsn(ALOAD, 10);
+ Label label30 = new Label();
+ methodVisitor.visitJumpInsn(IF_ACMPNE, label30);
+ Label label31 = new Label();
+ methodVisitor.visitLabel(label31);
+ methodVisitor.visitLineNumber(60, label31);
+ methodVisitor.visitVarInsn(ALOAD, 10);
+ methodVisitor.visitInsn(ARETURN);
+ methodVisitor.visitLabel(label13);
+ methodVisitor.visitFrame(
+ Opcodes.F_FULL,
+ 11,
+ new Object[] {
+ "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
+ "java/lang/Object",
+ Opcodes.TOP,
+ Opcodes.TOP,
+ Opcodes.TOP,
+ Opcodes.TOP,
+ Opcodes.TOP,
+ Opcodes.TOP,
+ Opcodes.TOP,
+ Opcodes.TOP,
+ "java/lang/Object"
+ },
+ 0,
+ new Object[] {});
+ methodVisitor.visitInsn(ICONST_0);
+ methodVisitor.visitVarInsn(ISTORE, 5);
+ Label label32 = new Label();
+ methodVisitor.visitLabel(label32);
+ methodVisitor.visitInsn(ICONST_0);
+ methodVisitor.visitVarInsn(ISTORE, 7);
+ Label label33 = new Label();
+ methodVisitor.visitLabel(label33);
+ methodVisitor.visitInsn(ACONST_NULL);
+ methodVisitor.visitVarInsn(ASTORE, 6);
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitFieldInsn(
+ GETFIELD,
+ "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
+ "L$2",
+ "Ljava/lang/Object;");
+ methodVisitor.visitTypeInsn(
+ CHECKCAST,
+ "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber");
+ methodVisitor.visitVarInsn(ASTORE, 4);
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitFieldInsn(
+ GETFIELD,
+ "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
+ "L$1",
+ "Ljava/lang/Object;");
+ methodVisitor.visitTypeInsn(CHECKCAST, "kotlinx/coroutines/channels/ReceiveChannel");
+ methodVisitor.visitVarInsn(ASTORE, 3);
+ Label label34 = new Label();
+ methodVisitor.visitLabel(label34);
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitFieldInsn(
+ GETFIELD,
+ "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
+ "L$0",
+ "Ljava/lang/Object;");
+ methodVisitor.visitTypeInsn(CHECKCAST, "kotlinx/coroutines/CoroutineScope");
+ methodVisitor.visitVarInsn(ASTORE, 2);
+ methodVisitor.visitLabel(label3);
+ methodVisitor.visitInsn(NOP);
+ methodVisitor.visitVarInsn(ALOAD, 1);
+ methodVisitor.visitMethodInsn(
+ INVOKESTATIC, "kotlin/ResultKt", "throwOnFailure", "(Ljava/lang/Object;)V", false);
+ methodVisitor.visitVarInsn(ALOAD, 1);
+ methodVisitor.visitLabel(label30);
+ methodVisitor.visitFrame(
+ Opcodes.F_FULL,
+ 11,
+ new Object[] {
+ "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
+ "java/lang/Object",
+ "kotlinx/coroutines/CoroutineScope",
+ "kotlinx/coroutines/channels/ReceiveChannel",
+ "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber",
+ Opcodes.INTEGER,
+ Opcodes.NULL,
+ Opcodes.INTEGER,
+ Opcodes.TOP,
+ Opcodes.TOP,
+ "java/lang/Object"
+ },
+ 1,
+ new Object[] {"java/lang/Object"});
+ methodVisitor.visitTypeInsn(CHECKCAST, "java/nio/ByteBuffer");
+ methodVisitor.visitVarInsn(ASTORE, 8);
+ methodVisitor.visitLabel(label26);
+ methodVisitor.visitLineNumber(70, label26);
+ methodVisitor.visitFrame(
+ Opcodes.F_FULL,
+ 11,
+ new Object[] {
+ "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
+ "java/lang/Object",
+ "kotlinx/coroutines/CoroutineScope",
+ "kotlinx/coroutines/channels/ReceiveChannel",
+ "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber",
+ Opcodes.INTEGER,
+ Opcodes.NULL,
+ Opcodes.INTEGER,
+ "java/nio/ByteBuffer",
+ Opcodes.TOP,
+ "java/lang/Object"
+ },
+ 0,
+ new Object[] {});
+ methodVisitor.visitVarInsn(ALOAD, 4);
+ methodVisitor.visitMethodInsn(
+ INVOKESTATIC,
+ "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber",
+ "access$getResponseChannel$p",
+ "(Lio/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber;)Lio/ktor/utils/io/ByteChannel;",
+ false);
+ methodVisitor.visitVarInsn(ALOAD, 8);
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitVarInsn(ALOAD, 2);
+ methodVisitor.visitFieldInsn(
+ PUTFIELD,
+ "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
+ "L$0",
+ "Ljava/lang/Object;");
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitVarInsn(ALOAD, 3);
+ methodVisitor.visitFieldInsn(
+ PUTFIELD,
+ "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
+ "L$1",
+ "Ljava/lang/Object;");
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitVarInsn(ALOAD, 4);
+ methodVisitor.visitFieldInsn(
+ PUTFIELD,
+ "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
+ "L$2",
+ "Ljava/lang/Object;");
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitInsn(ICONST_2);
+ methodVisitor.visitFieldInsn(
+ PUTFIELD,
+ "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
+ "label",
+ "I");
+ methodVisitor.visitMethodInsn(
+ INVOKEINTERFACE,
+ "io/ktor/utils/io/ByteChannel",
+ "writeFully",
+ "(Ljava/nio/ByteBuffer;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;",
+ true);
+ methodVisitor.visitLabel(label4);
+ methodVisitor.visitInsn(DUP);
+ methodVisitor.visitVarInsn(ALOAD, 10);
+ Label label35 = new Label();
+ methodVisitor.visitJumpInsn(IF_ACMPNE, label35);
+ Label label36 = new Label();
+ methodVisitor.visitLabel(label36);
+ methodVisitor.visitLineNumber(60, label36);
+ methodVisitor.visitVarInsn(ALOAD, 10);
+ methodVisitor.visitInsn(ARETURN);
+ methodVisitor.visitLabel(label14);
+ methodVisitor.visitFrame(
+ Opcodes.F_FULL,
+ 11,
+ new Object[] {
+ "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
+ "java/lang/Object",
+ Opcodes.TOP,
+ Opcodes.TOP,
+ Opcodes.TOP,
+ Opcodes.TOP,
+ Opcodes.TOP,
+ Opcodes.TOP,
+ Opcodes.TOP,
+ Opcodes.TOP,
+ "java/lang/Object"
+ },
+ 0,
+ new Object[] {});
+ methodVisitor.visitInsn(ICONST_0);
+ methodVisitor.visitVarInsn(ISTORE, 5);
+ Label label37 = new Label();
+ methodVisitor.visitLabel(label37);
+ methodVisitor.visitInsn(ICONST_0);
+ methodVisitor.visitVarInsn(ISTORE, 7);
+ Label label38 = new Label();
+ methodVisitor.visitLabel(label38);
+ methodVisitor.visitInsn(ACONST_NULL);
+ methodVisitor.visitVarInsn(ASTORE, 6);
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitFieldInsn(
+ GETFIELD,
+ "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
+ "L$2",
+ "Ljava/lang/Object;");
+ methodVisitor.visitTypeInsn(
+ CHECKCAST,
+ "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber");
+ methodVisitor.visitVarInsn(ASTORE, 4);
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitFieldInsn(
+ GETFIELD,
+ "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
+ "L$1",
+ "Ljava/lang/Object;");
+ methodVisitor.visitTypeInsn(CHECKCAST, "kotlinx/coroutines/channels/ReceiveChannel");
+ methodVisitor.visitVarInsn(ASTORE, 3);
+ Label label39 = new Label();
+ methodVisitor.visitLabel(label39);
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitFieldInsn(
+ GETFIELD,
+ "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
+ "L$0",
+ "Ljava/lang/Object;");
+ methodVisitor.visitTypeInsn(CHECKCAST, "kotlinx/coroutines/CoroutineScope");
+ methodVisitor.visitVarInsn(ASTORE, 2);
+ methodVisitor.visitLabel(label5);
+ methodVisitor.visitInsn(NOP);
+ methodVisitor.visitVarInsn(ALOAD, 1);
+ methodVisitor.visitMethodInsn(
+ INVOKESTATIC, "kotlin/ResultKt", "throwOnFailure", "(Ljava/lang/Object;)V", false);
+ methodVisitor.visitVarInsn(ALOAD, 1);
+ methodVisitor.visitLabel(label35);
+ methodVisitor.visitLineNumber(70, label35);
+ methodVisitor.visitFrame(
+ Opcodes.F_FULL,
+ 11,
+ new Object[] {
+ "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
+ "java/lang/Object",
+ "kotlinx/coroutines/CoroutineScope",
+ "kotlinx/coroutines/channels/ReceiveChannel",
+ "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber",
+ Opcodes.INTEGER,
+ Opcodes.NULL,
+ Opcodes.INTEGER,
+ Opcodes.TOP,
+ Opcodes.TOP,
+ "java/lang/Object"
+ },
+ 1,
+ new Object[] {"java/lang/Object"});
+ methodVisitor.visitInsn(POP);
+ methodVisitor.visitJumpInsn(GOTO, label22);
+ methodVisitor.visitLabel(label23);
+ methodVisitor.visitLineNumber(72, label23);
+ methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
+ methodVisitor.visitInsn(NOP);
+ Label label40 = new Label();
+ methodVisitor.visitLabel(label40);
+ methodVisitor.visitFieldInsn(GETSTATIC, "kotlin/Unit", "INSTANCE", "Lkotlin/Unit;");
+ methodVisitor.visitVarInsn(ASTORE, 9);
+ methodVisitor.visitLabel(label6);
+ methodVisitor.visitLineNumber(156, label6);
+ methodVisitor.visitVarInsn(ALOAD, 3);
+ methodVisitor.visitVarInsn(ALOAD, 6);
+ methodVisitor.visitMethodInsn(
+ INVOKESTATIC,
+ "kotlinx/coroutines/channels/ChannelsKt",
+ "cancelConsumed",
+ "(Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/lang/Throwable;)V",
+ false);
+ Label label41 = new Label();
+ methodVisitor.visitLabel(label41);
+ methodVisitor.visitLineNumber(151, label41);
+ Label label42 = new Label();
+ methodVisitor.visitJumpInsn(GOTO, label42);
+ methodVisitor.visitLabel(label2);
+ methodVisitor.visitLineNumber(152, label2);
+ methodVisitor.visitFrame(
+ Opcodes.F_FULL,
+ 11,
+ new Object[] {
+ "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
+ "java/lang/Object",
+ "kotlinx/coroutines/CoroutineScope",
+ "kotlinx/coroutines/channels/ReceiveChannel",
+ "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber",
+ Opcodes.INTEGER,
+ Opcodes.NULL,
+ Opcodes.TOP,
+ Opcodes.TOP,
+ Opcodes.TOP,
+ "java/lang/Object"
+ },
+ 1,
+ new Object[] {"java/lang/Throwable"});
+ methodVisitor.visitVarInsn(ASTORE, 9);
+ Label label43 = new Label();
+ methodVisitor.visitLabel(label43);
+ methodVisitor.visitLineNumber(153, label43);
+ methodVisitor.visitVarInsn(ALOAD, 9);
+ methodVisitor.visitVarInsn(ASTORE, 6);
+ Label label44 = new Label();
+ methodVisitor.visitLabel(label44);
+ methodVisitor.visitLineNumber(154, label44);
+ methodVisitor.visitVarInsn(ALOAD, 9);
+ methodVisitor.visitInsn(ATHROW);
+ methodVisitor.visitLabel(label7);
+ methodVisitor.visitLineNumber(155, label7);
+ methodVisitor.visitFrame(
+ Opcodes.F_FULL,
+ 11,
+ new Object[] {
+ "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
+ "java/lang/Object",
+ "kotlinx/coroutines/CoroutineScope",
+ "kotlinx/coroutines/channels/ReceiveChannel",
+ "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber",
+ Opcodes.INTEGER,
+ "java/lang/Throwable",
+ Opcodes.TOP,
+ Opcodes.TOP,
+ Opcodes.TOP,
+ "java/lang/Object"
+ },
+ 1,
+ new Object[] {"java/lang/Throwable"});
+ methodVisitor.visitVarInsn(ASTORE, 9);
+ methodVisitor.visitLabel(label8);
+ methodVisitor.visitLineNumber(156, label8);
+ methodVisitor.visitVarInsn(ALOAD, 3);
+ methodVisitor.visitVarInsn(ALOAD, 6);
+ methodVisitor.visitMethodInsn(
+ INVOKESTATIC,
+ "kotlinx/coroutines/channels/ChannelsKt",
+ "cancelConsumed",
+ "(Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/lang/Throwable;)V",
+ false);
+ methodVisitor.visitVarInsn(ALOAD, 9);
+ methodVisitor.visitInsn(ATHROW);
+ methodVisitor.visitLabel(label10);
+ methodVisitor.visitLineNumber(73, label10);
+ methodVisitor.visitFrame(
+ Opcodes.F_FULL,
+ 11,
+ new Object[] {
+ "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
+ "java/lang/Object",
+ "kotlinx/coroutines/CoroutineScope",
+ Opcodes.TOP,
+ Opcodes.TOP,
+ Opcodes.TOP,
+ Opcodes.TOP,
+ Opcodes.TOP,
+ Opcodes.TOP,
+ Opcodes.TOP,
+ "java/lang/Object"
+ },
+ 1,
+ new Object[] {"kotlinx/coroutines/channels/ClosedReceiveChannelException"});
+ methodVisitor.visitVarInsn(ASTORE, 3);
+ methodVisitor.visitLabel(label42);
+ methodVisitor.visitLineNumber(75, label42);
+ methodVisitor.visitFrame(
+ Opcodes.F_FULL,
+ 11,
+ new Object[] {
+ "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
+ "java/lang/Object",
+ "kotlinx/coroutines/CoroutineScope",
+ "java/lang/Object",
+ Opcodes.TOP,
+ Opcodes.TOP,
+ Opcodes.TOP,
+ Opcodes.TOP,
+ Opcodes.TOP,
+ Opcodes.TOP,
+ "java/lang/Object"
+ },
+ 0,
+ new Object[] {});
+ methodVisitor.visitFieldInsn(GETSTATIC, "kotlin/Unit", "INSTANCE", "Lkotlin/Unit;");
+ methodVisitor.visitInsn(ARETURN);
+ methodVisitor.visitLabel(label15);
+ methodVisitor.visitFrame(
+ Opcodes.F_FULL,
+ 11,
+ new Object[] {
+ "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
+ "java/lang/Object",
+ Opcodes.TOP,
+ Opcodes.TOP,
+ Opcodes.TOP,
+ Opcodes.TOP,
+ Opcodes.TOP,
+ Opcodes.TOP,
+ Opcodes.TOP,
+ Opcodes.TOP,
+ "java/lang/Object"
+ },
+ 0,
+ new Object[] {});
+ methodVisitor.visitTypeInsn(NEW, "java/lang/IllegalStateException");
+ methodVisitor.visitInsn(DUP);
+ methodVisitor.visitLdcInsn("call to 'resume' before 'invoke' with coroutine");
+ methodVisitor.visitMethodInsn(
+ INVOKESPECIAL,
+ "java/lang/IllegalStateException",
+ "<init>",
+ "(Ljava/lang/String;)V",
+ false);
+ methodVisitor.visitInsn(ATHROW);
+ methodVisitor.visitLocalVariable(
+ "$this$launch", "Lkotlinx/coroutines/CoroutineScope;", null, label9, label13, 2);
+ methodVisitor.visitLocalVariable(
+ "$this$launch", "Lkotlinx/coroutines/CoroutineScope;", null, label3, label14, 2);
+ methodVisitor.visitLocalVariable(
+ "$this$launch", "Lkotlinx/coroutines/CoroutineScope;", null, label5, label23, 2);
+ methodVisitor.visitLocalVariable(
+ "$this$consume$iv",
+ "Lkotlinx/coroutines/channels/ReceiveChannel;",
+ null,
+ label18,
+ label13,
+ 3);
+ methodVisitor.visitLocalVariable(
+ "$this$consume$iv",
+ "Lkotlinx/coroutines/channels/ReceiveChannel;",
+ null,
+ label34,
+ label14,
+ 3);
+ methodVisitor.visitLocalVariable(
+ "$this$consume$iv",
+ "Lkotlinx/coroutines/channels/ReceiveChannel;",
+ null,
+ label39,
+ label40,
+ 3);
+ methodVisitor.visitLocalVariable(
+ "$this$consume$iv",
+ "Lkotlinx/coroutines/channels/ReceiveChannel;",
+ null,
+ label40,
+ label41,
+ 3);
+ methodVisitor.visitLocalVariable(
+ "$this$consume$iv",
+ "Lkotlinx/coroutines/channels/ReceiveChannel;",
+ null,
+ label2,
+ label10,
+ 3);
+ methodVisitor.visitLocalVariable(
+ "cause$iv", "Ljava/lang/Throwable;", null, label0, label13, 6);
+ methodVisitor.visitLocalVariable(
+ "cause$iv", "Ljava/lang/Throwable;", null, label34, label14, 6);
+ methodVisitor.visitLocalVariable(
+ "cause$iv", "Ljava/lang/Throwable;", null, label39, label40, 6);
+ methodVisitor.visitLocalVariable(
+ "cause$iv", "Ljava/lang/Throwable;", null, label40, label41, 6);
+ methodVisitor.visitLocalVariable(
+ "cause$iv", "Ljava/lang/Throwable;", null, label2, label44, 6);
+ methodVisitor.visitLocalVariable(
+ "cause$iv", "Ljava/lang/Throwable;", null, label44, label10, 6);
+ methodVisitor.visitLocalVariable(
+ "buffer", "Ljava/nio/ByteBuffer;", null, label25, label27, 8);
+ methodVisitor.visitLocalVariable(
+ "buffer", "Ljava/nio/ByteBuffer;", null, label26, label4, 8);
+ methodVisitor.visitLocalVariable("e$iv", "Ljava/lang/Throwable;", null, label43, label7, 9);
+ methodVisitor.visitLocalVariable(
+ "$i$a$-consume-JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1$1",
+ "I",
+ null,
+ label22,
+ label13,
+ 7);
+ methodVisitor.visitLocalVariable("$i$f$consume", "I", null, label19, label13, 5);
+ methodVisitor.visitLocalVariable(
+ "this",
+ "Lio/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1;",
+ null,
+ label16,
+ label15,
+ 0);
+ methodVisitor.visitLocalVariable(
+ "$result", "Ljava/lang/Object;", null, label16, label15, 1);
+ methodVisitor.visitLocalVariable(
+ "$i$a$-consume-JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1$1",
+ "I",
+ null,
+ label33,
+ label14,
+ 7);
+ methodVisitor.visitLocalVariable("$i$f$consume", "I", null, label32, label14, 5);
+ methodVisitor.visitLocalVariable(
+ "$i$a$-consume-JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1$1",
+ "I",
+ null,
+ label38,
+ label40,
+ 7);
+ methodVisitor.visitLocalVariable("$i$f$consume", "I", null, label37, label10, 5);
+ methodVisitor.visitMaxs(5, 11);
+ methodVisitor.visitEnd();
+ }
+ {
+ methodVisitor =
+ classWriter.visitMethod(
+ ACC_PUBLIC | ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
+ methodVisitor.visitCode();
+ Label label0 = new Label();
+ methodVisitor.visitLabel(label0);
+ methodVisitor.visitLineNumber(70, label0);
+ methodVisitor.visitTypeInsn(
+ NEW, "com/android/tools/r8/cf/CfDebugLocalStackMapVerificationTest$Main");
+ methodVisitor.visitInsn(DUP);
+ methodVisitor.visitMethodInsn(
+ INVOKESPECIAL,
+ "com/android/tools/r8/cf/CfDebugLocalStackMapVerificationTest$Main",
+ "<init>",
+ "()V",
+ false);
+ methodVisitor.visitInsn(ACONST_NULL);
+ methodVisitor.visitMethodInsn(
+ INVOKEVIRTUAL,
+ "com/android/tools/r8/cf/CfDebugLocalStackMapVerificationTest$Main",
+ "invokeSuspend",
+ "(Ljava/lang/Object;)Ljava/lang/Object;",
+ false);
+ methodVisitor.visitInsn(POP);
+ Label label1 = new Label();
+ methodVisitor.visitLabel(label1);
+ methodVisitor.visitLineNumber(71, label1);
+ methodVisitor.visitInsn(RETURN);
+ Label label2 = new Label();
+ methodVisitor.visitLabel(label2);
+ methodVisitor.visitLocalVariable("args", "[Ljava/lang/String;", null, label0, label2, 0);
+ methodVisitor.visitMaxs(2, 1);
+ methodVisitor.visitEnd();
+ }
+ classWriter.visitEnd();
+
+ return classWriter.toByteArray();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/cf/methodhandles/InvokeMethodHandleRuntimeErrorTest.java b/src/test/java/com/android/tools/r8/cf/methodhandles/InvokeMethodHandleRuntimeErrorTest.java
index 1fcabcd..b16a586 100644
--- a/src/test/java/com/android/tools/r8/cf/methodhandles/InvokeMethodHandleRuntimeErrorTest.java
+++ b/src/test/java/com/android/tools/r8/cf/methodhandles/InvokeMethodHandleRuntimeErrorTest.java
@@ -66,10 +66,6 @@
.addProgramClasses(Main.class, I.class, Super.class)
.addProgramClassFileData(getInvokeCustomTransform())
.setMinApi(parameters.getApiLevel())
- .mapUnsupportedFeaturesToWarnings()
- // TODO(b/238175192): remove again when resolved
- .addOptionsModification(
- options -> options.enableUnrepresentableInDexInstructionRemoval = true)
.compileWithExpectedDiagnostics(
diagnostics -> {
if (hasCompileSupport()) {
diff --git a/src/test/java/com/android/tools/r8/cf/methodhandles/InvokePolymorphicTypesTest.java b/src/test/java/com/android/tools/r8/cf/methodhandles/InvokePolymorphicTypesTest.java
new file mode 100644
index 0000000..58de21e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/methodhandles/InvokePolymorphicTypesTest.java
@@ -0,0 +1,280 @@
+// 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.cf.methodhandles;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assume.assumeTrue;
+
+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.references.Reference;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+/**
+ * This test is a refactoring of the InvokePolymorphic test code run by the various AndroidOTest
+ * runners.
+ */
+@RunWith(Parameterized.class)
+public class InvokePolymorphicTypesTest extends TestBase {
+
+ static final String EXPECTED =
+ StringUtils.lines(
+ "N-1-string",
+ "2-a--1-1.1-2.24-12345678-N-1-string",
+ "false",
+ "h",
+ "56",
+ "72",
+ "2147483689",
+ "0.56",
+ "100.0",
+ "hello",
+ "goodbye",
+ "true");
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
+ }
+
+ public InvokePolymorphicTypesTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ boolean hasCompileSupport =
+ parameters.getApiLevel().isGreaterThanOrEqualTo(apiLevelWithInvokePolymorphicSupport());
+ boolean hasRuntimeSupport =
+ parameters.isCfRuntime()
+ || parameters.asDexRuntime().getVersion().isNewerThanOrEqual(Version.V8_1_0);
+ testForD8(parameters.getBackend())
+ .addProgramClasses(Data.class, TestClass.class)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), TestClass.class)
+ .applyIf(
+ hasCompileSupport,
+ r -> r.assertSuccessWithOutput(EXPECTED),
+ hasRuntimeSupport,
+ r ->
+ r.assertSuccess()
+ .assertStderrMatches(
+ containsString(
+ "Instruction is unrepresentable in DEX V35: invoke-polymorphic")),
+ r -> r.assertFailureWithErrorThatThrows(NoClassDefFoundError.class));
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ assumeTrue(parameters.isDexRuntime() || parameters.getApiLevel().equals(AndroidApiLevel.B));
+ boolean hasCompileSupport =
+ parameters.isCfRuntime()
+ || parameters
+ .getApiLevel()
+ .isGreaterThanOrEqualTo(apiLevelWithInvokePolymorphicSupport());
+ boolean hasRuntimeSupport =
+ parameters.isCfRuntime()
+ || parameters.asDexRuntime().getVersion().isNewerThanOrEqual(Version.V8_1_0);
+ testForR8(parameters.getBackend())
+ .addProgramClasses(Data.class, TestClass.class)
+ .setMinApi(parameters.getApiLevel())
+ .applyIf(
+ !hasCompileSupport,
+ b ->
+ b.addDontWarn(
+ MethodType.class,
+ MethodHandle.class,
+ MethodHandles.class,
+ MethodHandles.Lookup.class))
+ .addKeepMainRule(TestClass.class)
+ .addKeepMethodRules(
+ Reference.methodFromMethod(Data.class.getDeclaredConstructor()),
+ Reference.methodFromMethod(
+ TestClass.class.getDeclaredMethod(
+ "buildString", Integer.class, int.class, String.class)),
+ Reference.methodFromMethod(
+ TestClass.class.getDeclaredMethod(
+ "buildString",
+ byte.class,
+ char.class,
+ short.class,
+ float.class,
+ double.class,
+ long.class,
+ Integer.class,
+ int.class,
+ String.class)),
+ Reference.methodFromMethod(
+ TestClass.class.getDeclaredMethod(
+ "testWithAllTypes",
+ boolean.class,
+ char.class,
+ short.class,
+ int.class,
+ long.class,
+ float.class,
+ double.class,
+ String.class,
+ Object.class)))
+ .allowDiagnosticMessages()
+ .run(parameters.getRuntime(), TestClass.class)
+ .applyIf(
+ hasCompileSupport,
+ r -> r.assertSuccessWithOutput(EXPECTED),
+ hasRuntimeSupport,
+ r ->
+ r.assertSuccess()
+ .assertStderrMatches(
+ containsString(
+ "Instruction is unrepresentable in DEX V35: invoke-polymorphic")),
+ r -> r.assertFailureWithErrorThatThrows(NoClassDefFoundError.class));
+ }
+
+ static class Data {}
+
+ static class TestClass {
+
+ public String buildString(Integer i1, int i2, String s) {
+ return (i1 == null ? "N" : "!N") + "-" + i2 + "-" + s;
+ }
+
+ public void testInvokePolymorphic() {
+ MethodType mt = MethodType.methodType(String.class, Integer.class, int.class, String.class);
+ MethodHandles.Lookup lk = MethodHandles.lookup();
+
+ try {
+ MethodHandle mh = lk.findVirtual(getClass(), "buildString", mt);
+ System.out.println(mh.invoke(this, null, 1, "string"));
+ } catch (Throwable t) {
+ t.printStackTrace();
+ }
+ }
+
+ public String buildString(
+ byte b, char c, short s, float f, double d, long l, Integer i1, int i2, String str) {
+ return b
+ + "-"
+ + c
+ + "-"
+ + s
+ + "-"
+ + f
+ + "-"
+ + d
+ + "-"
+ + l
+ + "-"
+ + (i1 == null ? "N" : "!N")
+ + "-"
+ + i2
+ + "-"
+ + str;
+ }
+
+ public void testInvokePolymorphicRange() {
+ MethodType mt =
+ MethodType.methodType(
+ String.class,
+ byte.class,
+ char.class,
+ short.class,
+ float.class,
+ double.class,
+ long.class,
+ Integer.class,
+ int.class,
+ String.class);
+ MethodHandles.Lookup lk = MethodHandles.lookup();
+
+ try {
+ MethodHandle mh = lk.findVirtual(getClass(), "buildString", mt);
+ System.out.println(
+ mh.invoke(
+ this, (byte) 2, 'a', (short) 0xFFFF, 1.1f, 2.24d, 12345678L, null, 1, "string"));
+ } catch (Throwable t) {
+ t.printStackTrace();
+ }
+ }
+
+ public static void testWithAllTypes(
+ boolean z, char a, short b, int c, long d, float e, double f, String g, Object h) {
+ System.out.println(z);
+ System.out.println(a);
+ System.out.println(b);
+ System.out.println(c);
+ System.out.println(d);
+ System.out.println(e);
+ System.out.println(f);
+ System.out.println(g);
+ System.out.println(h);
+ }
+
+ public void testInvokePolymorphicWithAllTypes() {
+ try {
+ MethodHandle mth =
+ MethodHandles.lookup()
+ .findStatic(
+ TestClass.class,
+ "testWithAllTypes",
+ MethodType.methodType(
+ void.class,
+ boolean.class,
+ char.class,
+ short.class,
+ int.class,
+ long.class,
+ float.class,
+ double.class,
+ String.class,
+ Object.class));
+ mth.invokeExact(
+ false,
+ 'h',
+ (short) 56,
+ 72,
+ Integer.MAX_VALUE + 42l,
+ 0.56f,
+ 100.0d,
+ "hello",
+ (Object) "goodbye");
+ } catch (Throwable t) {
+ t.printStackTrace();
+ }
+ }
+
+ public MethodHandle testInvokePolymorphicWithConstructor() {
+ MethodHandle mh = null;
+ MethodType mt = MethodType.methodType(void.class);
+ MethodHandles.Lookup lk = MethodHandles.lookup();
+
+ try {
+ mh = lk.findConstructor(Data.class, mt);
+ System.out.println(mh.invoke().getClass() == Data.class);
+ } catch (Throwable t) {
+ t.printStackTrace();
+ }
+
+ return mh;
+ }
+
+ public static void main(String[] args) {
+ TestClass invokePolymorphic = new TestClass();
+ invokePolymorphic.testInvokePolymorphic();
+ invokePolymorphic.testInvokePolymorphicRange();
+ invokePolymorphic.testInvokePolymorphicWithAllTypes();
+ invokePolymorphic.testInvokePolymorphicWithConstructor();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/cf/methodhandles/MethodHandleTest.java b/src/test/java/com/android/tools/r8/cf/methodhandles/MethodHandleTest.java
index 497e19f..fbea211 100644
--- a/src/test/java/com/android/tools/r8/cf/methodhandles/MethodHandleTest.java
+++ b/src/test/java/com/android/tools/r8/cf/methodhandles/MethodHandleTest.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.cf.methodhandles;
-import com.android.tools.r8.NoVerticalClassMerging;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
@@ -61,7 +60,6 @@
// Class that is only mentioned in return value of LDC(MethodType)-instruction.
}
- @NoVerticalClassMerging
public interface I {
static void svi(int i) {
@@ -126,8 +124,8 @@
divicMethod().invoke(i, 15, 'x');
assertEquals(42L, (long) dijicMethod().invoke(i, 16, 'x'));
constructorMethod().invoke(21);
- System.out.println(veType().parameterType(0).getName().lastIndexOf('.'));
- System.out.println(fType().returnType().getName().lastIndexOf('.'));
+ System.out.println(veType().parameterType(0).equals(E.class));
+ System.out.println(fType().returnType().equals(F.class));
} catch (Throwable e) {
throw new RuntimeException(e);
}
diff --git a/src/test/java/com/android/tools/r8/cf/methodhandles/MethodHandleTestRunner.java b/src/test/java/com/android/tools/r8/cf/methodhandles/MethodHandleTestRunner.java
index ae44ac9..3189837 100644
--- a/src/test/java/com/android/tools/r8/cf/methodhandles/MethodHandleTestRunner.java
+++ b/src/test/java/com/android/tools/r8/cf/methodhandles/MethodHandleTestRunner.java
@@ -5,17 +5,16 @@
import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
import static org.hamcrest.CoreMatchers.containsString;
-import static org.junit.Assert.assertThrows;
import static org.junit.Assume.assumeTrue;
-import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.R8TestBuilder;
+import com.android.tools.r8.R8FullTestBuilder;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestDiagnosticMessages;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestRunResult;
+import com.android.tools.r8.TestShrinkerBuilder;
+import com.android.tools.r8.ThrowableConsumer;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.cf.methodhandles.MethodHandleTest.C;
import com.android.tools.r8.cf.methodhandles.MethodHandleTest.E;
import com.android.tools.r8.cf.methodhandles.MethodHandleTest.F;
@@ -25,7 +24,6 @@
import com.android.tools.r8.utils.StringUtils;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;
-import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -41,49 +39,25 @@
CONSTANT,
}
- enum MinifyMode {
- NONE,
- MINIFY,
- }
-
private String getExpected() {
return StringUtils.lines(
"C 42", "svi 1", "sji 2", "svic 3", "sjic 4", "vvi 5", "vji 6", "vvic 7", "vjic 8", "svi 9",
- "sji 10", "svic 11", "sjic 12", "dvi 13", "dji 14", "dvic 15", "djic 16", "C 21", "37",
- "37");
+ "sji 10", "svic 11", "sjic 12", "dvi 13", "dji 14", "dvic 15", "djic 16", "C 21", "true",
+ "true");
}
private final TestParameters parameters;
private final LookupType lookupType;
- private final MinifyMode minifyMode;
- @Parameters(name = "{0}, lookup:{1}, minify:{2}")
+ @Parameters(name = "{0}, lookup:{1}")
public static List<Object[]> data() {
- List<Object[]> res = new ArrayList<>();
- for (TestParameters params :
- TestParameters.builder()
- .withCfRuntimes()
- .withDexRuntimesStartingFromExcluding(Version.V7_0_0)
- // .withApiLevelsStartingAtIncluding(AndroidApiLevel.P)
- .withAllApiLevels()
- .build()) {
- for (LookupType lookupType : LookupType.values()) {
- for (MinifyMode minifyMode : MinifyMode.values()) {
- if (lookupType == LookupType.DYNAMIC && minifyMode == MinifyMode.MINIFY) {
- // Skip because we don't keep the members looked up dynamically.
- continue;
- }
- res.add(new Object[] {params, lookupType.name(), minifyMode.name()});
- }
- }
- }
- return res;
+ return buildParameters(
+ TestParameters.builder().withAllRuntimesAndApiLevels().build(), LookupType.values());
}
- public MethodHandleTestRunner(TestParameters parameters, String lookupType, String minifyMode) {
+ public MethodHandleTestRunner(TestParameters parameters, LookupType lookupType) {
this.parameters = parameters;
- this.lookupType = LookupType.valueOf(lookupType);
- this.minifyMode = MinifyMode.valueOf(minifyMode);
+ this.lookupType = lookupType;
}
@Test
@@ -98,61 +72,38 @@
@Test
public void testD8() throws Exception {
- assumeTrue(parameters.isDexRuntime() && minifyMode == MinifyMode.NONE);
+ assumeTrue(parameters.isDexRuntime());
testForD8(parameters.getBackend())
.setMinApi(parameters.getApiLevel())
.addProgramClasses(getInputClasses())
.addProgramClassFileData(getTransformedClasses())
- .mapUnsupportedFeaturesToWarnings()
- // TODO(b/238175192): remove again when resolved
- .addOptionsModification(
- options -> options.enableUnrepresentableInDexInstructionRemoval = true)
.compileWithExpectedDiagnostics(this::checkDiagnostics)
.run(parameters.getRuntime(), CLASS.getName())
.apply(this::checkResult);
}
@Test
+ public void testKeepAllR8() throws Exception {
+ // For the dynamic case, method handles/types are created reflectively so keep all.
+ runR8(TestShrinkerBuilder::addKeepAllClassesRule);
+ }
+
+ @Test
public void testR8() throws Exception {
- R8TestBuilder<?> builder =
- testForR8(parameters.getBackend())
- .setMinApi(parameters.getApiLevel())
- .addProgramClasses(getInputClasses())
- .addProgramClassFileData(getTransformedClasses())
- .addLibraryFiles(ToolHelper.getMostRecentAndroidJar())
- .addNoVerticalClassMergingAnnotations()
- // TODO(b/238175192): remove again when resolved
- .addOptionsModification(
- options -> options.enableUnrepresentableInDexInstructionRemoval = true);
- if (minifyMode == MinifyMode.MINIFY) {
- builder
- .enableProguardTestOptions()
- .addKeepMainRule(MethodHandleTest.class)
- .addKeepRules(
- // Prevent the second argument of C.svic(), C.sjic(), I.sjic() and I.svic() from
- // being removed although they are never used unused. This is needed since these
- // methods are accessed reflectively.
- "-keep,allowobfuscation public class " + typeName(C.class) + " {",
- " static void svic(int, char);",
- " static long sjic(int, char);",
- "}",
- "-keep,allowobfuscation public interface " + typeName(I.class) + " {",
- " static long sjic(int, char);",
- " static void svic(int, char);",
- "}");
- // TODO(b/235810300): The compiler fails with assertion in AppInfoWithLiveness.
- if (lookupType == LookupType.CONSTANT && hasConstMethodCompileSupport()) {
- builder.allowDiagnosticMessages();
- assertThrows(CompilationFailedException.class, builder::compile);
- return;
- }
- } else {
- builder.noTreeShaking();
- builder.noMinification();
- }
- builder
+ // We can't shrink the DYNAMIC variant as the methods are looked up reflectively.
+ assumeTrue(lookupType == LookupType.CONSTANT);
+ runR8(b -> {});
+ }
+
+ private void runR8(ThrowableConsumer<R8FullTestBuilder> additionalSetUp) throws Exception {
+ testForR8(parameters.getBackend())
+ .apply(additionalSetUp)
+ .setMinApi(parameters.getApiLevel())
+ .addProgramClasses(getInputClasses())
+ .addProgramClassFileData(getTransformedClasses())
+ .addLibraryFiles(ToolHelper.getMostRecentAndroidJar())
+ .addKeepMainRule(MethodHandleTest.class)
.allowDiagnosticMessages()
- .mapUnsupportedFeaturesToWarnings()
.compileWithExpectedDiagnostics(this::checkDiagnostics)
.run(parameters.getRuntime(), CLASS.getCanonicalName())
.apply(this::checkResult);
@@ -168,6 +119,14 @@
|| parameters.getApiLevel().isGreaterThanOrEqualTo(apiLevelWithInvokePolymorphicSupport());
}
+ private boolean hasMethodHandlesRuntimeSupport() {
+ return parameters.isCfRuntime()
+ || parameters
+ .asDexRuntime()
+ .maxSupportedApiLevel()
+ .isGreaterThanOrEqualTo(apiLevelWithInvokePolymorphicSupport());
+ }
+
private void checkDiagnostics(TestDiagnosticMessages diagnostics) {
if ((lookupType == LookupType.DYNAMIC && !hasInvokePolymorphicCompileSupport())
|| (lookupType == LookupType.CONSTANT && !hasConstMethodCompileSupport())) {
@@ -180,6 +139,12 @@
}
private void checkResult(TestRunResult<?> result) {
+ if (lookupType == LookupType.DYNAMIC && !hasMethodHandlesRuntimeSupport()) {
+ result
+ .assertFailureWithErrorThatThrows(NoClassDefFoundError.class)
+ .assertStderrMatches(containsString("java.lang.invoke.MethodHandles"));
+ return;
+ }
if (lookupType == LookupType.DYNAMIC && !hasInvokePolymorphicCompileSupport()) {
result
.assertFailureWithErrorThatThrows(RuntimeException.class)
diff --git a/src/test/java/com/android/tools/r8/cf/methodhandles/VarHandleTest.java b/src/test/java/com/android/tools/r8/cf/methodhandles/VarHandleTest.java
index 962f964..ce9cae3 100644
--- a/src/test/java/com/android/tools/r8/cf/methodhandles/VarHandleTest.java
+++ b/src/test/java/com/android/tools/r8/cf/methodhandles/VarHandleTest.java
@@ -93,10 +93,6 @@
testForD8()
.addProgramFiles(getProgramInputs())
.setMinApi(parameters.getApiLevel())
- .mapUnsupportedFeaturesToWarnings()
- // TODO(b/238175192): remove again when resolved
- .addOptionsModification(
- options -> options.enableUnrepresentableInDexInstructionRemoval = true)
.compileWithExpectedDiagnostics(
diagnostics -> {
if (hasInvokePolymorphicCompileSupport()) {
diff --git a/src/test/java/com/android/tools/r8/cf/methodhandles/fields/ClassFieldMethodHandleTest.java b/src/test/java/com/android/tools/r8/cf/methodhandles/fields/ClassFieldMethodHandleTest.java
index aac5a20..daf61c4 100644
--- a/src/test/java/com/android/tools/r8/cf/methodhandles/fields/ClassFieldMethodHandleTest.java
+++ b/src/test/java/com/android/tools/r8/cf/methodhandles/fields/ClassFieldMethodHandleTest.java
@@ -72,10 +72,6 @@
.addProgramClasses(C.class)
.addProgramClassFileData(getTransformedMain())
.setMinApi(parameters.getApiLevel())
- .mapUnsupportedFeaturesToWarnings()
- // TODO(b/238175192): remove again when resolved
- .addOptionsModification(
- options -> options.enableUnrepresentableInDexInstructionRemoval = true)
.compileWithExpectedDiagnostics(this::checkDiagnostics)
.run(parameters.getRuntime(), Main.class)
.apply(this::checkResult);
@@ -90,10 +86,6 @@
.addProgramClassFileData(getTransformedMain())
.setMinApi(parameters.getApiLevel())
.allowDiagnosticMessages()
- .mapUnsupportedFeaturesToWarnings()
- // TODO(b/238175192): remove again when resolved
- .addOptionsModification(
- options -> options.enableUnrepresentableInDexInstructionRemoval = true)
.compileWithExpectedDiagnostics(this::checkDiagnostics)
.run(parameters.getRuntime(), Main.class)
.apply(this::checkResult);
diff --git a/src/test/java/com/android/tools/r8/cf/methodhandles/fields/InterfaceFieldMethodHandleTest.java b/src/test/java/com/android/tools/r8/cf/methodhandles/fields/InterfaceFieldMethodHandleTest.java
index 629a737..927b27e 100644
--- a/src/test/java/com/android/tools/r8/cf/methodhandles/fields/InterfaceFieldMethodHandleTest.java
+++ b/src/test/java/com/android/tools/r8/cf/methodhandles/fields/InterfaceFieldMethodHandleTest.java
@@ -75,10 +75,6 @@
.addProgramClasses(I.class)
.addProgramClassFileData(getTransformedMain())
.setMinApi(parameters.getApiLevel())
- .mapUnsupportedFeaturesToWarnings()
- // TODO(b/238175192): remove again when resolved
- .addOptionsModification(
- options -> options.enableUnrepresentableInDexInstructionRemoval = true)
.compileWithExpectedDiagnostics(this::checkDiagnostics)
.run(parameters.getRuntime(), Main.class)
.apply(this::checkResult);
@@ -93,10 +89,6 @@
.addProgramClassFileData(getTransformedMain())
.setMinApi(parameters.getApiLevel())
.allowDiagnosticMessages()
- .mapUnsupportedFeaturesToWarnings()
- // TODO(b/238175192): remove again when resolved
- .addOptionsModification(
- options -> options.enableUnrepresentableInDexInstructionRemoval = true)
.compileWithExpectedDiagnostics(this::checkDiagnostics)
.run(parameters.getRuntime(), Main.class)
.apply(this::checkResult);
diff --git a/src/test/java/com/android/tools/r8/cf/methodhandles/invokespecial/InvokeSpecialMethodHandleTest.java b/src/test/java/com/android/tools/r8/cf/methodhandles/invokespecial/InvokeSpecialMethodHandleTest.java
index 70d3aec..7453e9b 100644
--- a/src/test/java/com/android/tools/r8/cf/methodhandles/invokespecial/InvokeSpecialMethodHandleTest.java
+++ b/src/test/java/com/android/tools/r8/cf/methodhandles/invokespecial/InvokeSpecialMethodHandleTest.java
@@ -75,10 +75,6 @@
.addProgramClasses(C.class, Main.class)
.addProgramClassFileData(getTransformedD())
.setMinApi(parameters.getApiLevel())
- .mapUnsupportedFeaturesToWarnings()
- // TODO(b/238175192): remove again when resolved
- .addOptionsModification(
- options -> options.enableUnrepresentableInDexInstructionRemoval = true)
.compileWithExpectedDiagnostics(this::checkDiagnostics)
.run(parameters.getRuntime(), Main.class)
.apply(this::checkResult);
@@ -93,10 +89,6 @@
.addProgramClassFileData(getTransformedD())
.setMinApi(parameters.getApiLevel())
.allowDiagnosticMessages()
- .mapUnsupportedFeaturesToWarnings()
- // TODO(b/238175192): remove again when resolved
- .addOptionsModification(
- options -> options.enableUnrepresentableInDexInstructionRemoval = true)
.compileWithExpectedDiagnostics(this::checkDiagnostics)
.run(parameters.getRuntime(), Main.class)
.apply(this::checkResult);
diff --git a/src/test/java/com/android/tools/r8/code/invokedynamic/CompileGuavaWithUnrepresentableRewritingTest.java b/src/test/java/com/android/tools/r8/code/invokedynamic/CompileGuavaWithUnrepresentableRewritingTest.java
index 655675e..a1c821e 100644
--- a/src/test/java/com/android/tools/r8/code/invokedynamic/CompileGuavaWithUnrepresentableRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/code/invokedynamic/CompileGuavaWithUnrepresentableRewritingTest.java
@@ -4,7 +4,7 @@
package com.android.tools.r8.code.invokedynamic;
-import static org.hamcrest.CoreMatchers.containsString;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
import static org.junit.Assert.assertThrows;
import static org.junit.Assume.assumeTrue;
@@ -13,6 +13,8 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.errors.DexFileOverflowDiagnostic;
+import com.android.tools.r8.errors.UnsupportedInvokeCustomDiagnostic;
import com.android.tools.r8.utils.AndroidApiLevel;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -34,25 +36,21 @@
@Test
public void testD8DexNoDesugaring() throws Throwable {
assumeTrue(ToolHelper.isTestingR8Lib());
- // TODO(b/238175192): We should not generate invalid bytecode
assertThrows(
CompilationFailedException.class,
() -> {
testForD8(Backend.DEX)
+ // DEPS contains all R8 dependencies, including guava, which extends the surface
+ // of UnrepresentableRewriting.
.addProgramFiles(ToolHelper.DEPS)
.setMinApi(AndroidApiLevel.B)
.disableDesugaring()
- .mapUnsupportedFeaturesToWarnings()
- // TODO(b/238175192): remove again when resolved
- .addOptionsModification(
- options -> options.enableUnrepresentableInDexInstructionRemoval = true)
.compileWithExpectedDiagnostics(
diagnostics ->
diagnostics
- .assertErrorMessageThatMatches(
- containsString("Expected stack type to be single width"))
- .assertWarningMessageThatMatches(
- containsString("Invalid stack map table at instruction")));
+ .assertAllWarningsMatch(
+ diagnosticType(UnsupportedInvokeCustomDiagnostic.class))
+ .assertErrorThatMatches(diagnosticType(DexFileOverflowDiagnostic.class)));
});
}
}
diff --git a/src/test/java/com/android/tools/r8/code/invokedynamic/InvokeCustomRuntimeErrorTest.java b/src/test/java/com/android/tools/r8/code/invokedynamic/InvokeCustomRuntimeErrorTest.java
index 1fa8060..a7acf9e 100644
--- a/src/test/java/com/android/tools/r8/code/invokedynamic/InvokeCustomRuntimeErrorTest.java
+++ b/src/test/java/com/android/tools/r8/code/invokedynamic/InvokeCustomRuntimeErrorTest.java
@@ -88,10 +88,6 @@
.addProgramClassFileData(getTransformedTestClass())
.setMinApi(parameters.getApiLevel())
.disableDesugaring()
- .mapUnsupportedFeaturesToWarnings()
- // TODO(b/238175192): remove again when resolved
- .addOptionsModification(
- options -> options.enableUnrepresentableInDexInstructionRemoval = true)
.compileWithExpectedDiagnostics(
diagnostics ->
diagnostics
@@ -112,9 +108,6 @@
.addProgramClasses(I.class, A.class)
.addProgramClassFileData(getTransformedTestClass())
.setMinApi(minApi)
- .mapUnsupportedFeaturesToWarnings()
- .addOptionsModification(
- options -> options.enableUnrepresentableInDexInstructionRemoval = true)
.compileWithExpectedDiagnostics(
diagnostics -> {
if (expectedSuccess) {
diff --git a/src/test/java/com/android/tools/r8/code/invokedynamic/InvokeDynamicVirtualDispatchToDefaultInterfaceMethodTest.java b/src/test/java/com/android/tools/r8/code/invokedynamic/InvokeDynamicVirtualDispatchToDefaultInterfaceMethodTest.java
index c2caab7..7494b13 100644
--- a/src/test/java/com/android/tools/r8/code/invokedynamic/InvokeDynamicVirtualDispatchToDefaultInterfaceMethodTest.java
+++ b/src/test/java/com/android/tools/r8/code/invokedynamic/InvokeDynamicVirtualDispatchToDefaultInterfaceMethodTest.java
@@ -71,9 +71,6 @@
.addProgramClassFileData(getTransformedTestClass())
.setMinApi(parameters.getApiLevel())
.addKeepMainRule(TestClass.class)
- // TODO(b/238175192): remove again when resolved
- .addOptionsModification(
- options -> options.enableUnrepresentableInDexInstructionRemoval = true)
.addKeepRules("-keepclassmembers class * { *** foo(...); }")
.run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutput(EXPECTED);
diff --git a/src/test/java/com/android/tools/r8/desugar/constantdynamic/ConstantDynamicHolderTest.java b/src/test/java/com/android/tools/r8/desugar/constantdynamic/ConstantDynamicHolderTest.java
index 2dde386..9525784 100644
--- a/src/test/java/com/android/tools/r8/desugar/constantdynamic/ConstantDynamicHolderTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/constantdynamic/ConstantDynamicHolderTest.java
@@ -66,9 +66,6 @@
|| diagnostic instanceof ConstantDynamicDesugarDiagnostic)
? DiagnosticsLevel.WARNING
: level)
- // TODO(b/238175192): remove again when resolved
- .addOptionsModification(
- options -> options.enableUnrepresentableInDexInstructionRemoval = true)
.compileWithExpectedDiagnostics(
diagnostics ->
diagnostics.assertWarningsMatch(
@@ -107,10 +104,6 @@
.setMinApi(parameters.getApiLevel())
.addKeepMainRule(Main.class)
.allowDiagnosticWarningMessages()
- .mapUnsupportedFeaturesToWarnings()
- // TODO(b/238175192): remove again when resolved
- .addOptionsModification(
- options -> options.enableUnrepresentableInDexInstructionRemoval = true)
.compileWithExpectedDiagnostics(
diagnostics -> {
if (parameters.isDexRuntime()) {
diff --git a/src/test/java/com/android/tools/r8/desugar/constantdynamic/SharedBootstrapMethodConstantDynamicTest.java b/src/test/java/com/android/tools/r8/desugar/constantdynamic/SharedBootstrapMethodConstantDynamicTest.java
index 88328c7..486b958 100644
--- a/src/test/java/com/android/tools/r8/desugar/constantdynamic/SharedBootstrapMethodConstantDynamicTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/constantdynamic/SharedBootstrapMethodConstantDynamicTest.java
@@ -90,9 +90,6 @@
|| diagnostic instanceof UnsupportedFeatureDiagnostic)
? DiagnosticsLevel.WARNING
: level)
- // TODO(b/238175192): remove again when resolved
- .addOptionsModification(
- options -> options.enableUnrepresentableInDexInstructionRemoval = true)
.compileWithExpectedDiagnostics(
diagnostics ->
diagnostics
@@ -124,9 +121,6 @@
|| diagnostic instanceof UnsupportedFeatureDiagnostic)
? DiagnosticsLevel.WARNING
: level)
- // TODO(b/238175192): remove again when resolved
- .addOptionsModification(
- options -> options.enableUnrepresentableInDexInstructionRemoval = true)
.compileWithExpectedDiagnostics(
diagnostics -> {
diagnostics.assertOnlyWarnings();
@@ -172,10 +166,6 @@
.setMinApi(parameters.getApiLevel())
.addKeepMainRule(MAIN_CLASS)
.allowDiagnosticMessages()
- .mapUnsupportedFeaturesToWarnings()
- // TODO(b/238175192): remove again when resolved
- .addOptionsModification(
- options -> options.enableUnrepresentableInDexInstructionRemoval = true)
.compileWithExpectedDiagnostics(
diagnostics -> {
diagnostics
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java
index 8fc32b2..3737814 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java
@@ -11,11 +11,11 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import com.android.tools.r8.GenerateLintFiles;
import com.android.tools.r8.StringResource;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification;
+import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexType;
@@ -25,6 +25,7 @@
import com.android.tools.r8.graph.GenericSignature.TypeSignature;
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecification;
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecificationParser;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.lint.GenerateLintFiles;
import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.WrapperDescriptor;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
@@ -210,11 +211,13 @@
null,
false,
minApi.getLevel());
+
+ DexApplication app =
+ libraryDesugaringSpecification.getAppForTesting(
+ new InternalOptions(factory, new Reporter()), spec.isLibraryCompilation());
MachineDesugaredLibrarySpecification specification =
- spec.toMachineSpecification(
- new InternalOptions(factory, new Reporter()),
- libraryDesugaringSpecification.getLibraryFiles(),
- Timing.empty());
+ spec.toMachineSpecification(app, Timing.empty());
+
Set<String> wrappersInSpec =
specification.getWrappers().keySet().stream()
.map(DexType::toString)
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LintFilesTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LintFilesTest.java
index 3c4a223..341ab5a 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LintFilesTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LintFilesTest.java
@@ -10,7 +10,6 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import com.android.tools.r8.GenerateLintFiles;
import com.android.tools.r8.StringResource;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
@@ -18,6 +17,7 @@
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecification;
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecificationParser;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.lint.GenerateLintFiles;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.InternalOptions;
@@ -37,6 +37,8 @@
private final LibraryDesugaringSpecification libraryDesugaringSpecification;
+ private List<String> lintContents;
+
@Parameters(name = "{0}, spec: {1}")
public static List<Object[]> data() {
return buildParameters(getTestParameters().withNoneRuntime().build(), getJdk8Jdk11());
@@ -48,78 +50,105 @@
this.libraryDesugaringSpecification = libraryDesugaringSpecification;
}
+ private boolean supportsAllMethodsOf(String type) {
+ return lintContents.contains(type);
+ }
+
+ private boolean supportsMethodButNotAllMethodsInClass(String method) {
+ assert method.contains("#");
+ return !supportsAllMethodsOf(method.split("#")[0]) && lintContents.contains(method);
+ }
+
private void checkFileContent(AndroidApiLevel minApiLevel, Path lintFile) throws Exception {
// Just do some light probing in the generated lint files.
- List<String> methods = FileUtils.readAllLines(lintFile);
+ lintContents = FileUtils.readAllLines(lintFile);
// All methods supported on Optional*.
- assertTrue(methods.contains("java/util/Optional"));
- assertTrue(methods.contains("java/util/OptionalInt"));
-
- // ConcurrentHashMap is fully supported on JDK 11.
- assertEquals(
- libraryDesugaringSpecification != JDK8,
- methods.contains("java/util/concurrent/ConcurrentHashMap"));
+ assertTrue(supportsAllMethodsOf("java/util/Optional"));
+ assertTrue(supportsAllMethodsOf("java/util/OptionalInt"));
// No parallel* methods pre L, and all stream methods supported from L.
assertEquals(
minApiLevel == AndroidApiLevel.L,
- methods.contains("java/util/Collection#parallelStream()Ljava/util/stream/Stream;"));
+ supportsMethodButNotAllMethodsInClass(
+ "java/util/Collection#parallelStream()Ljava/util/stream/Stream;"));
assertEquals(
- minApiLevel == AndroidApiLevel.L, methods.contains("java/util/stream/DoubleStream"));
+ minApiLevel == AndroidApiLevel.L, supportsAllMethodsOf("java/util/stream/DoubleStream"));
assertFalse(
- methods.contains(
+ supportsMethodButNotAllMethodsInClass(
"java/util/stream/DoubleStream#parallel()Ljava/util/stream/DoubleStream;"));
assertFalse(
- methods.contains("java/util/stream/DoubleStream#parallel()Ljava/util/stream/BaseStream;"));
+ supportsMethodButNotAllMethodsInClass(
+ "java/util/stream/DoubleStream#parallel()Ljava/util/stream/BaseStream;"));
assertEquals(
minApiLevel == AndroidApiLevel.B,
- methods.contains(
+ supportsMethodButNotAllMethodsInClass(
"java/util/stream/DoubleStream#allMatch(Ljava/util/function/DoublePredicate;)Z"));
- assertEquals(minApiLevel == AndroidApiLevel.L, methods.contains("java/util/stream/IntStream"));
+ assertEquals(
+ minApiLevel == AndroidApiLevel.L, lintContents.contains("java/util/stream/IntStream"));
assertFalse(
- methods.contains("java/util/stream/IntStream#parallel()Ljava/util/stream/IntStream;"));
+ supportsMethodButNotAllMethodsInClass(
+ "java/util/stream/IntStream#parallel()Ljava/util/stream/IntStream;"));
assertFalse(
- methods.contains("java/util/stream/IntStream#parallel()Ljava/util/stream/BaseStream;"));
+ supportsMethodButNotAllMethodsInClass(
+ "java/util/stream/IntStream#parallel()Ljava/util/stream/BaseStream;"));
assertEquals(
minApiLevel == AndroidApiLevel.B,
- methods.contains(
+ supportsMethodButNotAllMethodsInClass(
"java/util/stream/IntStream#allMatch(Ljava/util/function/IntPredicate;)Z"));
- if (libraryDesugaringSpecification != JDK8) {
- // TODO(b/203382252): Investigate why the following assertions are not working on JDK 11.
- return;
+ assertEquals(
+ libraryDesugaringSpecification != JDK8,
+ supportsAllMethodsOf("java/util/concurrent/ConcurrentHashMap"));
+
+ // Checks specific methods are supported or not in JDK8, all is supported in JDK11.
+ if (libraryDesugaringSpecification == JDK8) {
+ // Supported methods on ConcurrentHashMap.
+ assertTrue(
+ supportsMethodButNotAllMethodsInClass(
+ "java/util/concurrent/ConcurrentHashMap#getOrDefault(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"));
+
+ // Don't include constructors.
+ assertFalse(
+ supportsMethodButNotAllMethodsInClass(
+ "java/util/concurrent/ConcurrentHashMap#<init>()V"));
+
+ // Unsupported methods on ConcurrentHashMap.
+ assertFalse(
+ supportsMethodButNotAllMethodsInClass(
+ "java/util/concurrent/ConcurrentHashMap#reduce(JLjava/util/function/BiFunction;Ljava/util/function/BiFunction;)Ljava/lang/Object;"));
+ assertFalse(
+ supportsMethodButNotAllMethodsInClass(
+ "java/util/concurrent/ConcurrentHashMap#newKeySet()Ljava/util/concurrent/ConcurrentHashMap$KeySetView;"));
}
- // Supported methods on ConcurrentHashMap.
+ // Maintain type.
+ assertEquals(
+ libraryDesugaringSpecification != JDK8,
+ supportsAllMethodsOf("java/io/UncheckedIOException"));
+
+ // Retarget method.
assertTrue(
- methods.contains(
- "java/util/concurrent/ConcurrentHashMap#getOrDefault(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"));
-
- // Don't include constructors.
- assertFalse(methods.contains("java/util/concurrent/ConcurrentHashMap#<init>()V"));
-
- // Unsupported methods on ConcurrentHashMap.
- assertFalse(
- methods.contains(
- "java/util/concurrent/ConcurrentHashMap#reduce(JLjava/util/function/BiFunction;Ljava/util/function/BiFunction;)Ljava/lang/Object;"));
- assertFalse(
- methods.contains(
- "java/util/concurrent/ConcurrentHashMap#newKeySet()Ljava/util/concurrent/ConcurrentHashMap$KeySetView;"));
+ supportsMethodButNotAllMethodsInClass(
+ "java/util/Arrays#spliterator([I)Ljava/util/Spliterator$OfInt;"));
// Emulated interface default method.
- assertTrue(methods.contains("java/util/List#spliterator()Ljava/util/Spliterator;"));
+ assertTrue(
+ supportsMethodButNotAllMethodsInClass(
+ "java/util/List#spliterator()Ljava/util/Spliterator;"));
// Emulated interface static method.
- assertTrue(methods.contains("java/util/Map$Entry#comparingByValue()Ljava/util/Comparator;"));
+ assertTrue(
+ supportsMethodButNotAllMethodsInClass(
+ "java/util/Map$Entry#comparingByValue()Ljava/util/Comparator;"));
// No no-default method from emulated interface.
- assertFalse(methods.contains("java/util/List#size()I"));
+ assertFalse(supportsMethodButNotAllMethodsInClass("java/util/List#size()I"));
// File should be sorted.
- List<String> sorted = new ArrayList<>(methods);
+ List<String> sorted = new ArrayList<>(lintContents);
sorted.sort(Comparator.naturalOrder());
- assertEquals(methods, sorted);
+ assertEquals(lintContents, sorted);
}
@Test
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/TelemetryMapInterfaceTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/TelemetryMapInterfaceTest.java
new file mode 100644
index 0000000..174c311
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/TelemetryMapInterfaceTest.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.desugar.desugaredlibrary;
+
+import static com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification.SPECIFICATIONS_WITH_CF2CF;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.getJdk8Jdk11;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification;
+import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification;
+import com.android.tools.r8.utils.StringUtils;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.BiConsumer;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+// This is a reproduction for b/202188674.
+@RunWith(Parameterized.class)
+public class TelemetryMapInterfaceTest extends DesugaredLibraryTestBase {
+
+ private static final String EXPECTED_OUTPUT =
+ StringUtils.lines("k=key1;v=value1", "k=key1;v=value1", "k=key1;v=value1", "k=key1;v=value1");
+
+ private final TestParameters parameters;
+ private final CompilationSpecification compilationSpecification;
+ private final LibraryDesugaringSpecification libraryDesugaringSpecification;
+
+ @Parameters(name = "{0}, spec: {1}, {2}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build(),
+ getJdk8Jdk11(),
+ SPECIFICATIONS_WITH_CF2CF);
+ }
+
+ public TelemetryMapInterfaceTest(
+ TestParameters parameters,
+ LibraryDesugaringSpecification libraryDesugaringSpecification,
+ CompilationSpecification compilationSpecification) {
+ this.parameters = parameters;
+ this.compilationSpecification = compilationSpecification;
+ this.libraryDesugaringSpecification = libraryDesugaringSpecification;
+ }
+
+ @Test
+ public void testMap() throws Throwable {
+ testForDesugaredLibrary(parameters, libraryDesugaringSpecification, compilationSpecification)
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ Concrete concrete = new Concrete();
+ concrete.put("key1", "value1");
+ concrete.forEach((k, v) -> System.out.println("k=" + k + ";v=" + v));
+ MapInterface mapInterface = concrete;
+ mapInterface.forEach((k, v) -> System.out.println("k=" + k + ";v=" + v));
+ HashMap<String, Object> hashMap = concrete;
+ hashMap.forEach((k, v) -> System.out.println("k=" + k + ";v=" + v));
+ Map<String, Object> map = concrete;
+ map.forEach((k, v) -> System.out.println("k=" + k + ";v=" + v));
+ }
+ }
+
+ interface MapInterface {
+
+ void forEach(BiConsumer<? super String, ? super Object> consumer);
+ }
+
+ static class Concrete extends HashMap<String, Object> implements MapInterface {}
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/specification/ConvertExportReadTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/specification/ConvertExportReadTest.java
index 7c98dbe..cd5c568 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/specification/ConvertExportReadTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/specification/ConvertExportReadTest.java
@@ -4,7 +4,9 @@
package com.android.tools.r8.desugar.desugaredlibrary.specification;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.StringResource;
@@ -13,7 +15,10 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification;
+import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.ir.desugar.desugaredlibrary.ApiLevelRange;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser;
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.ir.desugar.desugaredlibrary.humanspecification.MultiAPILevelHumanDesugaredLibrarySpecification;
@@ -21,8 +26,17 @@
import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.MultiAPILevelHumanDesugaredLibrarySpecificationParser;
import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.MultiAPILevelLegacyDesugaredLibrarySpecification;
import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.MultiAPILevelLegacyDesugaredLibrarySpecificationParser;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecificationParser;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineRewritingFlags;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineTopLevelFlags;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MultiAPILevelMachineDesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MultiAPILevelMachineDesugaredLibrarySpecificationJsonExporter;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion.HumanToMachineSpecificationConverter;
import com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion.LegacyToHumanSpecificationConverter;
import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.synthesis.SyntheticNaming;
+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.Timing;
@@ -46,7 +60,7 @@
}
@Test
- public void testMultiLevel() throws IOException {
+ public void testMultiLevelLegacy() throws IOException {
Assume.assumeTrue(ToolHelper.isLocalDevelopment());
LibraryDesugaringSpecification legacySpec = LibraryDesugaringSpecification.JDK8;
@@ -61,9 +75,10 @@
options.dexItemFactory(), options.reporter)
.parseMultiLevelConfiguration(StringResource.fromFile(legacySpec.getSpecification()));
+ DexApplication app = legacySpec.getAppForTesting(options, true);
+
MultiAPILevelHumanDesugaredLibrarySpecification humanSpec1 =
- converter.convertAllAPILevels(
- spec, legacySpec.getDesugarJdkLibs(), legacySpec.getLibraryFiles(), options);
+ converter.convertAllAPILevels(spec, app);
Box<String> json = new Box<>();
MultiAPILevelHumanDesugaredLibrarySpecificationJsonExporter.export(
@@ -74,6 +89,75 @@
.parseMultiLevelConfiguration(StringResource.fromString(json.get(), Origin.unknown()));
assertSpecEquals(humanSpec1, humanSpec2);
+
+ Box<String> json2 = new Box<>();
+ HumanToMachineSpecificationConverter converter2 =
+ new HumanToMachineSpecificationConverter(Timing.empty());
+ MultiAPILevelMachineDesugaredLibrarySpecification machineSpec1 =
+ converter2.convertAllAPILevels(humanSpec2, app);
+ MultiAPILevelMachineDesugaredLibrarySpecificationJsonExporter.export(
+ machineSpec1, (string, handler) -> json2.set(string), options.dexItemFactory());
+
+ MachineDesugaredLibrarySpecification machineSpecParsed =
+ new MachineDesugaredLibrarySpecificationParser(
+ options.dexItemFactory(),
+ options.reporter,
+ true,
+ AndroidApiLevel.B.getLevel(),
+ new SyntheticNaming())
+ .parse(StringResource.fromString(json2.get(), Origin.unknown()));
+ assertFalse(machineSpecParsed.getRewriteType().isEmpty());
+ }
+
+ @Test
+ public void testSingleLevel() throws IOException {
+ Assume.assumeTrue(ToolHelper.isLocalDevelopment());
+
+ LibraryDesugaringSpecification humanSpec = LibraryDesugaringSpecification.JDK11_PATH;
+
+ InternalOptions options = new InternalOptions();
+
+ DexApplication app = humanSpec.getAppForTesting(options, true);
+
+ MultiAPILevelHumanDesugaredLibrarySpecification humanSpec2 =
+ new MultiAPILevelHumanDesugaredLibrarySpecificationParser(
+ options.dexItemFactory(), options.reporter)
+ .parseMultiLevelConfiguration(StringResource.fromFile(humanSpec.getSpecification()));
+
+ Box<String> json2 = new Box<>();
+ HumanToMachineSpecificationConverter converter2 =
+ new HumanToMachineSpecificationConverter(Timing.empty());
+ MultiAPILevelMachineDesugaredLibrarySpecification machineSpec1 =
+ converter2.convertAllAPILevels(humanSpec2, app);
+ MultiAPILevelMachineDesugaredLibrarySpecificationJsonExporter.export(
+ machineSpec1, (string, handler) -> json2.set(string), options.dexItemFactory());
+
+ for (AndroidApiLevel api :
+ new AndroidApiLevel[] {AndroidApiLevel.B, AndroidApiLevel.N, AndroidApiLevel.O}) {
+ MachineDesugaredLibrarySpecification machineSpecParsed =
+ new MachineDesugaredLibrarySpecificationParser(
+ options.dexItemFactory(),
+ options.reporter,
+ true,
+ api.getLevel(),
+ new SyntheticNaming())
+ .parse(StringResource.fromString(json2.get(), Origin.unknown()));
+
+ HumanDesugaredLibrarySpecification humanSpecB =
+ new HumanDesugaredLibrarySpecificationParser(
+ options.dexItemFactory(), options.reporter, true, api.getLevel())
+ .parse(StringResource.fromFile(humanSpec.getSpecification()));
+ MachineDesugaredLibrarySpecification machineSpecConverted =
+ humanSpecB.toMachineSpecification(app, Timing.empty());
+
+ assertSpecEquals(machineSpecConverted, machineSpecParsed);
+ }
+ }
+
+ private void assertSpecEquals(
+ MachineDesugaredLibrarySpecification spec1, MachineDesugaredLibrarySpecification spec2) {
+ assertTopLevelFlagsEquals(spec1.getTopLevelFlags(), spec2.getTopLevelFlags());
+ assertFlagsEquals(spec1.getRewritingFlags(), spec2.getRewritingFlags());
}
private void assertSpecEquals(
@@ -96,6 +180,46 @@
}
private void assertFlagsEquals(
+ MachineRewritingFlags rewritingFlags1, MachineRewritingFlags rewritingFlags2) {
+ assertEquals(rewritingFlags1.getRewriteType(), rewritingFlags2.getRewriteType());
+ assertEquals(rewritingFlags1.getMaintainType(), rewritingFlags2.getMaintainType());
+ assertEquals(
+ rewritingFlags1.getRewriteDerivedTypeOnly(), rewritingFlags2.getRewriteDerivedTypeOnly());
+ assertEquals(
+ rewritingFlags1.getStaticFieldRetarget(), rewritingFlags2.getStaticFieldRetarget());
+ assertEquals(rewritingFlags1.getCovariantRetarget(), rewritingFlags2.getCovariantRetarget());
+ assertEquals(rewritingFlags1.getStaticRetarget(), rewritingFlags2.getStaticRetarget());
+ assertEquals(
+ rewritingFlags1.getNonEmulatedVirtualRetarget(),
+ rewritingFlags2.getNonEmulatedVirtualRetarget());
+ assertEquals(
+ rewritingFlags1.getEmulatedVirtualRetarget(), rewritingFlags2.getEmulatedVirtualRetarget());
+ assertEquals(
+ rewritingFlags1.getEmulatedVirtualRetargetThroughEmulatedInterface(),
+ rewritingFlags2.getEmulatedVirtualRetargetThroughEmulatedInterface());
+ assertEquals(
+ rewritingFlags1.getApiGenericConversion().keySet(),
+ rewritingFlags2.getApiGenericConversion().keySet());
+ rewritingFlags1
+ .getApiGenericConversion()
+ .keySet()
+ .forEach(
+ k ->
+ assertArrayEquals(
+ rewritingFlags1.getApiGenericConversion().get(k),
+ rewritingFlags2.getApiGenericConversion().get(k)));
+ assertEquals(
+ rewritingFlags1.getEmulatedInterfaces().keySet(),
+ rewritingFlags2.getEmulatedInterfaces().keySet());
+ assertEquals(rewritingFlags1.getWrappers().keySet(), rewritingFlags2.getWrappers().keySet());
+ assertEquals(rewritingFlags1.getLegacyBackport(), rewritingFlags2.getLegacyBackport());
+ assertEquals(rewritingFlags1.getDontRetarget(), rewritingFlags2.getDontRetarget());
+ assertEquals(rewritingFlags1.getCustomConversions(), rewritingFlags2.getCustomConversions());
+ assertEquals(rewritingFlags1.getAmendLibraryMethod(), rewritingFlags2.getAmendLibraryMethod());
+ assertEquals(rewritingFlags1.getAmendLibraryField(), rewritingFlags2.getAmendLibraryField());
+ }
+
+ private void assertFlagsEquals(
HumanRewritingFlags humanRewritingFlags1, HumanRewritingFlags humanRewritingFlags2) {
assertEquals(humanRewritingFlags1.getRewritePrefix(), humanRewritingFlags2.getRewritePrefix());
assertEquals(
@@ -123,6 +247,20 @@
}
private void assertTopLevelFlagsEquals(
+ MachineTopLevelFlags topLevelFlags1, MachineTopLevelFlags topLevelFlags2) {
+ String kr1 = String.join("\n", topLevelFlags1.getExtraKeepRules());
+ String kr2 = String.join("\n", topLevelFlags2.getExtraKeepRules());
+ assertEquals(kr1, kr2);
+ assertEquals(topLevelFlags1.getIdentifier(), topLevelFlags2.getIdentifier());
+ assertEquals(
+ topLevelFlags1.getRequiredCompilationApiLevel().getLevel(),
+ topLevelFlags2.getRequiredCompilationApiLevel().getLevel());
+ assertEquals(
+ topLevelFlags1.getSynthesizedLibraryClassesPackagePrefix(),
+ topLevelFlags2.getSynthesizedLibraryClassesPackagePrefix());
+ }
+
+ private void assertTopLevelFlagsEquals(
HumanTopLevelFlags topLevelFlags1, HumanTopLevelFlags topLevelFlags2) {
assertEquals(topLevelFlags1.getExtraKeepRules(), topLevelFlags2.getExtraKeepRules());
assertEquals(topLevelFlags1.getIdentifier(), topLevelFlags2.getIdentifier());
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/test/LibraryDesugaringSpecification.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/test/LibraryDesugaringSpecification.java
index 70fbd07..8b8118f 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/test/LibraryDesugaringSpecification.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/test/LibraryDesugaringSpecification.java
@@ -14,13 +14,21 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.Timing;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
+import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.ExecutorService;
public class LibraryDesugaringSpecification {
@@ -258,6 +266,27 @@
return ImmutableList.of(JDK8, JDK11);
}
+ public DexApplication getAppForTesting(InternalOptions options, boolean libraryCompilation)
+ throws IOException {
+ AndroidApp.Builder builder = AndroidApp.builder();
+ if (libraryCompilation) {
+ builder.addProgramFiles(getDesugarJdkLibs());
+ }
+ AndroidApp inputApp = builder.addLibraryFiles(getLibraryFiles()).build();
+ return internalReadApp(inputApp, options);
+ }
+
+ private DexApplication internalReadApp(AndroidApp inputApp, InternalOptions options)
+ throws IOException {
+ ApplicationReader applicationReader = new ApplicationReader(inputApp, options, Timing.empty());
+ ExecutorService executorService = ThreadUtils.getExecutorService(options);
+ assert !options.ignoreJavaLibraryOverride;
+ options.ignoreJavaLibraryOverride = true;
+ DexApplication app = applicationReader.read(executorService);
+ options.ignoreJavaLibraryOverride = false;
+ return app;
+ }
+
public boolean hasEmulatedInterfaceDesugaring(TestParameters parameters) {
return parameters.getApiLevel().getLevel() < descriptor.getEmulatedInterfaceDesugaring();
}
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8CompilationTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8CompilationTest.java
deleted file mode 100644
index cb549f5..0000000
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8CompilationTest.java
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright (c) 2019, 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.nestaccesscontrol;
-
-import static junit.framework.TestCase.assertTrue;
-
-import com.android.tools.r8.TestBase;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.codeinspector.CodeInspector;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-
-@RunWith(Parameterized.class)
-public class Java11R8CompilationTest extends TestBase {
-
- public Java11R8CompilationTest(TestParameters parameters) {
- this.parameters = parameters;
- }
-
- private final TestParameters parameters;
-
- @Parameters(name = "{0}")
- public static TestParametersCollection data() {
- return getTestParameters()
- // Use of APIs, such as java.nio.file.* are only available from 26+.
- .withApiLevelsStartingAtIncluding(AndroidApiLevel.O)
- .withDexRuntimes()
- .build();
- }
-
- private static final Path MAIN_KEEP = Paths.get("src/main/keep.txt");
-
- private static void assertNoNests(CodeInspector inspector) {
- assertTrue(
- inspector.allClasses().stream().noneMatch(subj -> subj.getDexProgramClass().isInANest()));
- }
-
- @Test
- public void testR8CompiledWithR8() throws Exception {
- testForR8(parameters.getBackend())
- .setMinApi(parameters.getApiLevel())
- .addProgramFiles(ToolHelper.R8_WITH_RELOCATED_DEPS_11_JAR)
- .addKeepRuleFiles(MAIN_KEEP)
- .compile()
- .inspect(this::assertNotEmpty)
- .inspect(Java11R8CompilationTest::assertNoNests);
- }
-
- private void assertNotEmpty(CodeInspector inspector) {
- assertTrue(inspector.allClasses().size() > 0);
- }
-}
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8BootstrapTest.java b/src/test/java/com/android/tools/r8/desugar/r8bootstrap/Java11R8BootstrapTest.java
similarity index 75%
rename from src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8BootstrapTest.java
rename to src/test/java/com/android/tools/r8/desugar/r8bootstrap/Java11R8BootstrapTest.java
index 04e2842..b3a9adf 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8BootstrapTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/r8bootstrap/Java11R8BootstrapTest.java
@@ -1,12 +1,12 @@
-// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// 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.nestaccesscontrol;
+package com.android.tools.r8.desugar.r8bootstrap;
+import static com.android.tools.r8.desugar.r8bootstrap.JavaBootstrapUtils.MAIN_KEEP;
import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
import static junit.framework.TestCase.assertEquals;
-import static junit.framework.TestCase.assertTrue;
import com.android.tools.r8.Jdk11TestUtils;
import com.android.tools.r8.TestBase;
@@ -15,8 +15,6 @@
import com.android.tools.r8.TestRuntime.CfVm;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.cf.bootstrap.BootstrapCurrentEqualityTest;
-import com.android.tools.r8.utils.InternalOptions.DesugarState;
-import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.google.common.collect.ImmutableList;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -37,7 +35,6 @@
@RunWith(Parameterized.class)
public class Java11R8BootstrapTest extends TestBase {
- private static final Path MAIN_KEEP = Paths.get("src/main/keep.txt");
private static final String[] HELLO_KEEP = {
"-keep class hello.Hello { public static void main(...);}"
};
@@ -63,31 +60,10 @@
}
private static Path compileR8(boolean desugar) throws Exception {
- // Shrink R8 11 with R8
- return testForR8(TestBase.getStaticTemp(), Backend.CF)
- .addProgramFiles(ToolHelper.R8_WITH_RELOCATED_DEPS_11_JAR)
- .addLibraryFiles(Jdk11TestUtils.getJdk11LibraryFiles(getStaticTemp()))
- .addKeepRuleFiles(MAIN_KEEP)
- .applyIf(
- desugar,
- builder ->
- builder.addOptionsModification(
- options -> {
- options.desugarState = DesugarState.ON;
- }))
- .compile()
- .inspect(inspector -> assertNests(inspector, desugar))
- .writeToZip();
- }
-
- private static void assertNests(CodeInspector inspector, boolean desugar) {
- if (desugar) {
- assertTrue(
- inspector.allClasses().stream().noneMatch(subj -> subj.getDexProgramClass().isInANest()));
- } else {
- assertTrue(
- inspector.allClasses().stream().anyMatch(subj -> subj.getDexProgramClass().isInANest()));
- }
+ return JavaBootstrapUtils.compileR8(
+ ToolHelper.R8_WITH_RELOCATED_DEPS_11_JAR,
+ Jdk11TestUtils.getJdk11LibraryFiles(getStaticTemp()),
+ desugar);
}
private Path[] jarsToCompare() {
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8BootstrapTest.java b/src/test/java/com/android/tools/r8/desugar/r8bootstrap/Java17R8BootstrapTest.java
similarity index 65%
copy from src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8BootstrapTest.java
copy to src/test/java/com/android/tools/r8/desugar/r8bootstrap/Java17R8BootstrapTest.java
index 04e2842..2ed69e1 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8BootstrapTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/r8bootstrap/Java17R8BootstrapTest.java
@@ -1,12 +1,12 @@
-// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// 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.nestaccesscontrol;
+package com.android.tools.r8.desugar.r8bootstrap;
+import static com.android.tools.r8.desugar.r8bootstrap.JavaBootstrapUtils.MAIN_KEEP;
import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
import static junit.framework.TestCase.assertEquals;
-import static junit.framework.TestCase.assertTrue;
import com.android.tools.r8.Jdk11TestUtils;
import com.android.tools.r8.TestBase;
@@ -15,8 +15,6 @@
import com.android.tools.r8.TestRuntime.CfVm;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.cf.bootstrap.BootstrapCurrentEqualityTest;
-import com.android.tools.r8.utils.InternalOptions.DesugarState;
-import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.google.common.collect.ImmutableList;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -28,24 +26,23 @@
import org.junit.runners.Parameterized.Parameters;
/**
- * This test relies on a freshly built build/libs/r8_with_relocated_deps_11.jar.
+ * This test relies on a freshly built build/libs/r8_with_relocated_deps_17.jar.
*
- * <p>The test compiles Hello/R8 with the same settings using R8 compiled with Java 11 (non shrunk),
+ * <p>The test compiles Hello/R8 with the same settings using R8 compiled with Java 17 (non shrunk),
* and the same but shrunk with/without nest desugaring. All generated jars should be run correctly
* and identical.
*/
@RunWith(Parameterized.class)
-public class Java11R8BootstrapTest extends TestBase {
+public class Java17R8BootstrapTest extends TestBase {
- private static final Path MAIN_KEEP = Paths.get("src/main/keep.txt");
private static final String[] HELLO_KEEP = {
"-keep class hello.Hello { public static void main(...);}"
};
- private static Path r8Lib11NoDesugar;
- private static Path r8Lib11Desugar;
+ private static Path r8Lib17NoDesugar;
+ private static Path r8Lib17Desugar;
- public Java11R8BootstrapTest(TestParameters parameters) {
+ public Java17R8BootstrapTest(TestParameters parameters) {
this.parameters = parameters;
}
@@ -53,54 +50,39 @@
@Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withCfRuntimesStartingFromIncluding(CfVm.JDK11).build();
+ return getTestParameters().withCfRuntimesStartingFromIncluding(CfVm.JDK17).build();
+ }
+
+ private static boolean supportsSealedClassesWhenGeneratingCf() {
+ // TODO(b/227160052): When this is true enable this test.
+ return false;
}
@BeforeClass
public static void beforeAll() throws Exception {
- r8Lib11NoDesugar = compileR8(false);
- r8Lib11Desugar = compileR8(true);
+ if (!supportsSealedClassesWhenGeneratingCf()) {
+ return;
+ }
+ r8Lib17NoDesugar = compileR8(false);
+ r8Lib17Desugar = compileR8(true);
}
private static Path compileR8(boolean desugar) throws Exception {
- // Shrink R8 11 with R8
- return testForR8(TestBase.getStaticTemp(), Backend.CF)
- .addProgramFiles(ToolHelper.R8_WITH_RELOCATED_DEPS_11_JAR)
- .addLibraryFiles(Jdk11TestUtils.getJdk11LibraryFiles(getStaticTemp()))
- .addKeepRuleFiles(MAIN_KEEP)
- .applyIf(
- desugar,
- builder ->
- builder.addOptionsModification(
- options -> {
- options.desugarState = DesugarState.ON;
- }))
- .compile()
- .inspect(inspector -> assertNests(inspector, desugar))
- .writeToZip();
- }
-
- private static void assertNests(CodeInspector inspector, boolean desugar) {
- if (desugar) {
- assertTrue(
- inspector.allClasses().stream().noneMatch(subj -> subj.getDexProgramClass().isInANest()));
- } else {
- assertTrue(
- inspector.allClasses().stream().anyMatch(subj -> subj.getDexProgramClass().isInANest()));
- }
+ return JavaBootstrapUtils.compileR8(
+ ToolHelper.R8_WITH_RELOCATED_DEPS_17_JAR,
+ Jdk11TestUtils.getJdk11LibraryFiles(getStaticTemp()),
+ desugar);
}
private Path[] jarsToCompare() {
- return new Path[] {
- ToolHelper.R8_WITH_RELOCATED_DEPS_11_JAR,
- r8Lib11NoDesugar,
- r8Lib11Desugar
- };
+ return new Path[] {ToolHelper.R8_WITH_RELOCATED_DEPS_17_JAR, r8Lib17NoDesugar, r8Lib17Desugar};
}
@Test
public void testHello() throws Exception {
Assume.assumeTrue(!ToolHelper.isWindows());
+ Assume.assumeTrue(JavaBootstrapUtils.exists(ToolHelper.R8_WITH_RELOCATED_DEPS_17_JAR));
+ Assume.assumeTrue(supportsSealedClassesWhenGeneratingCf());
Path prevGeneratedJar = null;
String prevRunResult = null;
for (Path jar : jarsToCompare()) {
@@ -131,7 +113,9 @@
public void testR8() throws Exception {
Assume.assumeTrue(!ToolHelper.isWindows());
Assume.assumeTrue(parameters.isCfRuntime());
- Assume.assumeTrue(CfVm.JDK11.lessThanOrEqual(parameters.getRuntime().asCf().getVm()));
+ Assume.assumeTrue(CfVm.JDK17.lessThanOrEqual(parameters.getRuntime().asCf().getVm()));
+ Assume.assumeTrue(JavaBootstrapUtils.exists(ToolHelper.R8_WITH_RELOCATED_DEPS_17_JAR));
+ Assume.assumeTrue(supportsSealedClassesWhenGeneratingCf());
Path prevGeneratedJar = null;
for (Path jar : jarsToCompare()) {
Path generatedJar =
diff --git a/src/test/java/com/android/tools/r8/desugar/r8bootstrap/JavaBootstrapUtils.java b/src/test/java/com/android/tools/r8/desugar/r8bootstrap/JavaBootstrapUtils.java
new file mode 100644
index 0000000..63d7dde
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/r8bootstrap/JavaBootstrapUtils.java
@@ -0,0 +1,57 @@
+// 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.r8bootstrap;
+
+import static junit.framework.TestCase.assertTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.utils.InternalOptions.DesugarState;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+public class JavaBootstrapUtils extends TestBase {
+
+ static final Path MAIN_KEEP = Paths.get("src/main/keep.txt");
+
+ static boolean exists(Path r8WithRelocatedDeps) {
+ // This test runs only if the dependencies have been generated using:
+ // <code> tools/gradle.py r8WithRelocatedDeps17 r8WithRelocatedDeps11 </code>
+ return Files.exists(r8WithRelocatedDeps);
+ }
+
+ static Path compileR8(Path r8WithRelocatedDeps, Path[] libraryFiles, boolean desugar)
+ throws Exception {
+ if (!exists(r8WithRelocatedDeps)) {
+ return null;
+ }
+ // Shrink R8 11 with R8
+ return testForR8(getStaticTemp(), Backend.CF)
+ .addProgramFiles(r8WithRelocatedDeps)
+ .addLibraryFiles(libraryFiles)
+ .addKeepRuleFiles(MAIN_KEEP)
+ .applyIf(
+ desugar,
+ builder ->
+ builder.addOptionsModification(
+ options -> {
+ options.desugarState = DesugarState.ON;
+ }))
+ .compile()
+ .inspect(inspector -> assertNests(inspector, desugar))
+ .writeToZip();
+ }
+
+ private static void assertNests(CodeInspector inspector, boolean desugar) {
+ if (desugar) {
+ assertTrue(
+ inspector.allClasses().stream().noneMatch(subj -> subj.getDexProgramClass().isInANest()));
+ } else {
+ assertTrue(
+ inspector.allClasses().stream().anyMatch(subj -> subj.getDexProgramClass().isInANest()));
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11D8CompilationTest.java b/src/test/java/com/android/tools/r8/desugar/r8bootstrap/JavaD8CompilationTest.java
similarity index 78%
rename from src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11D8CompilationTest.java
rename to src/test/java/com/android/tools/r8/desugar/r8bootstrap/JavaD8CompilationTest.java
index a4c0ca7..1970880 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11D8CompilationTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/r8bootstrap/JavaD8CompilationTest.java
@@ -1,8 +1,8 @@
-// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// 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.nestaccesscontrol;
+package com.android.tools.r8.desugar.r8bootstrap;
import static com.android.tools.r8.utils.FileUtils.CLASS_EXTENSION;
import static com.android.tools.r8.utils.InternalOptions.ASM_VERSION;
@@ -12,7 +12,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;
import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.references.Reference;
@@ -20,9 +19,12 @@
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.ZipUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
+import java.util.List;
+import org.junit.Assume;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -32,15 +34,21 @@
import org.objectweb.asm.ClassVisitor;
@RunWith(Parameterized.class)
-public class Java11D8CompilationTest extends TestBase {
+public class JavaD8CompilationTest extends TestBase {
- public Java11D8CompilationTest(TestParameters parameters) {
+ private final Path r8WithRelocatedDeps;
+
+ public JavaD8CompilationTest(TestParameters parameters, Path r8WithRelocatedDeps) {
parameters.assertNoneRuntime();
+ this.r8WithRelocatedDeps = r8WithRelocatedDeps;
}
- @Parameters(name = "{0}")
- public static TestParametersCollection data() {
- return getTestParameters().withNoneRuntime().build();
+ @Parameters(name = "{0}, java: {1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withNoneRuntime().build(),
+ ImmutableList.of(
+ ToolHelper.R8_WITH_RELOCATED_DEPS_11_JAR, ToolHelper.R8_WITH_RELOCATED_DEPS_17_JAR));
}
private static void assertNoNests(CodeInspector inspector) {
@@ -50,22 +58,24 @@
@Test
public void testR8CompiledWithD8() throws Exception {
+ Assume.assumeTrue(JavaBootstrapUtils.exists(r8WithRelocatedDeps));
testForD8()
- .addProgramFiles(ToolHelper.R8_WITH_RELOCATED_DEPS_11_JAR)
+ .addProgramFiles(r8WithRelocatedDeps)
.addLibraryFiles(ToolHelper.getJava8RuntimeJar())
.compile()
- .inspect(Java11D8CompilationTest::assertNoNests);
+ .inspect(JavaD8CompilationTest::assertNoNests);
}
@Test
public void testR8CompiledWithD8ToCf() throws Exception {
+ Assume.assumeTrue(JavaBootstrapUtils.exists(r8WithRelocatedDeps));
Path r8Desugared =
testForD8(Backend.CF)
- .addProgramFiles(ToolHelper.R8_WITH_RELOCATED_DEPS_11_JAR)
+ .addProgramFiles(r8WithRelocatedDeps)
.addLibraryFiles(ToolHelper.getJava8RuntimeJar())
.setMinApi(AndroidApiLevel.B)
.compile()
- .inspect(Java11D8CompilationTest::assertNoNests)
+ .inspect(JavaD8CompilationTest::assertNoNests)
.writeToZip();
// Check that the desugared classes has the expected class file versions and that no nest
diff --git a/src/test/java/com/android/tools/r8/desugar/r8bootstrap/JavaR8CompilationTest.java b/src/test/java/com/android/tools/r8/desugar/r8bootstrap/JavaR8CompilationTest.java
new file mode 100644
index 0000000..09cb049
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/r8bootstrap/JavaR8CompilationTest.java
@@ -0,0 +1,69 @@
+// 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.r8bootstrap;
+
+import static junit.framework.TestCase.assertTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import org.junit.Assume;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class JavaR8CompilationTest extends TestBase {
+
+ private final TestParameters parameters;
+ private final Path r8WithRelocatedDeps;
+
+ public JavaR8CompilationTest(TestParameters parameters, Path r8WithRelocatedDeps) {
+ this.parameters = parameters;
+ this.r8WithRelocatedDeps = r8WithRelocatedDeps;
+ }
+
+ @Parameters(name = "{0}, java: {1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters()
+ // Use of APIs, such as java.nio.file.* are only available from 26+.
+ .withApiLevelsStartingAtIncluding(AndroidApiLevel.O)
+ .withDexRuntimes()
+ .build(),
+ ImmutableList.of(
+ ToolHelper.R8_WITH_RELOCATED_DEPS_11_JAR, ToolHelper.R8_WITH_RELOCATED_DEPS_17_JAR));
+ }
+
+ private static final Path MAIN_KEEP = Paths.get("src/main/keep.txt");
+
+ private static void assertNoNests(CodeInspector inspector) {
+ assertTrue(
+ inspector.allClasses().stream().noneMatch(subj -> subj.getDexProgramClass().isInANest()));
+ }
+
+ @Test
+ public void testR8CompiledWithR8() throws Exception {
+ Assume.assumeTrue(JavaBootstrapUtils.exists(r8WithRelocatedDeps));
+ testForR8(parameters.getBackend())
+ .setMinApi(parameters.getApiLevel())
+ .addProgramFiles(r8WithRelocatedDeps)
+ .addKeepRuleFiles(MAIN_KEEP)
+ .compile()
+ .inspect(this::assertNotEmpty)
+ .inspect(JavaR8CompilationTest::assertNoNests);
+ }
+
+ private void assertNotEmpty(CodeInspector inspector) {
+ assertTrue(inspector.allClasses().size() > 0);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordClasspathTest.java b/src/test/java/com/android/tools/r8/desugar/records/RecordClasspathTest.java
index 7c2ae96..6860b8b 100644
--- a/src/test/java/com/android/tools/r8/desugar/records/RecordClasspathTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/records/RecordClasspathTest.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestRuntime.CfVm;
import com.android.tools.r8.synthesis.globals.GlobalSyntheticsTestingConsumer;
+import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import java.util.List;
@@ -34,19 +35,35 @@
private static final String EXPECTED_RESULT = StringUtils.lines("Hello");
private final TestParameters parameters;
+ private final boolean stripClasspath;
- public RecordClasspathTest(TestParameters parameters) {
+ public RecordClasspathTest(TestParameters parameters, boolean stripClasspath) {
this.parameters = parameters;
+ this.stripClasspath = stripClasspath;
}
- @Parameterized.Parameters(name = "{0}")
+ @Parameterized.Parameters(name = "{0}, strip: {1}")
public static List<Object[]> data() {
return buildParameters(
getTestParameters()
.withCfRuntimesStartingFromIncluding(CfVm.JDK17)
.withDexRuntimes()
.withAllApiLevelsAlsoForCf()
- .build());
+ .build(),
+ BooleanUtils.values());
+ }
+
+ private byte[][] getClasspathData() {
+ return stripClasspath ? stripFields(PROGRAM_DATA_1) : PROGRAM_DATA_1;
+ }
+
+ private byte[][] stripFields(byte[][] programData1) {
+ byte[][] bytes = new byte[programData1.length][];
+ for (int i = 0; i < programData1.length; i++) {
+ bytes[i] =
+ transformer(programData1[i], null).removeFields((a, b, c, d, e) -> true).transform();
+ }
+ return bytes;
}
@Test
@@ -54,13 +71,13 @@
if (parameters.isCfRuntime()) {
testForJvm()
.addProgramClasses(TestClass.class)
- .addClasspathClassFileData(PROGRAM_DATA_1)
+ .addClasspathClassFileData(getClasspathData())
.run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutput(EXPECTED_RESULT);
}
testForD8(parameters.getBackend())
.addProgramClasses(TestClass.class)
- .addClasspathClassFileData(PROGRAM_DATA_1)
+ .addClasspathClassFileData(getClasspathData())
.setMinApi(parameters.getApiLevel())
.compile()
.inspect(this::assertNoRecord)
@@ -74,7 +91,7 @@
Assume.assumeFalse(parameters.isCfRuntime());
testForD8(parameters.getBackend())
.addProgramClasses(TestClass.class)
- .addClasspathClassFileData(PROGRAM_DATA_1)
+ .addClasspathClassFileData(getClasspathData())
.setMinApi(parameters.getApiLevel())
.setIntermediate(true)
.setOutputMode(OutputMode.DexFilePerClassFile)
@@ -91,7 +108,7 @@
R8FullTestBuilder builder =
testForR8(parameters.getBackend())
.addProgramClasses(TestClass.class)
- .addClasspathClassFileData(PROGRAM_DATA_1)
+ .addClasspathClassFileData(getClasspathData())
.setMinApi(parameters.getApiLevel())
.addKeepMainRule(TestClass.class);
if (parameters.isCfRuntime()) {
diff --git a/src/test/java/com/android/tools/r8/diagnostics/UnsupportedFeaturesDiagnosticsTest.java b/src/test/java/com/android/tools/r8/diagnostics/UnsupportedFeaturesDiagnosticsTest.java
index 8133dc2..1901f24 100644
--- a/src/test/java/com/android/tools/r8/diagnostics/UnsupportedFeaturesDiagnosticsTest.java
+++ b/src/test/java/com/android/tools/r8/diagnostics/UnsupportedFeaturesDiagnosticsTest.java
@@ -68,7 +68,7 @@
parameters.assertNoneRuntime();
}
- @Test(expected = CompilationFailedException.class)
+ @Test
public void testInvokeLambdaMetafactory() throws Exception {
testForD8()
.addProgramClassesAndInnerClasses(LambdaMetafactoryTest.class)
@@ -77,8 +77,8 @@
.compileWithExpectedDiagnostics(
diagnostics -> {
diagnostics
- .assertOnlyErrors()
- .assertErrorsMatch(
+ .assertOnlyWarnings()
+ .assertWarningsMatch(
allOf(
matches("invoke-custom"),
diagnosticMessage(startsWith(AGP_INVOKE_CUSTOM))));
diff --git a/src/test/java/com/android/tools/r8/naming/SeedMapperTests.java b/src/test/java/com/android/tools/r8/naming/SeedMapperTests.java
index d420618..e7511bd 100644
--- a/src/test/java/com/android/tools/r8/naming/SeedMapperTests.java
+++ b/src/test/java/com/android/tools/r8/naming/SeedMapperTests.java
@@ -10,7 +10,6 @@
import com.android.tools.r8.Diagnostic;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestDiagnosticMessagesImpl;
-import com.android.tools.r8.position.TextPosition;
import com.android.tools.r8.utils.AbortException;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.Reporter;
@@ -58,7 +57,7 @@
assertEquals(
String.format(ProguardMapError.DUPLICATE_SOURCE_MESSAGE, "A.B.C"),
diagnostic.getDiagnosticMessage());
- assertEquals(2, ((TextPosition) diagnostic.getPosition()).getLine());
+ assertEquals("line 2", diagnostic.getPosition().getDescription());
}
}
@@ -81,7 +80,7 @@
assertEquals(
String.format(ProguardMapError.DUPLICATE_SOURCE_MESSAGE, "int aaaa(B)"),
diagnostic.getDiagnosticMessage());
- assertEquals(3, ((TextPosition) diagnostic.getPosition()).getLine());
+ assertEquals("line 3", diagnostic.getPosition().getDescription());
}
}
@@ -104,7 +103,7 @@
assertEquals(
String.format(ProguardMapError.DUPLICATE_SOURCE_MESSAGE, "int aaaa"),
diagnostic.getDiagnosticMessage());
- assertEquals(3, ((TextPosition) diagnostic.getPosition()).getLine());
+ assertEquals("line 3", diagnostic.getPosition().getDescription());
}
}
@@ -122,7 +121,7 @@
assertEquals(
String.format(ProguardMapError.DUPLICATE_TARGET_MESSAGE, "A.B.D", "A.B.C", "a"),
diagnostic.getDiagnosticMessage());
- assertEquals(2, ((TextPosition) diagnostic.getPosition()).getLine());
+ assertEquals("line 2", diagnostic.getPosition().getDescription());
}
}
@@ -161,7 +160,7 @@
assertEquals(
String.format(ProguardMapError.DUPLICATE_TARGET_MESSAGE, "int bar(A)", "int foo(A)", "a"),
diagnostic.getDiagnosticMessage());
- assertEquals(2, ((TextPosition) diagnostic.getPosition()).getLine());
+ assertEquals("line 2", diagnostic.getPosition().getDescription());
}
}
diff --git a/src/test/java/com/android/tools/r8/relocator/RelocatorTest.java b/src/test/java/com/android/tools/r8/relocator/RelocatorTest.java
index e891067..08516ec 100644
--- a/src/test/java/com/android/tools/r8/relocator/RelocatorTest.java
+++ b/src/test/java/com/android/tools/r8/relocator/RelocatorTest.java
@@ -56,6 +56,7 @@
}
public RelocatorTest(TestParameters parameters, boolean external) {
+ parameters.assertNoneRuntime();
this.external = external;
}
@@ -275,15 +276,15 @@
}
@Test
- public void testNest() throws IOException, CompilationFailedException, ExecutionException {
+ public void testNest() throws IOException, CompilationFailedException {
String originalPrefix = "com.android.tools.r8";
String newPrefix = "com.android.tools.r8";
Path output = temp.newFile("output.jar").toPath();
Map<String, String> mapping = new HashMap<>();
mapping.put(originalPrefix, newPrefix);
- runRelocator(ToolHelper.R8_WITH_DEPS_11_JAR, mapping, output);
+ runRelocator(ToolHelper.R8_WITH_DEPS_17_JAR, mapping, output);
// Assert that all classes are the same, have the same methods and nest info.
- CodeInspector originalInspector = new CodeInspector(ToolHelper.R8_WITH_DEPS_11_JAR);
+ CodeInspector originalInspector = new CodeInspector(ToolHelper.R8_WITH_DEPS_17_JAR);
CodeInspector relocatedInspector = new CodeInspector(output);
for (FoundClassSubject originalSubject : originalInspector.allClasses()) {
ClassSubject relocatedSubject = relocatedInspector.clazz(originalSubject.getFinalName());
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageClassWithKeepPackageNameOnTargetTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageClassWithKeepPackageNameOnTargetTest.java
new file mode 100644
index 0000000..ce8088f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageClassWithKeepPackageNameOnTargetTest.java
@@ -0,0 +1,77 @@
+// 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.repackage;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.startsWith;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertNotEquals;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class RepackageClassWithKeepPackageNameOnTargetTest extends RepackageTestBase {
+
+ private static final String DESTINATION_PACKAGE = "other.package";
+
+ public RepackageClassWithKeepPackageNameOnTargetTest(
+ String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
+ super(flattenPackageHierarchyOrRepackageClasses, parameters);
+ }
+
+ @Override
+ protected String getRepackagePackage() {
+ return DESTINATION_PACKAGE;
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ // TODO(b/241220445): Should be able to relocate when having -dontobfuscate in some way to
+ // support mainline.
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(Main.class)
+ .enableInliningAnnotations()
+ .apply(this::configureRepackaging)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Foo::foo()")
+ .inspect(
+ inspector -> {
+ ClassSubject clazz = inspector.clazz(Foo.class);
+ assertThat(clazz, isPresent());
+ assertThat(clazz.getFinalName(), startsWith(DESTINATION_PACKAGE));
+ String relocatedPackageSuffix =
+ DescriptorUtils.getPackageBinaryNameFromJavaType(
+ clazz.getFinalName().substring(DESTINATION_PACKAGE.length() + 1));
+ String originalPackage =
+ DescriptorUtils.getPackageBinaryNameFromJavaType(clazz.getOriginalName());
+ // TODO(b/241220445): Have a configuration where the suffix is identicial to the
+ // original.
+ assertNotEquals(relocatedPackageSuffix, originalPackage);
+ });
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ Foo.foo();
+ }
+ }
+
+ public static class Foo {
+
+ @NeverInline
+ public static void foo() {
+ System.out.println("Foo::foo()");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceTests.java b/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
index 4602712..c62aa08 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
@@ -508,6 +508,7 @@
ProguardMappingSupplier.builder()
.setProguardMapProducer(
ProguardMapProducer.fromString(stackTraceForTest.mapping()))
+ .setLoadAllDefinitions(!stream)
.setAllowExperimental(allowExperimentalMapping)
.build())
.setRetracedStackTraceConsumer(
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiInferSourceFileTest.java b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiInferSourceFileTest.java
index 5563fea..08d73db 100644
--- a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiInferSourceFileTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiInferSourceFileTest.java
@@ -33,7 +33,7 @@
public static class ApiTest implements RetraceApiBinaryTest {
- private final String mapping =
+ private static final String mapping =
"some.Class -> a:\n"
+ " 1:3:int strawberry(int):99:101 -> s\n"
+ " 4:5:int mango(float):121:122 -> s";
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiInlineInOutlineTest.java b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiInlineInOutlineTest.java
index 8a86283..793a22d 100644
--- a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiInlineInOutlineTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiInlineInOutlineTest.java
@@ -6,6 +6,7 @@
import static org.junit.Assert.assertEquals;
+import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.Reference;
@@ -62,11 +63,11 @@
@Test
public void test() {
- ProguardMappingSupplier mappingProvider =
+ ProguardMappingSupplier mappingSupplier =
ProguardMappingSupplier.builder()
.setProguardMapProducer(ProguardMapProducer.fromString(mapping))
.build();
- Retracer retracer = Retracer.builder().setMappingSupplier(mappingProvider).build();
+ Retracer retracer = mappingSupplier.createRetracer(new DiagnosticsHandler() {});
List<RetraceFrameElement> outlineRetraced =
retracer
.retraceFrame(
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiOutlineInOutlineStackTrace.java b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiOutlineInOutlineStackTrace.java
index cfefe61..c037782 100644
--- a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiOutlineInOutlineStackTrace.java
+++ b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiOutlineInOutlineStackTrace.java
@@ -6,6 +6,7 @@
import static org.junit.Assert.assertEquals;
+import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.MethodReference;
@@ -70,11 +71,11 @@
@Test
public void test() {
- ProguardMappingSupplier mappingProvider =
+ ProguardMappingSupplier mappingSupplier =
ProguardMappingSupplier.builder()
.setProguardMapProducer(ProguardMapProducer.fromString(mapping))
.build();
- Retracer retracer = Retracer.builder().setMappingSupplier(mappingProvider).build();
+ Retracer retracer = mappingSupplier.createRetracer(new DiagnosticsHandler() {});
// Retrace the first outline.
RetraceStackTraceContext outlineContext =
retraceOutline(
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiOutlineInlineTest.java b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiOutlineInlineTest.java
index 34f1217..0479f47 100644
--- a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiOutlineInlineTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiOutlineInlineTest.java
@@ -6,6 +6,7 @@
import static org.junit.Assert.assertEquals;
+import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.Reference;
@@ -65,7 +66,7 @@
ProguardMappingSupplier.builder()
.setProguardMapProducer(ProguardMapProducer.fromString(mapping))
.build();
- Retracer retracer = Retracer.builder().setMappingSupplier(mappingSupplier).build();
+ Retracer retracer = mappingSupplier.createRetracer(new DiagnosticsHandler() {});
List<RetraceFrameElement> outlineRetraced =
retracer
.retraceFrame(
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiOutlineNoInlineTest.java b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiOutlineNoInlineTest.java
index 38736ce..93611eb 100644
--- a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiOutlineNoInlineTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiOutlineNoInlineTest.java
@@ -7,6 +7,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.Reference;
@@ -61,11 +62,11 @@
@Test
public void test() {
- ProguardMappingSupplier mappingProvider =
+ ProguardMappingSupplier mappingSupplier =
ProguardMappingSupplier.builder()
.setProguardMapProducer(ProguardMapProducer.fromString(mapping))
.build();
- Retracer retracer = Retracer.builder().setMappingSupplier(mappingProvider).build();
+ Retracer retracer = mappingSupplier.createRetracer(new DiagnosticsHandler() {});
List<RetraceFrameElement> outlineRetraced =
retracer
.retraceFrame(
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiRewriteFrameInlineNpeResidualTest.java b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiRewriteFrameInlineNpeResidualTest.java
index 76b0ff4..de43fae 100644
--- a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiRewriteFrameInlineNpeResidualTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiRewriteFrameInlineNpeResidualTest.java
@@ -7,6 +7,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
+import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.Reference;
@@ -79,11 +80,11 @@
@Test
public void testUsingObfuscatedName() {
- ProguardMappingSupplier mappingProvider =
+ ProguardMappingSupplier mappingSupplier =
ProguardMappingSupplier.builder()
.setProguardMapProducer(ProguardMapProducer.fromString(mapping))
.build();
- Retracer retracer = Retracer.builder().setMappingSupplier(mappingProvider).build();
+ Retracer retracer = mappingSupplier.createRetracer(new DiagnosticsHandler() {});
List<RetraceThrownExceptionElement> npeRetraced =
retracer.retraceThrownException(renamedException).stream().collect(Collectors.toList());
assertEquals(1, npeRetraced.size());
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiRewriteFrameInlineNpeTest.java b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiRewriteFrameInlineNpeTest.java
index 4334060..b244742 100644
--- a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiRewriteFrameInlineNpeTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiRewriteFrameInlineNpeTest.java
@@ -59,15 +59,11 @@
@Test
public void testFirstStackLineIsRemoved() {
TestDiagnosticsHandler testDiagnosticsHandler = new TestDiagnosticsHandler();
- ProguardMappingSupplier mappingProvider =
+ ProguardMappingSupplier mappingSupplier =
ProguardMappingSupplier.builder()
.setProguardMapProducer(ProguardMapProducer.fromString(mapping))
.build();
- Retracer retracer =
- Retracer.builder()
- .setMappingSupplier(mappingProvider)
- .setDiagnosticsHandler(testDiagnosticsHandler)
- .build();
+ Retracer retracer = mappingSupplier.createRetracer(testDiagnosticsHandler);
List<RetraceThrownExceptionElement> npeRetraced =
retracer.retraceThrownException(Reference.classFromDescriptor(npeDescriptor)).stream()
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetracePartitionRoundTripInlineTest.java b/src/test/java/com/android/tools/r8/retrace/api/RetracePartitionRoundTripInlineTest.java
index 3bc2eb2..5d78d68 100644
--- a/src/test/java/com/android/tools/r8/retrace/api/RetracePartitionRoundTripInlineTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/api/RetracePartitionRoundTripInlineTest.java
@@ -66,9 +66,10 @@
@Test
public void test() throws IOException {
ProguardMapProducer proguardMapProducer = ProguardMapProducer.fromString(mapping);
+ DiagnosticsHandler diagnosticsHandler = new DiagnosticsHandler() {};
Map<String, byte[]> partitions = new HashMap<>();
MappingPartitionMetadata metadataData =
- ProguardMapPartitioner.builder(new DiagnosticsHandler() {})
+ ProguardMapPartitioner.builder(diagnosticsHandler)
.setProguardMapProducer(proguardMapProducer)
.setPartitionConsumer(
partition -> partitions.put(partition.getKey(), partition.getPayload()))
@@ -91,7 +92,8 @@
})
.build();
assertEquals(0, prepareCounter);
- Retracer retracer = Retracer.builder().setMappingSupplier(mappingSupplier).build();
+ mappingSupplier.registerClassUse(diagnosticsHandler, callerRenamed);
+ Retracer retracer = mappingSupplier.createRetracer(diagnosticsHandler);
List<RetraceFrameElement> callerRetraced =
retracer
.retraceFrame(
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetracePartitionRoundTripTest.java b/src/test/java/com/android/tools/r8/retrace/api/RetracePartitionRoundTripTest.java
index 0eac918..732c1ce 100644
--- a/src/test/java/com/android/tools/r8/retrace/api/RetracePartitionRoundTripTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/api/RetracePartitionRoundTripTest.java
@@ -75,8 +75,9 @@
public void test() throws IOException {
ProguardMapProducer proguardMapProducer = ProguardMapProducer.fromString(mapping);
Map<String, byte[]> partitions = new HashMap<>();
+ DiagnosticsHandler diagnosticsHandler = new DiagnosticsHandler() {};
MappingPartitionMetadata metadataData =
- ProguardMapPartitioner.builder(new DiagnosticsHandler() {})
+ ProguardMapPartitioner.builder(diagnosticsHandler)
.setProguardMapProducer(proguardMapProducer)
.setPartitionConsumer(
partition -> partitions.put(partition.getKey(), partition.getPayload()))
@@ -98,7 +99,8 @@
})
.build();
assertEquals(0, prepareCounter);
- Retracer retracer = Retracer.builder().setMappingSupplier(mappingSupplier).build();
+ mappingSupplier.registerClassUse(diagnosticsHandler, outlineRenamed);
+ Retracer retracer = mappingSupplier.createRetracer(diagnosticsHandler);
List<RetraceFrameElement> outlineRetraced =
retracer
.retraceFrame(
@@ -130,6 +132,8 @@
assertEquals(0, rewrittenReferences.size());
// Retrace the outline position
+ mappingSupplier.registerClassUse(diagnosticsHandler, callsiteRenamed);
+ retracer = mappingSupplier.createRetracer(diagnosticsHandler);
RetraceStackTraceContext context = retraceFrameElement.getRetraceStackTraceContext();
List<RetraceFrameElement> retraceOutlineCallee =
retracer
diff --git a/src/test/java/com/android/tools/r8/startup/StartupInstrumentationTest.java b/src/test/java/com/android/tools/r8/startup/StartupInstrumentationTest.java
index 6047b63..07f4487 100644
--- a/src/test/java/com/android/tools/r8/startup/StartupInstrumentationTest.java
+++ b/src/test/java/com/android/tools/r8/startup/StartupInstrumentationTest.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.experimental.startup.StartupClass;
import com.android.tools.r8.experimental.startup.StartupItem;
import com.android.tools.r8.experimental.startup.StartupMethod;
import com.android.tools.r8.references.ClassReference;
@@ -74,17 +75,17 @@
return ImmutableList.of("foo");
}
- private List<StartupMethod<ClassReference, MethodReference>> getExpectedStartupList()
+ private List<StartupItem<ClassReference, MethodReference, ?>> getExpectedStartupList()
throws NoSuchMethodException {
return ImmutableList.of(
- StartupMethod.referenceBuilder()
- .setMethodReference(MethodReferenceUtils.classConstructor(Main.class))
+ StartupClass.referenceBuilder()
+ .setClassReference(Reference.classFromClass(Main.class))
.build(),
StartupMethod.referenceBuilder()
.setMethodReference(MethodReferenceUtils.mainMethod(Main.class))
.build(),
- StartupMethod.referenceBuilder()
- .setMethodReference(MethodReferenceUtils.classConstructor(AStartupClass.class))
+ StartupClass.referenceBuilder()
+ .setClassReference(Reference.classFromClass(AStartupClass.class))
.build(),
StartupMethod.referenceBuilder()
.setMethodReference(
diff --git a/src/test/java/com/android/tools/r8/startup/StartupSyntheticPlacementTest.java b/src/test/java/com/android/tools/r8/startup/StartupSyntheticPlacementTest.java
index 5e25d36..7ae582f 100644
--- a/src/test/java/com/android/tools/r8/startup/StartupSyntheticPlacementTest.java
+++ b/src/test/java/com/android/tools/r8/startup/StartupSyntheticPlacementTest.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.experimental.startup.StartupClass;
import com.android.tools.r8.experimental.startup.StartupItem;
import com.android.tools.r8.experimental.startup.StartupMethod;
import com.android.tools.r8.graph.DexProgramClass;
@@ -96,25 +97,25 @@
return ImmutableList.of("A", "B", "C");
}
- private List<StartupMethod<ClassReference, MethodReference>> getExpectedStartupList()
+ private List<StartupItem<ClassReference, MethodReference, ?>> getExpectedStartupList()
throws NoSuchMethodException {
- ImmutableList.Builder<StartupMethod<ClassReference, MethodReference>> builder =
+ ImmutableList.Builder<StartupItem<ClassReference, MethodReference, ?>> builder =
ImmutableList.builder();
builder.add(
- StartupMethod.referenceBuilder()
- .setMethodReference(MethodReferenceUtils.classConstructor(Main.class))
+ StartupClass.referenceBuilder()
+ .setClassReference(Reference.classFromClass(Main.class))
.build(),
StartupMethod.referenceBuilder()
.setMethodReference(MethodReferenceUtils.mainMethod(Main.class))
.build(),
- StartupMethod.referenceBuilder()
- .setMethodReference(MethodReferenceUtils.classConstructor(A.class))
+ StartupClass.referenceBuilder()
+ .setClassReference(Reference.classFromClass(A.class))
.build(),
StartupMethod.referenceBuilder()
.setMethodReference(Reference.methodFromMethod(A.class.getDeclaredMethod("a")))
.build(),
- StartupMethod.referenceBuilder()
- .setMethodReference(MethodReferenceUtils.classConstructor(B.class))
+ StartupClass.referenceBuilder()
+ .setClassReference(Reference.classFromClass(B.class))
.build(),
StartupMethod.referenceBuilder()
.setMethodReference(
@@ -122,21 +123,8 @@
.build());
if (useLambda) {
builder.add(
- StartupMethod.referenceBuilder()
- .setMethodReference(MethodReferenceUtils.classConstructor(B.class))
- .setSynthetic()
- .build(),
- StartupMethod.referenceBuilder()
- .setMethodReference(MethodReferenceUtils.instanceConstructor(B.class))
- .setSynthetic()
- .build(),
- StartupMethod.referenceBuilder()
- .setMethodReference(
- Reference.method(
- Reference.classFromClass(B.class),
- "accept",
- ImmutableList.of(Reference.classFromClass(Object.class)),
- null))
+ StartupClass.referenceBuilder()
+ .setClassReference(Reference.classFromClass(B.class))
.setSynthetic()
.build(),
StartupMethod.referenceBuilder()
@@ -145,8 +133,8 @@
.build());
}
builder.add(
- StartupMethod.referenceBuilder()
- .setMethodReference(MethodReferenceUtils.classConstructor(C.class))
+ StartupClass.referenceBuilder()
+ .setClassReference(Reference.classFromClass(C.class))
.build(),
StartupMethod.referenceBuilder()
.setMethodReference(Reference.methodFromMethod(C.class.getDeclaredMethod("c")))
diff --git a/src/test/java/com/android/tools/r8/startup/StartupSyntheticWithoutContextTest.java b/src/test/java/com/android/tools/r8/startup/StartupSyntheticWithoutContextTest.java
index e1e3149..3087653 100644
--- a/src/test/java/com/android/tools/r8/startup/StartupSyntheticWithoutContextTest.java
+++ b/src/test/java/com/android/tools/r8/startup/StartupSyntheticWithoutContextTest.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.experimental.startup.StartupClass;
import com.android.tools.r8.experimental.startup.StartupItem;
import com.android.tools.r8.experimental.startup.StartupMethod;
import com.android.tools.r8.graph.DexProgramClass;
@@ -30,7 +31,6 @@
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -93,46 +93,36 @@
return ImmutableList.of("A", "B", "C");
}
- private List<StartupMethod<ClassReference, MethodReference>> getExpectedStartupList()
+ private List<StartupItem<ClassReference, MethodReference, ?>> getExpectedStartupList()
throws NoSuchMethodException {
return ImmutableList.of(
- StartupMethod.referenceBuilder()
- .setMethodReference(MethodReferenceUtils.classConstructor(Main.class))
+ StartupClass.referenceBuilder()
+ .setClassReference(Reference.classFromClass(Main.class))
.build(),
StartupMethod.referenceBuilder()
.setMethodReference(MethodReferenceUtils.mainMethod(Main.class))
.build(),
- StartupMethod.referenceBuilder()
- .setMethodReference(MethodReferenceUtils.classConstructor(A.class))
+ StartupClass.referenceBuilder()
+ .setClassReference(Reference.classFromClass(A.class))
.build(),
StartupMethod.referenceBuilder()
.setMethodReference(Reference.methodFromMethod(A.class.getDeclaredMethod("a")))
.build(),
- StartupMethod.referenceBuilder()
- .setMethodReference(MethodReferenceUtils.classConstructor(B.class))
+ StartupClass.referenceBuilder()
+ .setClassReference(Reference.classFromClass(B.class))
.build(),
StartupMethod.referenceBuilder()
.setMethodReference(Reference.methodFromMethod(B.class.getDeclaredMethod("b")))
.build(),
- StartupMethod.referenceBuilder()
- .setMethodReference(MethodReferenceUtils.classConstructor(B.class))
- .setSynthetic()
- .build(),
- StartupMethod.referenceBuilder()
- .setMethodReference(MethodReferenceUtils.instanceConstructor(B.class))
- .setSynthetic()
- .build(),
- StartupMethod.referenceBuilder()
- .setMethodReference(
- Reference.method(
- Reference.classFromClass(B.class), "run", Collections.emptyList(), null))
+ StartupClass.referenceBuilder()
+ .setClassReference(Reference.classFromClass(B.class))
.setSynthetic()
.build(),
StartupMethod.referenceBuilder()
.setMethodReference(Reference.methodFromMethod(B.class.getDeclaredMethod("lambda$b$0")))
.build(),
- StartupMethod.referenceBuilder()
- .setMethodReference(MethodReferenceUtils.classConstructor(C.class))
+ StartupClass.referenceBuilder()
+ .setClassReference(Reference.classFromClass(C.class))
.build(),
StartupMethod.referenceBuilder()
.setMethodReference(Reference.methodFromMethod(C.class.getDeclaredMethod("c")))
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
index 74b5de5..e29b4e8 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
@@ -36,7 +36,8 @@
import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.retrace.Retracer;
-import com.android.tools.r8.retrace.internal.ProguardMappingSupplierImpl;
+import com.android.tools.r8.retrace.internal.MappingSupplierInternalImpl;
+import com.android.tools.r8.retrace.internal.RetracerImpl;
import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.BiMapContainer;
@@ -184,10 +185,9 @@
public Retracer getRetracer() {
if (lazyRetracer == null) {
lazyRetracer =
- Retracer.builder()
- .setMappingSupplier(new ProguardMappingSupplierImpl(mapping))
- .setDiagnosticsHandler(new TestDiagnosticMessagesImpl())
- .build();
+ RetracerImpl.createInternal(
+ MappingSupplierInternalImpl.createInternal(mapping),
+ new TestDiagnosticMessagesImpl());
}
return lazyRetracer;
}
@@ -548,11 +548,9 @@
}
public Retracer retrace() {
- return Retracer.builder()
- .setMappingSupplier(
- new ProguardMappingSupplierImpl(
- mapping == null ? ClassNameMapper.builder().build() : mapping))
- .setDiagnosticsHandler(new TestDiagnosticMessagesImpl())
- .build();
+ return RetracerImpl.createInternal(
+ MappingSupplierInternalImpl.createInternal(
+ mapping == null ? ClassNameMapper.builder().build() : mapping),
+ new TestDiagnosticMessagesImpl());
}
}
diff --git a/third_party/retrace/binary_compatibility.tar.gz.sha1 b/third_party/retrace/binary_compatibility.tar.gz.sha1
index da20d2e..750079a 100644
--- a/third_party/retrace/binary_compatibility.tar.gz.sha1
+++ b/third_party/retrace/binary_compatibility.tar.gz.sha1
@@ -1 +1 @@
-4408d86adfb21f32c2dcb93c2c8f2e392f0e0de0
\ No newline at end of file
+ed632240a9f5652ed988ccf84c5f592a487d24c3
\ No newline at end of file
diff --git a/tools/create_maven_release.py b/tools/create_maven_release.py
index de9f0ac..33e6f75 100755
--- a/tools/create_maven_release.py
+++ b/tools/create_maven_release.py
@@ -372,7 +372,7 @@
jdk.GetJavaExecutable(),
'-cp',
utils.R8_JAR,
- 'com.android.tools.r8.GenerateLintFiles',
+ 'com.android.tools.r8.ir.desugar.desugaredlibrary.lint.GenerateLintFiles',
configuration,
implementation,
lint_dir]
diff --git a/tools/r8_release.py b/tools/r8_release.py
index a25baf6..c00c15c 100755
--- a/tools/r8_release.py
+++ b/tools/r8_release.py
@@ -366,13 +366,18 @@
g4_open('src.jar')
g4_open('lib.jar')
g4_open('lib.jar.map')
+ g4_open('retrace_full.jar')
g4_open('retrace_lib.jar')
+ g4_open('retrace_lib.jar.map')
g4_open('desugar_jdk_libs_configuration.jar')
download_file(options.version, 'r8-full-exclude-deps.jar', 'full.jar')
+ download_file(options.version, 'r8-full-exclude-deps.jar', 'retrace_full.jar')
download_file(options.version, 'r8-src.jar', 'src.jar')
download_file(options.version, 'r8lib-exclude-deps.jar', 'lib.jar')
download_file(
options.version, 'r8lib-exclude-deps.jar.map', 'lib.jar.map')
+ download_file(
+ options.version, 'r8lib-exclude-deps.jar.map', 'retrace_lib.jar.map')
download_file(options.version, 'desugar_jdk_libs_configuration.jar',
'desugar_jdk_libs_configuration.jar')
download_file(options.version, 'r8retrace-exclude-deps.jar', 'retrace_lib.jar')
@@ -418,6 +423,67 @@
return release_google3
+def prepare_google3_retrace(args):
+ assert args.version
+ # Check if an existing client exists.
+ if not args.use_existing_work_branch:
+ check_no_google3_client(args, args.p4_client)
+
+ def release_google3_retrace(options):
+ print("Releasing Retrace for Google 3")
+ if options.dry_run:
+ return 'DryRun: omitting g3 release for %s' % options.version
+
+ google3_base = subprocess.check_output(
+ ['p4', 'g4d', '-f', args.p4_client]).decode('utf-8').rstrip()
+ third_party_r8 = os.path.join(google3_base, 'third_party', 'java', 'r8')
+ with utils.ChangedWorkingDirectory(third_party_r8):
+ # download files
+ g4_open('retrace_full.jar')
+ g4_open('retrace_lib.jar')
+ g4_open('retrace_lib.jar.map')
+ download_file(options.version, 'r8-full-exclude-deps.jar', 'retrace_full.jar')
+ download_file(options.version, 'r8retrace-exclude-deps.jar', 'retrace_lib.jar')
+ download_file(
+ options.version, 'r8lib-exclude-deps.jar.map', 'retrace_lib.jar.map')
+ g4_open('METADATA')
+ metadata_path = os.path.join(third_party_r8, 'METADATA')
+ match_count = 0
+ version_match_regexp = r'[1-9]\.[0-9]{1,2}\.[0-9]{1,3}-dev/r8retrace-exclude-deps.jar'
+ for line in open(metadata_path, 'r'):
+ result = re.search(version_match_regexp, line)
+ if result:
+ match_count = match_count + 1
+ if match_count != 1:
+ print(("Could not find the previous retrace release string to replace in " +
+ "METADATA. Expected to find is mentioned 1 times. Please update %s " +
+ "manually and run again with options --google3retrace " +
+ "--use-existing-work-branch.") % metadata_path)
+ sys.exit(1)
+ sed(version_match_regexp, options.version + "/r8retrace-exclude-deps.jar", metadata_path)
+ subprocess.check_output('chmod u+w *', shell=True)
+
+ with utils.ChangedWorkingDirectory(google3_base):
+ blaze_result = blaze_run('//third_party/java/r8:retrace -- --version')
+
+ print(blaze_result)
+ assert options.version in blaze_result
+
+ if not options.no_upload:
+ change_result = g4_change(options.version)
+ change_result += 'Run \'(g4d ' + args.p4_client \
+ + ' && tap_presubmit -p all --train -c ' \
+ + get_cl_id(change_result) + ')\' for running TAP global' \
+ + ' presubmit using the train.\n' \
+ + 'Run \'(g4d ' + args.p4_client \
+ + ' && tap_presubmit -p all --notrain --detach --email' \
+ + ' --skip_flaky_targets --skip_already_failing -c ' \
+ + get_cl_id(change_result) + ')\' for running an isolated' \
+ + ' TAP global presubmit.'
+ return change_result
+
+ return release_google3_retrace
+
def update_desugar_library_in_studio(args):
assert os.path.exists(args.studio), ("Could not find STUDIO path %s"
% args.studio)
@@ -843,6 +909,10 @@
default=False,
action='store_true',
help='Release for google 3')
+ result.add_argument('--google3retrace',
+ default=False,
+ action='store_true',
+ help='Release retrace for google 3')
result.add_argument('--p4-client',
default='update-r8',
metavar=('<client name>'),
@@ -906,6 +976,8 @@
if args.google3:
targets_to_run.append(prepare_google3(args))
+ if args.google3retrace:
+ targets_to_run.append(prepare_google3_retrace(args))
if args.studio and not args.update_desugar_library_in_studio:
targets_to_run.append(prepare_studio(args))
if args.aosp:
diff --git a/tools/startup/generate_startup_descriptors.py b/tools/startup/generate_startup_descriptors.py
index f1faee43..26846bc 100755
--- a/tools/startup/generate_startup_descriptors.py
+++ b/tools/startup/generate_startup_descriptors.py
@@ -15,10 +15,12 @@
generate_startup_profile(options)
if options.logcat:
write_tmp_logcat(logcat, iteration, options)
- current_startup_descriptors = get_r8_startup_descriptors_from_logcat(logcat)
+ current_startup_descriptors = get_r8_startup_descriptors_from_logcat(
+ logcat, options)
else:
write_tmp_profile(profile, iteration, options)
- write_tmp_profile_classes_and_methods(profile_classes_and_methods, iteration, options)
+ write_tmp_profile_classes_and_methods(
+ profile_classes_and_methods, iteration, options)
current_startup_descriptors = \
transform_classes_and_methods_to_r8_startup_descriptors(
profile_classes_and_methods, options)
@@ -52,7 +54,7 @@
# Clear logcat and start capturing logcat.
adb_utils.clear_logcat(options.device_id)
logcat_process = adb_utils.start_logcat(
- options.device_id, format='raw', filter='r8:I *:S')
+ options.device_id, format='tag', filter='r8:I ActivityTaskManager:I *:S')
else:
# Clear existing profile data.
adb_utils.clear_profile_data(options.app_id, options.device_id)
@@ -77,30 +79,96 @@
# Shutdown app.
adb_utils.stop_app(options.app_id, options.device_id)
- adb_utils.tear_down_after_interaction_with_device(
+ adb_utils.teardown_after_interaction_with_device(
tear_down_options, options.device_id)
return (logcat, profile, profile_classes_and_methods)
-def get_r8_startup_descriptors_from_logcat(logcat):
- startup_descriptors = []
+def get_r8_startup_descriptors_from_logcat(logcat, options):
+ post_startup = False
+ startup_descriptors = {}
for line in logcat:
- if line == '--------- beginning of main':
+ line_elements = parse_logcat_line(line)
+ if line_elements is None:
continue
- if line == '--------- beginning of system':
- continue
- if not line.startswith('L') or not line.endswith(';'):
- print('Unrecognized line in logcat: %s' % line)
- continue
- startup_descriptors.append(line)
+ (priority, tag, message) = line_elements
+ if tag == 'ActivityTaskManager':
+ if message.startswith('START') \
+ or message.startswith('Activity pause timeout for') \
+ or message.startswith('Activity top resumed state loss timeout for') \
+ or message.startswith('Force removing')
+ or message.startswith(
+ 'Launch timeout has expired, giving up wake lock!'):
+ continue
+ elif message.startswith('Displayed %s/' % options.app_id):
+ print('Entering post startup: %s' % message)
+ post_startup = True
+ continue
+ elif tag == 'r8':
+ if is_startup_descriptor(message):
+ startup_descriptors[message] = {
+ 'conditional_startup': False,
+ 'post_startup': post_startup
+ }
+ continue
+ # Reaching here means we didn't expect this line.
+ report_unrecognized_logcat_line(line)
return startup_descriptors
+def is_startup_descriptor(string):
+ # The descriptor should start with the holder (possibly prefixed with 'S').
+ if not any(string.startswith('%sL' % flags) for flags in ['', 'S']):
+ return False
+ # The descriptor should end with ';', a primitive type, or void.
+ if not string.endswith(';') \
+ and not any(string.endswith(c) for c in get_primitive_descriptors()) \
+ and not string.endswith('V'):
+ return False
+ return True
+
+def get_primitive_descriptors():
+ return ['Z', 'B', 'S', 'C', 'I', 'F', 'J', 'D']
+
+def parse_logcat_line(line):
+ if line == '--------- beginning of kernel':
+ return None
+ if line == '--------- beginning of main':
+ return None
+ if line == '--------- beginning of system':
+ return None
+
+ priority = None
+ tag = None
+
+ try:
+ priority_end = line.index('/')
+ priority = line[0:priority_end]
+ line = line[priority_end + 1:]
+ except ValueError:
+ return report_unrecognized_logcat_line(line)
+
+ try:
+ tag_end = line.index(':')
+ tag = line[0:tag_end].strip()
+ line = line[tag_end + 1 :]
+ except ValueError:
+ return report_unrecognized_logcat_line(line)
+
+ message = line.strip()
+ return (priority, tag, message)
+
+def report_unrecognized_logcat_line(line):
+ print('Unrecognized line in logcat: %s' % line)
+
def transform_classes_and_methods_to_r8_startup_descriptors(
classes_and_methods, options):
startup_descriptors = []
for class_or_method in classes_and_methods:
descriptor = class_or_method.get('descriptor')
flags = class_or_method.get('flags')
+ if flags.get('conditional_startup') \
+ and not options.include_conditional_startup:
+ continue
if flags.get('post_startup') \
and not flags.get('startup') \
and not options.include_post_startup:
@@ -110,7 +178,22 @@
def add_r8_startup_descriptors(startup_descriptors, startup_descriptors_to_add):
previous_number_of_startup_descriptors = len(startup_descriptors)
- startup_descriptors.update(startup_descriptors_to_add)
+ if previous_number_of_startup_descriptors == 0:
+ for startup_descriptor, flags in startup_descriptors_to_add.items():
+ startup_descriptors[startup_descriptor] = flags.copy()
+ else:
+ for startup_descriptor, flags in startup_descriptors_to_add.items():
+ if startup_descriptor in startup_descriptors:
+ # Merge flags.
+ existing_flags = startup_descriptors[startup_descriptor]
+ assert not flags['conditional_startup']
+ if flags['post_startup']:
+ existing_flags['post_startup'] = True
+ else:
+ # Add new startup descriptor.
+ new_flags = flags.copy()
+ new_flags['conditional_startup'] = True
+ startup_descriptors[startup_descriptor] = new_flags
new_number_of_startup_descriptors = len(startup_descriptors)
return new_number_of_startup_descriptors \
- previous_number_of_startup_descriptors
@@ -159,8 +242,20 @@
item_to_string)
def write_tmp_startup_descriptors(startup_descriptors, iteration, options):
+ lines = [
+ startup_descriptor_to_string(startup_descriptor, flags)
+ for startup_descriptor, flags in startup_descriptors.items()]
write_tmp_textual_artifact(
- startup_descriptors, iteration, options, 'startup-descriptors.txt')
+ lines, iteration, options, 'startup-descriptors.txt')
+
+def startup_descriptor_to_string(startup_descriptor, flags):
+ result = ''
+ if flags['conditional_startup']:
+ pass # result += 'C'
+ if flags['post_startup']:
+ pass # result += 'P'
+ result += startup_descriptor
+ return result
def parse_options(argv):
result = argparse.ArgumentParser(
@@ -177,6 +272,11 @@
result.add_argument('--logcat',
action='store_true',
default=False)
+ result.add_argument('--include-conditional-startup',
+ help='Include conditional startup classes and methods in '
+ 'the R8 startup descriptors',
+ action='store_true',
+ default=False)
result.add_argument('--include-post-startup',
help='Include post startup classes and methods in the R8 '
'startup descriptors',
@@ -224,7 +324,7 @@
if options.apk:
adb_utils.uninstall(options.app_id, options.device_id)
adb_utils.install(options.apk, options.device_id)
- startup_descriptors = set()
+ startup_descriptors = {}
if options.until_stable:
iteration = 0
stable_iterations = 0
@@ -242,12 +342,12 @@
extend_startup_descriptors(startup_descriptors, iteration, options)
if options.out is not None:
with open(options.out, 'w') as f:
- for startup_descriptor in startup_descriptors:
- f.write(startup_descriptor)
+ for startup_descriptor, flags in startup_descriptors.items():
+ f.write(startup_descriptor_to_string(startup_descriptor, flags))
f.write('\n')
else:
- for startup_descriptor in startup_descriptors:
- print(startup_descriptor)
+ for startup_descriptor, flags in startup_descriptors.items():
+ print(startup_descriptor_to_string(startup_descriptor, flags))
if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))
diff --git a/tools/test.py b/tools/test.py
index 570da44..27c8b6a 100755
--- a/tools/test.py
+++ b/tools/test.py
@@ -359,9 +359,9 @@
gradle_args.append('-Ptesting-state-name=' + options.testing_state_name)
# Build an R8 with dependencies for bootstrapping tests before adding test sources.
- gradle_args.append('r8WithDeps')
- gradle_args.append('r8WithDeps11')
gradle_args.append('r8WithRelocatedDeps')
+ gradle_args.append('r8WithRelocatedDeps17')
+ # TODO(b/227160052): Remove 11 once the bootstrap test runs on 17.
gradle_args.append('r8WithRelocatedDeps11')
# Add Gradle tasks