Merge commit 'cfdab6475040d4d12daf817581fe6819fd94cbf0' into dev-release
diff --git a/infra/config/global/generated/cr-buildbucket.cfg b/infra/config/global/generated/cr-buildbucket.cfg
index ff1df31..aa285e0 100644
--- a/infra/config/global/generated/cr-buildbucket.cfg
+++ b/infra/config/global/generated/cr-buildbucket.cfg
@@ -1699,6 +1699,35 @@
}
}
builders {
+ name: "smali"
+ swarming_host: "chrome-swarming.appspot.com"
+ swarming_tags: "vpython:native-python-wrapper"
+ dimensions: "cores:8"
+ dimensions: "cpu:x86-64"
+ dimensions: "os:Ubuntu-20.04"
+ dimensions: "pool:luci.r8.ci"
+ exe {
+ cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
+ cipd_version: "refs/heads/master"
+ cmd: "luciexe"
+ }
+ properties:
+ '{'
+ ' "builder_group": "internal.client.smali",'
+ ' "recipe": "rex",'
+ ' "test_wrapper": "tools/archive_smali.py"'
+ '}'
+ priority: 26
+ execution_timeout_secs: 43200
+ expiration_secs: 126000
+ build_numbers: YES
+ service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
+ experiments {
+ key: "luci.recipes.use_python3"
+ value: 100
+ }
+ }
+ builders {
name: "windows"
swarming_host: "chrome-swarming.appspot.com"
swarming_tags: "vpython:native-python-wrapper"
diff --git a/infra/config/global/generated/luci-milo.cfg b/infra/config/global/generated/luci-milo.cfg
index b1481fe..4073c3d 100644
--- a/infra/config/global/generated/luci-milo.cfg
+++ b/infra/config/global/generated/luci-milo.cfg
@@ -126,6 +126,11 @@
short_name: "kotlin_old"
}
builders {
+ name: "buildbucket/luci.r8.ci/smali"
+ category: "R8"
+ short_name: "smali"
+ }
+ builders {
name: "buildbucket/luci.r8.ci/lib_desugar-archive-jdk11"
category: "library_desugar"
short_name: "jdk11"
diff --git a/infra/config/global/generated/luci-notify.cfg b/infra/config/global/generated/luci-notify.cfg
index fa8d5b0..5417911 100644
--- a/infra/config/global/generated/luci-notify.cfg
+++ b/infra/config/global/generated/luci-notify.cfg
@@ -540,6 +540,18 @@
}
builders {
bucket: "ci"
+ name: "smali"
+ repository: "https://r8.googlesource.com/r8"
+ }
+}
+notifiers {
+ notifications {
+ on_failure: true
+ on_new_failure: true
+ notify_blamelist {}
+ }
+ builders {
+ bucket: "ci"
name: "windows"
repository: "https://r8.googlesource.com/r8"
}
diff --git a/infra/config/global/generated/luci-scheduler.cfg b/infra/config/global/generated/luci-scheduler.cfg
index dbd8f00..982d085 100644
--- a/infra/config/global/generated/luci-scheduler.cfg
+++ b/infra/config/global/generated/luci-scheduler.cfg
@@ -688,6 +688,21 @@
}
}
job {
+ id: "smali"
+ realm: "ci"
+ acl_sets: "ci"
+ triggering_policy {
+ kind: GREEDY_BATCHING
+ max_concurrent_invocations: 1
+ max_batch_size: 1
+ }
+ buildbucket {
+ server: "cr-buildbucket.appspot.com"
+ bucket: "ci"
+ builder: "smali"
+ }
+}
+job {
id: "windows"
realm: "ci"
acl_sets: "ci"
@@ -805,6 +820,7 @@
triggers: "linux-kotlin_old"
triggers: "linux-none"
triggers: "linux-run-on-app-dump"
+ triggers: "smali"
triggers: "windows"
gitiles {
repo: "https://r8.googlesource.com/r8"
diff --git a/infra/config/global/generated/project.cfg b/infra/config/global/generated/project.cfg
index 11b6c85..cfee79c 100644
--- a/infra/config/global/generated/project.cfg
+++ b/infra/config/global/generated/project.cfg
@@ -7,7 +7,7 @@
name: "r8"
access: "group:all"
lucicfg {
- version: "1.38.2"
+ version: "1.39.4"
package_dir: ".."
config_dir: "generated"
entry_point: "main.star"
diff --git a/infra/config/global/main.star b/infra/config/global/main.star
index 32abb36..1e374ed 100755
--- a/infra/config/global/main.star
+++ b/infra/config/global/main.star
@@ -169,7 +169,7 @@
"--archive_failures"
]
-def get_dimensions(windows=False, internal=False, normal=False):
+def get_dimensions(windows=False, internal=False, normal=False, smali=False):
dimensions = {
"cores" : "2" if internal else "8",
"cpu" : "x86-64",
@@ -410,6 +410,22 @@
}
)
+r8_builder(
+ "smali",
+ dimensions = get_dimensions(smali=True),
+ triggering_policy = scheduler.policy(
+ kind = scheduler.GREEDY_BATCHING_KIND,
+ max_concurrent_invocations = 1,
+ max_batch_size = 1,
+ ),
+ properties = {
+ "test_wrapper" : "tools/archive_smali.py",
+ "builder_group" : "internal.client.smali"
+ },
+ execution_timeout = time.hour * 12,
+ expiration_timeout = time.hour * 35,
+)
+
order_of_categories = [
"archive",
"R8",
diff --git a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
index 285ef1b..cc62da7 100644
--- a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
+++ b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
@@ -22,6 +22,7 @@
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.InternalOptions.DesugarState;
import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.MapConsumerUtils;
import com.android.tools.r8.utils.ProgramConsumerUtils;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.ThreadUtils;
@@ -281,28 +282,25 @@
private int minApiLevel = 0;
private int threadCount = ThreadUtils.NOT_SPECIFIED;
protected DesugarState desugarState = DesugarState.ON;
- private List<StringResource> desugaredLibrarySpecificationResources = new ArrayList<>();
+ private final List<StringResource> desugaredLibrarySpecificationResources = new ArrayList<>();
private boolean includeClassesChecksum = false;
private boolean optimizeMultidexForLinearAlloc = false;
private BiPredicate<String, Long> dexClassChecksumFilter = (name, checksum) -> true;
- private List<AssertionsConfiguration> assertionsConfiguration = new ArrayList<>();
- private List<Consumer<Inspector>> outputInspections = new ArrayList<>();
+ private final List<AssertionsConfiguration> assertionsConfiguration = new ArrayList<>();
+ private final List<Consumer<Inspector>> outputInspections = new ArrayList<>();
protected StringConsumer proguardMapConsumer = null;
+ protected PartitionMapConsumer partitionMapConsumer = null;
private DumpInputFlags dumpInputFlags = DumpInputFlags.getDefault();
private MapIdProvider mapIdProvider = null;
private SourceFileProvider sourceFileProvider = null;
private boolean isAndroidPlatformBuild = false;
- private List<ArtProfileForRewriting> artProfilesForRewriting = new ArrayList<>();
- private List<StartupProfileProvider> startupProfileProviders = new ArrayList<>();
+ private final List<ArtProfileForRewriting> artProfilesForRewriting = new ArrayList<>();
+ private final List<StartupProfileProvider> startupProfileProviders = new ArrayList<>();
private ClassConflictResolver classConflictResolver = null;
private CancelCompilationChecker cancelCompilationChecker = null;
abstract CompilationMode defaultCompilationMode();
- Builder() {
- mode = defaultCompilationMode();
- }
-
Builder(DiagnosticsHandler diagnosticsHandler) {
super(diagnosticsHandler);
mode = defaultCompilationMode();
@@ -395,6 +393,33 @@
}
/**
+ * Set an output destination to which partition-map content should be written.
+ *
+ * <p>This is a short-hand for setting a {@link PartitionMapConsumer} using {@link
+ * #setPartitionMapConsumer}. Note that any subsequent call to this method or {@link
+ * #setPartitionMapConsumer} will override the previous setting.
+ *
+ * @param partitionMapOutput File-system path to write output at.
+ */
+ B setPartitionMapOutputPath(Path partitionMapOutput) {
+ assert partitionMapOutput != null;
+ return setPartitionMapConsumer(MapConsumerUtils.createZipConsumer(partitionMapOutput));
+ }
+
+ /**
+ * Set a consumer for receiving the partition map content.
+ *
+ * <p>Note that any subsequent call to this method or {@link #setPartitionMapOutputPath} will
+ * override the previous setting.
+ *
+ * @param partitionMapConsumer Consumer to receive the content once produced.
+ */
+ B setPartitionMapConsumer(PartitionMapConsumer partitionMapConsumer) {
+ this.partitionMapConsumer = partitionMapConsumer;
+ return self();
+ }
+
+ /**
* Get the main dex list consumer that will receive the final complete main dex list.
*/
public StringConsumer getMainDexListConsumer() {
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index 2af2e26..2e50083 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -302,9 +302,7 @@
timing.begin("Generate main-dex list");
appView.dexItemFactory().clearTypeElementsCache();
MainDexInfo mainDexInfo =
- new GenerateMainDexList(options)
- .traceMainDex(
- executor, appView.appInfo().app(), appView.appInfo().getMainDexInfo());
+ new GenerateMainDexList(options).traceMainDexForD8(appView, executor);
appView.setAppInfo(appView.appInfo().rebuildWithMainDexInfo(mainDexInfo));
timing.end();
}
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index 137cb8b..a97ec22 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -665,13 +665,10 @@
internal.setSyntheticInfoConsumer(syntheticInfoConsumer);
internal.desugarGraphConsumer = desugarGraphConsumer;
internal.mainDexKeepRules = mainDexKeepRules;
- internal.proguardMapConsumer =
+ internal.mapConsumer =
proguardMapConsumer == null
? null
- : ProguardMapStringConsumer.builder()
- .setStringConsumer(proguardMapConsumer)
- .setDiagnosticsHandler(getReporter())
- .build();
+ : ProguardMapStringConsumer.builder().setStringConsumer(proguardMapConsumer).build();
internal.lineNumberOptimization =
!internal.debug && proguardMapConsumer != null
? LineNumberOptimization.ON
diff --git a/src/main/java/com/android/tools/r8/GenerateMainDexList.java b/src/main/java/com/android/tools/r8/GenerateMainDexList.java
index 1e0f9c9..182d156 100644
--- a/src/main/java/com/android/tools/r8/GenerateMainDexList.java
+++ b/src/main/java/com/android/tools/r8/GenerateMainDexList.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.StringConsumer.ForwardingConsumer;
import com.android.tools.r8.dex.ApplicationReader;
import com.android.tools.r8.experimental.graphinfo.GraphConsumer;
+import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppServices;
import com.android.tools.r8.graph.AppView;
@@ -47,7 +48,7 @@
throws IOException {
try {
DexApplication application = new ApplicationReader(app, options, timing).read(executor);
- traceMainDex(executor, application, MainDexInfo.none())
+ traceMainDexForGenerateMainDexList(executor, application)
.forEach(type -> consumer.accept(type.toBinaryName() + ".class", options.reporter));
consumer.finished(options.reporter);
} catch (ExecutionException e) {
@@ -55,11 +56,22 @@
}
}
- public MainDexInfo traceMainDex(
- ExecutorService executor, DexApplication application, MainDexInfo existingMainDexInfo)
+ public MainDexInfo traceMainDexForD8(AppView<AppInfo> appView, ExecutorService executor)
throws ExecutionException {
- AppView<? extends AppInfoWithClassHierarchy> appView =
- AppView.createForR8(application.toDirect(), existingMainDexInfo);
+ return traceMainDex(
+ AppView.createForSimulatingR8InD8(
+ appView.app().toDirect(), appView.appInfo().getMainDexInfo()),
+ executor);
+ }
+
+ public MainDexInfo traceMainDexForGenerateMainDexList(
+ ExecutorService executor, DexApplication application) throws ExecutionException {
+ return traceMainDex(AppView.createForR8(application.toDirect()), executor);
+ }
+
+ private MainDexInfo traceMainDex(
+ AppView<? extends AppInfoWithClassHierarchy> appView, ExecutorService executor)
+ throws ExecutionException {
appView.setAppServices(AppServices.builder(appView).build());
MainDexListBuilder.checkForAssumedLibraryTypes(appView.appInfo());
diff --git a/src/main/java/com/android/tools/r8/MapConsumerToPartitionMapConsumer.java b/src/main/java/com/android/tools/r8/MapConsumerToPartitionMapConsumer.java
new file mode 100644
index 0000000..fcd3113
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/MapConsumerToPartitionMapConsumer.java
@@ -0,0 +1,49 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8;
+
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.naming.MapConsumer;
+import com.android.tools.r8.naming.ProguardMapMarkerInfo;
+import com.android.tools.r8.retrace.ProguardMapPartitioner;
+import com.android.tools.r8.retrace.internal.ProguardMapProducerInternal;
+import java.io.IOException;
+
+public class MapConsumerToPartitionMapConsumer implements MapConsumer {
+
+ protected final PartitionMapConsumer partitionMapConsumer;
+
+ protected MapConsumerToPartitionMapConsumer(PartitionMapConsumer partitionMapConsumer) {
+ assert partitionMapConsumer != null;
+ this.partitionMapConsumer = partitionMapConsumer;
+ }
+
+ @Override
+ public void accept(
+ DiagnosticsHandler diagnosticsHandler,
+ ProguardMapMarkerInfo makerInfo,
+ ClassNameMapper classNameMapper) {
+ try {
+ classNameMapper.setPreamble(makerInfo.toPreamble());
+ partitionMapConsumer.acceptMappingPartitionMetadata(
+ ProguardMapPartitioner.builder(diagnosticsHandler)
+ .setProguardMapProducer(new ProguardMapProducerInternal(classNameMapper))
+ .setPartitionConsumer(partitionMapConsumer::acceptMappingPartition)
+ // Modifying these do not actually do anything currently since there is no parsing.
+ .setAllowEmptyMappedRanges(false)
+ .setAllowExperimentalMapping(false)
+ .build()
+ .run());
+ } catch (IOException exception) {
+ throw new Unreachable("IOExceptions should only occur when parsing");
+ }
+ }
+
+ @Override
+ public void finished(DiagnosticsHandler handler) {
+ partitionMapConsumer.finished(handler);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/MappingSupplierInternal.java b/src/main/java/com/android/tools/r8/MappingSupplierInternal.java
new file mode 100644
index 0000000..116a964
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/MappingSupplierInternal.java
@@ -0,0 +1,15 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8;
+
+import com.android.tools.r8.naming.mappinginformation.MapVersionMappingInformation;
+import java.util.Set;
+
+public interface MappingSupplierInternal {
+
+ void verifyMappingFileHash(DiagnosticsHandler diagnosticsHandler);
+
+ Set<MapVersionMappingInformation> getMapVersions(DiagnosticsHandler diagnosticsHandler);
+}
diff --git a/src/main/java/com/android/tools/r8/PartitionMapConsumer.java b/src/main/java/com/android/tools/r8/PartitionMapConsumer.java
new file mode 100644
index 0000000..ea232d4
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/PartitionMapConsumer.java
@@ -0,0 +1,16 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8;
+
+import com.android.tools.r8.retrace.MappingPartition;
+import com.android.tools.r8.retrace.MappingPartitionMetadata;
+
+@Keep
+public interface PartitionMapConsumer extends Finishable {
+
+ void acceptMappingPartition(MappingPartition mappingPartition);
+
+ void acceptMappingPartitionMetadata(MappingPartitionMetadata mappingPartitionMetadata);
+}
diff --git a/src/main/java/com/android/tools/r8/ProguardMapConsumer.java b/src/main/java/com/android/tools/r8/ProguardMapConsumer.java
deleted file mode 100644
index f00b428..0000000
--- a/src/main/java/com/android/tools/r8/ProguardMapConsumer.java
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8;
-
-import com.android.tools.r8.naming.ClassNameMapper;
-import com.android.tools.r8.naming.ProguardMapMarkerInfo;
-
-public abstract class ProguardMapConsumer implements Finishable {
-
- public abstract void accept(ProguardMapMarkerInfo makerInfo, ClassNameMapper classNameMapper);
-}
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index edec018..39e7996 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -4,6 +4,7 @@
package com.android.tools.r8;
import static com.android.tools.r8.utils.InternalOptions.DETERMINISTIC_DEBUGGING;
+import static com.android.tools.r8.utils.MapConsumerUtils.wrapExistingMapConsumerIfNotNull;
import com.android.tools.r8.ProgramResource.Kind;
import com.android.tools.r8.dex.Marker.Tool;
@@ -18,6 +19,7 @@
import com.android.tools.r8.keepanno.asm.KeepEdgeReader;
import com.android.tools.r8.keepanno.ast.KeepEdge;
import com.android.tools.r8.keepanno.keeprules.KeepRuleExtractor;
+import com.android.tools.r8.naming.MapConsumer;
import com.android.tools.r8.naming.ProguardMapStringConsumer;
import com.android.tools.r8.naming.SourceFileRewriter;
import com.android.tools.r8.origin.Origin;
@@ -299,6 +301,34 @@
}
/**
+ * Set an output destination to which r8-map content should be written.
+ *
+ * <p>This is a short-hand for setting a {@link MapConsumerToPartitionMapConsumer} using {@link
+ * #setPartitionMapConsumer}. Note that any subsequent call to this method or {@link
+ * #setPartitionMapConsumer} will override the previous setting.
+ *
+ * @param partitionMapOutput File-system path to write output at.
+ */
+ @Override
+ public Builder setPartitionMapOutputPath(Path partitionMapOutput) {
+ assert partitionMapOutput != null;
+ return super.setPartitionMapOutputPath(partitionMapOutput);
+ }
+
+ /**
+ * Set a consumer for receiving the r8-map content.
+ *
+ * <p>Note that any subsequent call to this method or {@link #setPartitionMapOutputPath} will
+ * override the previous setting.
+ *
+ * @param partitionMapConsumer Consumer to receive the content once produced.
+ */
+ @Override
+ public Builder setPartitionMapConsumer(PartitionMapConsumer partitionMapConsumer) {
+ return super.setPartitionMapConsumer(partitionMapConsumer);
+ }
+
+ /**
* Set a consumer for receiving the keep rules to use when compiling the desugared library for
* the program being compiled in this compilation.
*
@@ -632,6 +662,7 @@
forceProguardCompatibility,
includeDataResources,
proguardMapConsumer,
+ partitionMapConsumer,
proguardUsageConsumer,
proguardSeedsConsumer,
proguardConfigurationConsumer,
@@ -832,6 +863,7 @@
private final boolean forceProguardCompatibility;
private final Optional<Boolean> includeDataResources;
private final StringConsumer proguardMapConsumer;
+ private final PartitionMapConsumer partitionMapConsumer;
private final StringConsumer proguardUsageConsumer;
private final StringConsumer proguardSeedsConsumer;
private final StringConsumer proguardConfigurationConsumer;
@@ -912,6 +944,7 @@
boolean forceProguardCompatibility,
Optional<Boolean> includeDataResources,
StringConsumer proguardMapConsumer,
+ PartitionMapConsumer partitionMapConsumer,
StringConsumer proguardUsageConsumer,
StringConsumer proguardSeedsConsumer,
StringConsumer proguardConfigurationConsumer,
@@ -969,6 +1002,7 @@
this.forceProguardCompatibility = forceProguardCompatibility;
this.includeDataResources = includeDataResources;
this.proguardMapConsumer = proguardMapConsumer;
+ this.partitionMapConsumer = partitionMapConsumer;
this.proguardUsageConsumer = proguardUsageConsumer;
this.proguardSeedsConsumer = proguardSeedsConsumer;
this.proguardConfigurationConsumer = proguardConfigurationConsumer;
@@ -992,6 +1026,7 @@
forceProguardCompatibility = false;
includeDataResources = null;
proguardMapConsumer = null;
+ partitionMapConsumer = null;
proguardUsageConsumer = null;
proguardSeedsConsumer = null;
proguardConfigurationConsumer = null;
@@ -1070,13 +1105,15 @@
proguardMapConsumer,
proguardConfiguration.isPrintMapping(),
proguardConfiguration.getPrintMappingFile());
- internal.proguardMapConsumer =
- stringConsumer == null
- ? null
- : ProguardMapStringConsumer.builder()
- .setStringConsumer(stringConsumer)
- .setDiagnosticsHandler(getReporter())
- .build();
+ MapConsumer mapConsumer =
+ wrapExistingMapConsumerIfNotNull(
+ internal.mapConsumer, partitionMapConsumer, MapConsumerToPartitionMapConsumer::new);
+ internal.mapConsumer =
+ wrapExistingMapConsumerIfNotNull(
+ mapConsumer,
+ stringConsumer,
+ nonNullStringConsumer ->
+ ProguardMapStringConsumer.builder().setStringConsumer(stringConsumer).build());
// Amend the usage information consumer with options from the proguard configuration.
internal.usageInformationConsumer =
diff --git a/src/main/java/com/android/tools/r8/bisect/Bisect.java b/src/main/java/com/android/tools/r8/bisect/Bisect.java
index caa1c27..c4c8170 100644
--- a/src/main/java/com/android/tools/r8/bisect/Bisect.java
+++ b/src/main/java/com/android/tools/r8/bisect/Bisect.java
@@ -5,7 +5,6 @@
import com.android.tools.r8.OutputMode;
import com.android.tools.r8.ProgramConsumer;
-import com.android.tools.r8.ProguardMapConsumer;
import com.android.tools.r8.bisect.BisectOptions.Result;
import com.android.tools.r8.dex.ApplicationReader;
import com.android.tools.r8.dex.ApplicationWriter;
@@ -14,6 +13,7 @@
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.naming.MapConsumer;
import com.android.tools.r8.synthesis.SyntheticItems.GlobalSyntheticsStrategy;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.AndroidAppConsumers;
@@ -184,7 +184,7 @@
InternalOptions options = app.options;
// Save the original consumers, so they can be unwrapped after write.
ProgramConsumer programConsumer = options.programConsumer;
- ProguardMapConsumer proguardMapConsumer = options.proguardMapConsumer;
+ MapConsumer mapConsumer = options.mapConsumer;
AndroidAppConsumers compatSink = new AndroidAppConsumers(options);
ApplicationWriter writer =
ApplicationWriter.create(
@@ -196,7 +196,7 @@
compatSink.build().writeToDirectory(output, OutputMode.DexIndexed);
// Restore original consumers.
options.programConsumer = programConsumer;
- options.proguardMapConsumer = proguardMapConsumer;
+ options.mapConsumer = mapConsumer;
}
public static DexProgramClass run(BisectOptions options) throws Exception {
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfLogicalBinop.java b/src/main/java/com/android/tools/r8/cf/code/CfLogicalBinop.java
index 32afbc5..a173410 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfLogicalBinop.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfLogicalBinop.java
@@ -106,6 +106,10 @@
}
public int getAsmOpcode() {
+ return getAsmOpcode(opcode, type);
+ }
+
+ public static int getAsmOpcode(Opcode opcode, NumericType type) {
switch (opcode) {
case Shl:
return type.isWide() ? Opcodes.LSHL : Opcodes.ISHL;
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
index a664d9d..37fad8c 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -289,7 +289,7 @@
}
private boolean willComputeProguardMap() {
- return options.proguardMapConsumer != null;
+ return options.mapConsumer != null;
}
/** Writer that never needs the input app to deal with mapping info for kotlin. */
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index 7a7b8d6..aff2b31 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -228,6 +228,24 @@
defaultTypeRewriter(appInfo));
}
+ public static AppView<AppInfoWithClassHierarchy> createForSimulatingR8InD8(
+ DirectMappedDexApplication application, MainDexInfo mainDexInfo) {
+ ClassToFeatureSplitMap classToFeatureSplitMap =
+ ClassToFeatureSplitMap.createInitialClassToFeatureSplitMap(application.options);
+ AppInfoWithClassHierarchy appInfo =
+ AppInfoWithClassHierarchy.createInitialAppInfoWithClassHierarchy(
+ application,
+ classToFeatureSplitMap,
+ mainDexInfo,
+ GlobalSyntheticsStrategy.forSingleOutputMode());
+ return new AppView<>(
+ appInfo,
+ ArtProfileCollection.empty(),
+ StartupProfile.empty(),
+ WholeProgramOptimizations.ON,
+ defaultTypeRewriter(appInfo));
+ }
+
public static <T extends AppInfo> AppView<T> createForD8(
T appInfo, TypeRewriter mapper, Timing timing) {
return new AppView<>(
diff --git a/src/main/java/com/android/tools/r8/graph/DexCode.java b/src/main/java/com/android/tools/r8/graph/DexCode.java
index 486aaa7..9532f1e 100644
--- a/src/main/java/com/android/tools/r8/graph/DexCode.java
+++ b/src/main/java/com/android/tools/r8/graph/DexCode.java
@@ -312,6 +312,17 @@
return new EventBasedDebugInfo(eventBasedInfo.startLine, newParameters, eventBasedInfo.events);
}
+ public DexDebugInfo debugInfoWithExtraParameters(DexItemFactory factory, int extraParameters) {
+ EventBasedDebugInfo eventBasedInfo = DexDebugInfo.convertToEventBased(this, factory);
+ if (eventBasedInfo == null) {
+ return eventBasedInfo;
+ }
+ DexString[] parameters = eventBasedInfo.parameters;
+ DexString[] newParameters = new DexString[parameters.length + extraParameters];
+ System.arraycopy(parameters, 0, newParameters, 0, parameters.length);
+ return new EventBasedDebugInfo(eventBasedInfo.startLine, newParameters, eventBasedInfo.events);
+ }
+
@Override
public Code getCodeAsInlining(
DexMethod caller, DexMethod callee, DexItemFactory factory, boolean isCalleeD8R8Synthesized) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index f9a5279..1c502d5 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -1011,6 +1011,20 @@
}
}
+ public static void setDebugInfoWithExtraParameters(
+ Code code, int arity, int extraParameters, AppView<?> appView) {
+ if (code.isDexCode()) {
+ DexCode dexCode = code.asDexCode();
+ DexDebugInfo newDebugInfo =
+ dexCode.debugInfoWithExtraParameters(appView.dexItemFactory(), extraParameters);
+ assert (newDebugInfo == null) || (arity == newDebugInfo.getParameterCount());
+ dexCode.setDebugInfo(newDebugInfo);
+ } else {
+ assert code.isCfCode();
+ // We don't have anything to do for Cf.
+ }
+ }
+
private DexCode toDexCodeThatLogsError(DexItemFactory itemFactory) {
checkIfObsolete();
Signature signature = MethodSignature.fromDexMethod(getReference());
diff --git a/src/main/java/com/android/tools/r8/graph/PrunedItems.java b/src/main/java/com/android/tools/r8/graph/PrunedItems.java
index f49fcbd..200b351 100644
--- a/src/main/java/com/android/tools/r8/graph/PrunedItems.java
+++ b/src/main/java/com/android/tools/r8/graph/PrunedItems.java
@@ -32,6 +32,10 @@
this.removedMethods = removedMethods;
}
+ public static Builder concurrentBuilder() {
+ return new ConcurrentBuilder();
+ }
+
public static Builder builder() {
return new Builder();
}
@@ -111,15 +115,22 @@
private DexApplication prunedApp;
- private final Set<DexReference> additionalPinnedItems = Sets.newIdentityHashSet();
- private final Set<DexType> noLongerSyntheticItems = Sets.newIdentityHashSet();
- private Set<DexType> removedClasses = Sets.newIdentityHashSet();
- private final Set<DexField> removedFields = Sets.newIdentityHashSet();
- private Set<DexMethod> removedMethods = Sets.newIdentityHashSet();
+ private final Set<DexReference> additionalPinnedItems;
+ private final Set<DexType> noLongerSyntheticItems;
+ private Set<DexType> removedClasses;
+ private final Set<DexField> removedFields;
+ private Set<DexMethod> removedMethods;
- Builder() {}
+ Builder() {
+ additionalPinnedItems = newEmptySet();
+ noLongerSyntheticItems = newEmptySet();
+ removedClasses = newEmptySet();
+ removedFields = newEmptySet();
+ removedMethods = newEmptySet();
+ }
Builder(PrunedItems prunedItems) {
+ this();
additionalPinnedItems.addAll(prunedItems.getAdditionalPinnedItems());
noLongerSyntheticItems.addAll(prunedItems.getNoLongerSyntheticItems());
prunedApp = prunedItems.getPrunedApp();
@@ -128,6 +139,10 @@
removedMethods.addAll(prunedItems.getRemovedMethods());
}
+ <T> Set<T> newEmptySet() {
+ return Sets.newIdentityHashSet();
+ }
+
public Builder setPrunedApp(DexApplication prunedApp) {
this.prunedApp = prunedApp;
return this;
@@ -196,4 +211,22 @@
removedMethods);
}
}
+
+ public static class ConcurrentBuilder extends Builder {
+
+ @Override
+ <T> Set<T> newEmptySet() {
+ return Sets.newConcurrentHashSet();
+ }
+
+ @Override
+ public Builder setRemovedClasses(Set<DexType> removedClasses) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Builder setRemovedMethods(Set<DexMethod> removedMethods) {
+ throw new UnsupportedOperationException();
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/graph/fixup/ConcurrentMethodFixup.java b/src/main/java/com/android/tools/r8/graph/fixup/ConcurrentMethodFixup.java
new file mode 100644
index 0000000..8a4c7bd
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/fixup/ConcurrentMethodFixup.java
@@ -0,0 +1,219 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.graph.fixup;
+
+import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.ClasspathOrLibraryClass;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexMethodSignature;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ImmediateProgramSubtypingInfo;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.optimize.argumentpropagation.utils.ProgramClassesBidirectedGraph;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.shaking.KeepMethodInfo;
+import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.Timing;
+import com.android.tools.r8.utils.collections.DexMethodSignatureSet;
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+import com.google.common.collect.Sets;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+
+public class ConcurrentMethodFixup {
+
+ private final AppView<AppInfoWithLiveness> appView;
+ private final Map<ClasspathOrLibraryClass, DexMethodSignatureSet> nonProgramVirtualMethods =
+ new ConcurrentHashMap<>();
+ private final ProgramClassFixer programClassFixer;
+
+ public ConcurrentMethodFixup(
+ AppView<AppInfoWithLiveness> appView, ProgramClassFixer programClassFixer) {
+ this.appView = appView;
+ this.programClassFixer = programClassFixer;
+ }
+
+ public void fixupClassesConcurrentlyByConnectedProgramComponents(
+ Timing timing, ExecutorService executorService) throws ExecutionException {
+ timing.begin("Concurrent method fixup");
+ timing.begin("Compute strongly connected components");
+ ImmediateProgramSubtypingInfo immediateSubtypingInfo =
+ ImmediateProgramSubtypingInfo.create(appView);
+ List<Set<DexProgramClass>> connectedComponents =
+ new ProgramClassesBidirectedGraph(appView, immediateSubtypingInfo)
+ .computeStronglyConnectedComponents();
+ timing.end();
+
+ timing.begin("Process strongly connected components");
+ ThreadUtils.processItems(
+ connectedComponents, this::processConnectedProgramComponents, executorService);
+ timing.end();
+ timing.end();
+ }
+
+ public interface ProgramClassFixer {
+ // When a class is fixed-up, it is guaranteed that its supertype and interfaces were processed
+ // before. In addition, all interfaces are processed before any class is processed.
+ void fixupProgramClass(DexProgramClass clazz, MethodNamingUtility namingUtility);
+
+ // Answers true if the method should be reserved as itself.
+ boolean shouldReserveAsIfPinned(ProgramMethod method);
+ }
+
+ private void processConnectedProgramComponents(Set<DexProgramClass> classes) {
+ List<DexProgramClass> sorted = new ArrayList<>(classes);
+ sorted.sort(Comparator.comparing(DexClass::getType));
+ BiMap<DexMethodSignature, DexMethodSignature> componentSignatures = HashBiMap.create();
+
+ // 1) Reserve all library overrides and pinned virtual methods.
+ reserveComponentPinnedAndInterfaceMethodSignatures(sorted, componentSignatures);
+
+ // 2) Map all interfaces top-down updating the componentSignatures.
+ Set<DexProgramClass> processedInterfaces = Sets.newIdentityHashSet();
+ for (DexProgramClass clazz : sorted) {
+ if (clazz.isInterface()) {
+ processInterface(clazz, processedInterfaces, componentSignatures);
+ }
+ }
+
+ // 3) Map all classes top-down propagating the inherited signatures.
+ // The componentSignatures are already fully computed and should not be updated anymore.
+ // TODO(b/279707790): Consider changing the processing to have a different componentSignatures
+ // per subtree.
+ Set<DexProgramClass> processedClasses = Sets.newIdentityHashSet();
+ for (DexProgramClass clazz : sorted) {
+ if (!clazz.isInterface()) {
+ processClass(clazz, processedClasses, componentSignatures);
+ }
+ }
+ }
+
+ private void processClass(
+ DexProgramClass clazz,
+ Set<DexProgramClass> processedClasses,
+ BiMap<DexMethodSignature, DexMethodSignature> componentSignatures) {
+ assert !clazz.isInterface();
+ if (!processedClasses.add(clazz)) {
+ return;
+ }
+ // We need to process first the super-type for the top-down propagation of inherited signatures.
+ DexProgramClass superClass = asProgramClassOrNull(appView.definitionFor(clazz.superType));
+ if (superClass != null) {
+ processClass(superClass, processedClasses, componentSignatures);
+ }
+ MethodNamingUtility utility = createMethodNamingUtility(componentSignatures, clazz);
+ programClassFixer.fixupProgramClass(clazz, utility);
+ }
+
+ private void processInterface(
+ DexProgramClass clazz,
+ Set<DexProgramClass> processedInterfaces,
+ BiMap<DexMethodSignature, DexMethodSignature> componentSignatures) {
+ assert clazz.isInterface();
+ if (!processedInterfaces.add(clazz)) {
+ return;
+ }
+ // We need to process first all super-interfaces to avoid generating collisions by renaming
+ // private or static methods into inherited virtual method signatures.
+ for (DexType superInterface : clazz.getInterfaces()) {
+ DexProgramClass superInterfaceClass =
+ asProgramClassOrNull(appView.definitionFor(superInterface));
+ if (superInterfaceClass != null) {
+ processInterface(superInterfaceClass, processedInterfaces, componentSignatures);
+ }
+ }
+ MethodNamingUtility utility = createMethodNamingUtility(componentSignatures, clazz);
+ programClassFixer.fixupProgramClass(clazz, utility);
+ }
+
+ private boolean shouldReserveAsPinned(ProgramMethod method) {
+ KeepMethodInfo keepInfo = appView.getKeepInfo(method);
+ return !keepInfo.isOptimizationAllowed(appView.options())
+ || !keepInfo.isShrinkingAllowed(appView.options())
+ || programClassFixer.shouldReserveAsIfPinned(method);
+ }
+
+ private MethodNamingUtility createMethodNamingUtility(
+ BiMap<DexMethodSignature, DexMethodSignature> inheritedSignatures, DexProgramClass clazz) {
+ BiMap<DexMethod, DexMethod> localSignatures = HashBiMap.create();
+ clazz.forEachProgramInstanceInitializer(
+ method -> {
+ if (shouldReserveAsPinned(method)) {
+ localSignatures.put(method.getReference(), method.getReference());
+ }
+ });
+ return new MethodNamingUtility(appView.dexItemFactory(), inheritedSignatures, localSignatures);
+ }
+
+ private void reserveComponentPinnedAndInterfaceMethodSignatures(
+ List<DexProgramClass> stronglyConnectedProgramClasses,
+ BiMap<DexMethodSignature, DexMethodSignature> componentSignatures) {
+ Set<ClasspathOrLibraryClass> seenNonProgramClasses = Sets.newIdentityHashSet();
+ for (DexProgramClass clazz : stronglyConnectedProgramClasses) {
+ // If a private or static method is pinned, we need to reserve the mapping to avoid creating
+ // a collision with a changed virtual method.
+ clazz.forEachProgramMethodMatching(
+ m -> !m.isInstanceInitializer(),
+ method -> {
+ if (shouldReserveAsPinned(method)) {
+ componentSignatures.put(method.getMethodSignature(), method.getMethodSignature());
+ }
+ });
+ clazz.forEachImmediateSupertype(
+ supertype -> {
+ DexClass superclass = appView.definitionFor(supertype);
+ if (superclass != null
+ && !superclass.isProgramClass()
+ && seenNonProgramClasses.add(superclass.asClasspathOrLibraryClass())) {
+ for (DexMethodSignature vMethod :
+ getOrComputeNonProgramVirtualMethods(superclass.asClasspathOrLibraryClass())) {
+ componentSignatures.put(vMethod, vMethod);
+ }
+ }
+ });
+ }
+ }
+
+ private DexMethodSignatureSet getOrComputeNonProgramVirtualMethods(
+ ClasspathOrLibraryClass clazz) {
+ DexMethodSignatureSet libraryMethodsOnClass = nonProgramVirtualMethods.get(clazz);
+ if (libraryMethodsOnClass != null) {
+ return libraryMethodsOnClass;
+ }
+ return computeNonProgramVirtualMethods(clazz);
+ }
+
+ private DexMethodSignatureSet computeNonProgramVirtualMethods(
+ ClasspathOrLibraryClass classpathOrLibraryClass) {
+ DexClass clazz = classpathOrLibraryClass.asDexClass();
+ DexMethodSignatureSet libraryMethodsOnClass = DexMethodSignatureSet.create();
+ clazz.forEachImmediateSupertype(
+ supertype -> {
+ DexClass superclass = appView.definitionFor(supertype);
+ if (superclass != null) {
+ assert !superclass.isProgramClass();
+ libraryMethodsOnClass.addAll(
+ getOrComputeNonProgramVirtualMethods(superclass.asClasspathOrLibraryClass()));
+ }
+ });
+ clazz.forEachClassMethodMatching(
+ DexEncodedMethod::belongsToVirtualPool,
+ method -> libraryMethodsOnClass.add(method.getMethodSignature()));
+ nonProgramVirtualMethods.put(classpathOrLibraryClass, libraryMethodsOnClass);
+ return libraryMethodsOnClass;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/fixup/MethodNamingUtility.java b/src/main/java/com/android/tools/r8/graph/fixup/MethodNamingUtility.java
new file mode 100644
index 0000000..977e01c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/fixup/MethodNamingUtility.java
@@ -0,0 +1,119 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.graph.fixup;
+
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexMethodSignature;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexType;
+import com.google.common.collect.BiMap;
+import java.util.function.BiConsumer;
+
+public class MethodNamingUtility {
+
+ private final DexItemFactory factory;
+ private final BiMap<DexMethodSignature, DexMethodSignature> inheritedSignatures;
+ private final BiMap<DexMethod, DexMethod> localSignatures;
+
+ public MethodNamingUtility(
+ DexItemFactory factory,
+ BiMap<DexMethodSignature, DexMethodSignature> inheritedSignatures,
+ BiMap<DexMethod, DexMethod> localSignatures) {
+ this.factory = factory;
+ this.inheritedSignatures = inheritedSignatures;
+ this.localSignatures = localSignatures;
+ }
+
+ public DexMethod nextUniqueMethod(
+ DexEncodedMethod method, DexProto newProto, DexType initExtraType) {
+ DexMethod reference = method.getReference();
+
+ if (method.isClassInitializer()) {
+ assert reference.getProto() == newProto;
+ return reference;
+ }
+
+ if (method.isInstanceInitializer()) {
+ assert initExtraType != null;
+ return nextUniqueInitializer(reference, newProto, initExtraType);
+ }
+
+ if (method.isNonPrivateVirtualMethod()) {
+ return nextUniqueVirtualMethod(reference, newProto);
+ }
+
+ return nextUniquePrivateOrStaticMethod(reference, newProto);
+ }
+
+ private DexMethod nextUniqueInitializer(
+ DexMethod reference, DexProto newProto, DexType initExtraType) {
+ assert !inheritedSignatures.containsKey(reference.getSignature());
+
+ // 1) We check if the reference has already been reserved (pinning).
+ DexMethod remapped = localSignatures.get(reference);
+ if (remapped != null) {
+ assert remapped.getProto() == newProto;
+ return remapped.withHolder(reference.getHolderType(), factory);
+ }
+
+ // 2) We check for collision with already mapped methods.
+ DexMethod newMethod = reference.withProto(newProto, factory);
+ if (localSignatures.containsValue(newMethod)) {
+ // This collides with something that has been renamed into this.
+ newMethod =
+ factory.createInstanceInitializerWithFreshProto(
+ newMethod, initExtraType, tryMethod -> !localSignatures.containsValue(tryMethod));
+ }
+
+ // 3) Finally register the new method and return it.
+ assert !localSignatures.containsValue(newMethod);
+ localSignatures.put(reference, newMethod);
+ return newMethod;
+ }
+
+ private DexMethod nextUniquePrivateOrStaticMethod(DexMethod reference, DexProto newProto) {
+ return nextUniqueMethod(reference, newProto, localSignatures::put);
+ }
+
+ private DexMethod nextUniqueVirtualMethod(DexMethod reference, DexProto newProto) {
+ return nextUniqueMethod(
+ reference,
+ newProto,
+ (from, to) -> inheritedSignatures.put(from.getSignature(), to.getSignature()));
+ }
+
+ private boolean anyCollision(DexMethod method) {
+ return localSignatures.containsValue(method)
+ || inheritedSignatures.containsValue(method.getSignature());
+ }
+
+ private DexMethod nextUniqueMethod(
+ DexMethod reference, DexProto newProto, BiConsumer<DexMethod, DexMethod> registration) {
+ // 1) We check if the reference has already been reserved (pinning or override).
+ DexMethodSignature remappedSignature = inheritedSignatures.get(reference.getSignature());
+ if (remappedSignature != null) {
+ assert remappedSignature.getProto() == newProto;
+ return remappedSignature.withHolder(reference.getHolderType(), factory);
+ }
+
+ // 2) We check for collision with already mapped methods.
+ DexMethod newMethod = reference.withProto(newProto, factory);
+ if (anyCollision(newMethod)) {
+ newMethod =
+ factory.createFreshMethodNameWithoutHolder(
+ newMethod.getName().toString(),
+ newMethod.getProto(),
+ newMethod.getHolderType(),
+ tryMethod -> !anyCollision(tryMethod));
+ }
+
+ // 3) Finally register the new method and return it.
+ assert !anyCollision(newMethod);
+ registration.accept(reference, newMethod);
+ return newMethod;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/TreeFixerBase.java b/src/main/java/com/android/tools/r8/graph/fixup/TreeFixerBase.java
similarity index 93%
rename from src/main/java/com/android/tools/r8/graph/TreeFixerBase.java
rename to src/main/java/com/android/tools/r8/graph/fixup/TreeFixerBase.java
index f3cc3e9..bf5e3de 100644
--- a/src/main/java/com/android/tools/r8/graph/TreeFixerBase.java
+++ b/src/main/java/com/android/tools/r8/graph/fixup/TreeFixerBase.java
@@ -2,8 +2,26 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.graph;
+package com.android.tools.r8.graph.fixup;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexMethodSignature;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexProto;
+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.EnclosingMethodAttribute;
+import com.android.tools.r8.graph.InnerClassAttribute;
+import com.android.tools.r8.graph.NestHostClassAttribute;
+import com.android.tools.r8.graph.NestMemberClassAttribute;
+import com.android.tools.r8.graph.PermittedSubclassAttribute;
+import com.android.tools.r8.graph.RecordComponentInfo;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.ConsumerUtils;
import com.android.tools.r8.utils.DescriptorUtils;
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
index a12baa2..482b43f 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
@@ -18,7 +18,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.graph.EnclosingMethodAttribute;
-import com.android.tools.r8.graph.TreeFixerBase;
+import com.android.tools.r8.graph.fixup.TreeFixerBase;
import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
import com.android.tools.r8.ir.conversion.ExtraUnusedNullParameter;
import com.android.tools.r8.profile.rewriting.ProfileCollectionAdditions;
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/SyntheticInitializerConverter.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/code/SyntheticInitializerConverter.java
index 8685b4a..269bf93 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/SyntheticInitializerConverter.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/code/SyntheticInitializerConverter.java
@@ -96,6 +96,9 @@
}
private AppView<AppInfo> createAppViewForConversion() {
+ assert appView.enableWholeProgramOptimizations();
+ assert appView.hasClassHierarchy();
+
// At this point the code rewritings described by repackaging and synthetic finalization have
// not been applied to the code objects. These code rewritings will be applied in the
// application writer. We therefore simulate that we are in D8, to allow building IR for each of
@@ -106,7 +109,7 @@
appView.dexItemFactory().clearTypeElementsCache();
AppView<AppInfo> appViewForConversion =
- AppView.createForD8(
+ AppView.createForSimulatingD8InR8(
AppInfo.createInitialAppInfo(
appView.appInfo().app(), GlobalSyntheticsStrategy.forNonSynthesizing()));
appViewForConversion.setGraphLens(appView.graphLens());
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java
index 2efe2c3..ca151c5 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java
@@ -345,7 +345,7 @@
Instruction materializingInstruction = objects.get(i).buildIR(appView, code);
instructionIterator.add(materializingInstruction);
instructionIterator.add(
- new ArrayPut(
+ ArrayPut.create(
MemberType.OBJECT,
newObjectsValue,
indexValue,
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/objectstate/EnumValuesObjectState.java b/src/main/java/com/android/tools/r8/ir/analysis/value/objectstate/EnumValuesObjectState.java
index 722dccc..1daf879 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/objectstate/EnumValuesObjectState.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/objectstate/EnumValuesObjectState.java
@@ -91,6 +91,12 @@
@Override
public ObjectState rewrittenWithLens(
AppView<AppInfoWithLiveness> appView, GraphLens lens, GraphLens codeLens) {
+ if (objectClassForOrdinal.enumHasBeenUnboxed(appView)) {
+ // It does not make sense to keep outdated data on unboxed enums.
+ // We have the exact content of the array but this is not modeled at this point so we simply
+ // return a KnownLengthArrayState.
+ return appView.abstractValueFactory().createKnownLengthArrayState(state.length);
+ }
ObjectState[] newState = new ObjectState[state.length];
for (int i = 0; i < state.length; i++) {
newState[i] = state[i].rewrittenWithLens(appView, lens, codeLens);
@@ -147,6 +153,8 @@
@Override
public abstract boolean equals(Object obj);
+
+ public abstract boolean enumHasBeenUnboxed(AppView<?> appView);
}
static class UniformObjectClassForOrdinal extends ObjectClassForOrdinal {
@@ -183,6 +191,11 @@
UniformObjectClassForOrdinal other = (UniformObjectClassForOrdinal) obj;
return type == other.type;
}
+
+ @Override
+ public boolean enumHasBeenUnboxed(AppView<?> appView) {
+ return appView.unboxedEnums().isUnboxedEnum(type);
+ }
}
static class VariableObjectClassForOrdinal extends ObjectClassForOrdinal {
@@ -227,5 +240,11 @@
VariableObjectClassForOrdinal other = (VariableObjectClassForOrdinal) obj;
return Arrays.equals(types, other.types);
}
+
+ @Override
+ public boolean enumHasBeenUnboxed(AppView<?> appView) {
+ assert types.length > 0;
+ return appView.unboxedEnums().isUnboxedEnum(types[0]);
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Add.java b/src/main/java/com/android/tools/r8/ir/code/Add.java
index dc8cb05..98e4cf2 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Add.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Add.java
@@ -19,7 +19,17 @@
public class Add extends ArithmeticBinop {
- public Add(NumericType type, Value dest, Value left, Value right) {
+ public static Add create(NumericType type, Value dest, Value left, Value right) {
+ Add add = createNonNormalized(type, dest, left, right);
+ add.normalizeArgumentsForCommutativeBinop();
+ return add;
+ }
+
+ public static Add createNonNormalized(NumericType type, Value dest, Value left, Value right) {
+ return new Add(type, dest, left, right);
+ }
+
+ private Add(NumericType type, Value dest, Value left, Value right) {
super(type, dest, left, right);
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/And.java b/src/main/java/com/android/tools/r8/ir/code/And.java
index f7aac6c..d7cd98c 100644
--- a/src/main/java/com/android/tools/r8/ir/code/And.java
+++ b/src/main/java/com/android/tools/r8/ir/code/And.java
@@ -16,7 +16,17 @@
public class And extends LogicalBinop {
- public And(NumericType type, Value dest, Value left, Value right) {
+ public static And create(NumericType type, Value dest, Value left, Value right) {
+ And and = new And(type, dest, left, right);
+ and.normalizeArgumentsForCommutativeBinop();
+ return and;
+ }
+
+ public static And createNonNormalized(NumericType type, Value dest, Value left, Value right) {
+ return new And(type, dest, left, right);
+ }
+
+ private And(NumericType type, Value dest, Value left, Value right) {
super(type, dest, left, right);
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArithmeticBinop.java b/src/main/java/com/android/tools/r8/ir/code/ArithmeticBinop.java
index f2aa7cf..92f7380 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ArithmeticBinop.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ArithmeticBinop.java
@@ -17,7 +17,7 @@
public abstract class ArithmeticBinop extends Binop {
- public ArithmeticBinop(NumericType type, Value dest, Value left, Value right) {
+ ArithmeticBinop(NumericType type, Value dest, Value left, Value right) {
super(type, dest, left, right);
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java b/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java
index 2bfd3c3..ab357ed 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java
@@ -31,6 +31,7 @@
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.ir.regalloc.RegisterAllocator;
+import com.android.tools.r8.lightir.LirBuilder;
import java.util.Arrays;
import java.util.Set;
@@ -286,4 +287,14 @@
int index = index().getConstInstruction().asConstNumber().getIntValue();
return newArraySize <= 0 || index < 0 || newArraySize <= index;
}
+
+ @Override
+ public void buildLir(LirBuilder<Value, ?> builder) {
+ if (getMemberType().isObject()) {
+ DexType destType = dest().getType().asReferenceType().toDexType(builder.factory());
+ builder.addArrayGetObject(destType, array(), index());
+ } else {
+ builder.addArrayGetPrimitive(getMemberType(), array(), index());
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java b/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java
index 5f07431..eff1adc 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java
@@ -27,6 +27,7 @@
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.ir.regalloc.RegisterAllocator;
+import com.android.tools.r8.lightir.LirBuilder;
import java.util.Arrays;
public class ArrayPut extends ArrayAccess {
@@ -36,14 +37,29 @@
private MemberType type;
- public ArrayPut(MemberType type, Value array, Value index, Value value) {
+ public static ArrayPut create(MemberType type, Value array, Value index, Value value) {
+ ArrayPut put = new ArrayPut(type, array, index, value);
+ assert put.verify();
+ return put;
+ }
+
+ public static ArrayPut createWithoutVerification(
+ MemberType type, Value array, Value index, Value value) {
+ return new ArrayPut(type, array, index, value);
+ }
+
+ private ArrayPut(MemberType type, Value array, Value index, Value value) {
super(null, Arrays.asList(array, index, value));
- assert type != null;
- assert array.verifyCompatible(ValueType.OBJECT);
- assert index.verifyCompatible(ValueType.INT);
this.type = type;
}
+ private boolean verify() {
+ assert type != null;
+ assert array().verifyCompatible(ValueType.OBJECT);
+ assert index().verifyCompatible(ValueType.INT);
+ return true;
+ }
+
@Override
public int opcode() {
return Opcodes.ARRAY_PUT;
@@ -251,6 +267,11 @@
@Override
public ArrayAccess withMemberType(MemberType newMemberType) {
- return new ArrayPut(newMemberType, array(), index(), value());
+ return ArrayPut.create(newMemberType, array(), index(), value());
+ }
+
+ @Override
+ public void buildLir(LirBuilder<Value, ?> builder) {
+ builder.addArrayPut(getMemberType(), array(), index(), value());
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Binop.java b/src/main/java/com/android/tools/r8/ir/code/Binop.java
index 613ff5b..1629cda 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Binop.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Binop.java
@@ -20,15 +20,19 @@
protected final NumericType type;
- public Binop(NumericType type, Value dest, Value left, Value right) {
+ Binop(NumericType type, Value dest, Value left, Value right) {
super(dest);
this.type = type;
- if (isCommutative() && (!right.isConstNumber() && left.isConstNumber())) {
- addInValue(right);
- addInValue(left);
- } else {
- addInValue(left);
- addInValue(right);
+ addInValue(left);
+ addInValue(right);
+ }
+
+ public void normalizeArgumentsForCommutativeBinop() {
+ assert isCommutative();
+ if (isCommutative() && !rightValue().isConstNumber() && leftValue().isConstNumber()) {
+ Value tmp = inValues.get(0);
+ inValues.set(0, inValues.get(1));
+ inValues.set(1, tmp);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/CheckCast.java b/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
index d9a1063..0bb5989 100644
--- a/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
+++ b/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
@@ -27,6 +27,7 @@
import com.android.tools.r8.ir.conversion.DexBuilder;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
+import com.android.tools.r8.lightir.LirBuilder;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.InternalOptions;
@@ -285,6 +286,11 @@
registry.registerCheckCast(type, ignoreCompatRules);
}
+ @Override
+ public void buildLir(LirBuilder<Value, ?> builder) {
+ builder.addCheckCast(type, object());
+ }
+
public static class Builder extends BuilderBase<Builder, CheckCast> {
protected DexType castType;
diff --git a/src/main/java/com/android/tools/r8/ir/code/LogicalBinop.java b/src/main/java/com/android/tools/r8/ir/code/LogicalBinop.java
index c9a1b03..5457a36 100644
--- a/src/main/java/com/android/tools/r8/ir/code/LogicalBinop.java
+++ b/src/main/java/com/android/tools/r8/ir/code/LogicalBinop.java
@@ -12,11 +12,12 @@
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.lightir.LirBuilder;
import java.util.function.Function;
public abstract class LogicalBinop extends Binop {
- public LogicalBinop(NumericType type, Value dest, Value left, Value right) {
+ LogicalBinop(NumericType type, Value dest, Value left, Value right) {
super(type, dest, left, right);
}
@@ -143,4 +144,9 @@
public void buildCf(CfBuilder builder) {
builder.add(new CfLogicalBinop(getCfOpcode(), type), this);
}
+
+ @Override
+ public void buildLir(LirBuilder<Value, ?> builder) {
+ builder.addLogicalBinop(getCfOpcode(), type, leftValue(), rightValue());
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Mul.java b/src/main/java/com/android/tools/r8/ir/code/Mul.java
index ccddf6a..41e4b35 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Mul.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Mul.java
@@ -19,7 +19,17 @@
public class Mul extends ArithmeticBinop {
- public Mul(NumericType type, Value dest, Value left, Value right) {
+ public static Mul create(NumericType type, Value dest, Value left, Value right) {
+ Mul mul = new Mul(type, dest, left, right);
+ mul.normalizeArgumentsForCommutativeBinop();
+ return mul;
+ }
+
+ public static Mul createNonNormalized(NumericType type, Value dest, Value left, Value right) {
+ return new Mul(type, dest, left, right);
+ }
+
+ private Mul(NumericType type, Value dest, Value left, Value right) {
super(type, dest, left, right);
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Or.java b/src/main/java/com/android/tools/r8/ir/code/Or.java
index 3fd1a77..f0ce553 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Or.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Or.java
@@ -15,7 +15,17 @@
public class Or extends LogicalBinop {
- public Or(NumericType type, Value dest, Value left, Value right) {
+ public static Or create(NumericType type, Value dest, Value left, Value right) {
+ Or or = new Or(type, dest, left, right);
+ or.normalizeArgumentsForCommutativeBinop();
+ return or;
+ }
+
+ public static Or createNonNormalized(NumericType type, Value dest, Value left, Value right) {
+ return new Or(type, dest, left, right);
+ }
+
+ private Or(NumericType type, Value dest, Value left, Value right) {
super(type, dest, left, right);
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Xor.java b/src/main/java/com/android/tools/r8/ir/code/Xor.java
index 657374a..db92998 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Xor.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Xor.java
@@ -15,7 +15,17 @@
public class Xor extends LogicalBinop {
- public Xor(NumericType type, Value dest, Value left, Value right) {
+ public static Xor create(NumericType type, Value dest, Value left, Value right) {
+ Xor xor = new Xor(type, dest, left, right);
+ xor.normalizeArgumentsForCommutativeBinop();
+ return xor;
+ }
+
+ public static Xor createNonNormalized(NumericType type, Value dest, Value left, Value right) {
+ return new Xor(type, dest, left, right);
+ }
+
+ private Xor(NumericType type, Value dest, Value left, Value right) {
super(type, dest, left, right);
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
index 3dfe55f..81f5c14 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
@@ -321,7 +321,7 @@
// Replace Not with Xor.
it.replaceCurrentInstruction(
- new Xor(current.asNot().type, current.outValue(), inValue, constValue));
+ Xor.create(current.asNot().type, current.outValue(), inValue, constValue));
}
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
index 779beba..cf7a34f 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -1086,7 +1086,7 @@
Value in1 = readNumericRegister(left, type);
Value in2 = readNumericRegister(right, type);
Value out = writeNumericRegister(dest, type, ThrowingInfo.NO_THROW);
- Add instruction = new Add(type, out, in1, in2);
+ Add instruction = Add.create(type, out, in1, in2);
assert !instruction.instructionTypeCanThrow();
addInstruction(instruction);
}
@@ -1096,7 +1096,7 @@
Value in1 = readNumericRegister(value, type);
Value in2 = readIntLiteral(constant);
Value out = writeNumericRegister(dest, type, ThrowingInfo.NO_THROW);
- Add instruction = new Add(type, out, in1, in2);
+ Add instruction = Add.create(type, out, in1, in2);
assert !instruction.instructionTypeCanThrow();
addInstruction(instruction);
}
@@ -1106,7 +1106,7 @@
Value in1 = readNumericRegister(left, type);
Value in2 = readNumericRegister(right, type);
Value out = writeNumericRegister(dest, type, ThrowingInfo.NO_THROW);
- And instruction = new And(type, out, in1, in2);
+ And instruction = And.create(type, out, in1, in2);
assert !instruction.instructionTypeCanThrow();
addInstruction(instruction);
}
@@ -1116,7 +1116,7 @@
Value in1 = readNumericRegister(value, type);
Value in2 = readIntLiteral(constant);
Value out = writeNumericRegister(dest, type, ThrowingInfo.NO_THROW);
- And instruction = new And(type, out, in1, in2);
+ And instruction = And.create(type, out, in1, in2);
assert !instruction.instructionTypeCanThrow();
addInstruction(instruction);
}
@@ -1158,7 +1158,7 @@
Value inValue = readRegister(value, ValueTypeConstraint.fromMemberType(type));
Value inArray = readRegister(array, ValueTypeConstraint.OBJECT);
Value inIndex = readRegister(index, ValueTypeConstraint.INT);
- ArrayPut instruction = new ArrayPut(type, inArray, inIndex, inValue);
+ ArrayPut instruction = ArrayPut.create(type, inArray, inIndex, inValue);
if (!type.isPrecise()) {
addImpreciseInstruction(instruction);
}
@@ -1323,7 +1323,7 @@
Value in1 = readNumericRegister(left, type);
Value in2 = readNumericRegister(right, type);
Value out = writeNumericRegister(dest, type, ThrowingInfo.NO_THROW);
- Mul instruction = new Mul(type, out, in1, in2);
+ Mul instruction = Mul.create(type, out, in1, in2);
assert !instruction.instructionTypeCanThrow();
addInstruction(instruction);
}
@@ -1333,7 +1333,7 @@
Value in1 = readNumericRegister(value, type);
Value in2 = readIntLiteral(constant);
Value out = writeNumericRegister(dest, type, ThrowingInfo.NO_THROW);
- Mul instruction = new Mul(type, out, in1, in2);
+ Mul instruction = Mul.create(type, out, in1, in2);
assert !instruction.instructionTypeCanThrow();
addInstruction(instruction);
}
@@ -1774,7 +1774,7 @@
instruction = new Not(type, out, in);
} else {
Value minusOne = readLiteral(ValueTypeConstraint.fromNumericType(type), -1);
- instruction = new Xor(type, out, in, minusOne);
+ instruction = Xor.create(type, out, in, minusOne);
}
assert !instruction.instructionTypeCanThrow();
addInstruction(instruction);
@@ -1991,7 +1991,7 @@
Value in1 = readNumericRegister(left, type);
Value in2 = readNumericRegister(right, type);
Value out = writeNumericRegister(dest, type, ThrowingInfo.NO_THROW);
- Or instruction = new Or(type, out, in1, in2);
+ Or instruction = Or.create(type, out, in1, in2);
assert !instruction.instructionTypeCanThrow();
addInstruction(instruction);
}
@@ -2001,7 +2001,7 @@
Value in1 = readNumericRegister(value, type);
Value in2 = readIntLiteral(constant);
Value out = writeNumericRegister(dest, type, ThrowingInfo.NO_THROW);
- Or instruction = new Or(type, out, in1, in2);
+ Or instruction = Or.create(type, out, in1, in2);
assert !instruction.instructionTypeCanThrow();
addInstruction(instruction);
}
@@ -2077,7 +2077,7 @@
&& in2.getConstInstruction().asConstNumber().isIntegerNegativeOne(type)) {
instruction = new Not(type, out, in1);
} else {
- instruction = new Xor(type, out, in1, in2);
+ instruction = Xor.create(type, out, in1, in2);
}
assert !instruction.instructionTypeCanThrow();
addInstruction(instruction);
@@ -2093,7 +2093,7 @@
} else {
Value in2 = readIntLiteral(constant);
Value out = writeNumericRegister(dest, type, ThrowingInfo.NO_THROW);
- instruction = new Xor(type, out, in1, in2);
+ instruction = Xor.create(type, out, in1, in2);
}
assert !instruction.instructionTypeCanThrow();
addInstruction(instruction);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index fc8d1f2..a5417c6 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -1041,6 +1041,7 @@
OptimizationFeedback feedback,
BytecodeMetadataProvider bytecodeMetadataProvider,
Timing timing) {
+ IRCode oldCode = code;
if (options.testing.roundtripThroughLir) {
code = roundtripThroughLir(code, feedback, bytecodeMetadataProvider, timing);
}
@@ -1075,6 +1076,9 @@
LirCode<EV> lirCode =
IR2LirConverter.translate(code, strategy.getEncodingStrategy(), appView.dexItemFactory());
timing.end();
+ // Check that printing does not fail.
+ String lirString = lirCode.toString();
+ assert !lirString.isEmpty();
timing.begin("LIR->IR (" + name + ")");
IRCode irCode =
Lir2IRConverter.translate(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
index 3e410e8..01a25e7 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
@@ -359,8 +359,8 @@
}
@Override
- public void acceptCompanionClassClinit(ProgramMethod method) {
- methodProcessor.scheduleDesugaredMethodForProcessing(method);
+ public void acceptCompanionClassClinit(ProgramMethod method, ProgramMethod companionMethod) {
+ methodProcessor.scheduleDesugaredMethodForProcessing(companionMethod);
}
@Override
@@ -496,7 +496,7 @@
}
@Override
- public void acceptCompanionClassClinit(ProgramMethod method) {
+ public void acceptCompanionClassClinit(ProgramMethod method, ProgramMethod companionMethod) {
// Intentionally empty. The method will be hit by tracing if required.
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/AbstractGenerateFiles.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/AbstractGenerateFiles.java
index 58f8ac7..dc4e201 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/AbstractGenerateFiles.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/AbstractGenerateFiles.java
@@ -33,7 +33,11 @@
private final DexItemFactory factory = new DexItemFactory();
private final Reporter reporter = new Reporter();
- final InternalOptions options = new InternalOptions(factory, reporter);
+ final InternalOptions options =
+ new InternalOptions(factory, reporter)
+ .getArtProfileOptions()
+ .setAllowReadingEmptyArtProfileProvidersMultipleTimesForTesting(true)
+ .getOptions();
final MachineDesugaredLibrarySpecification desugaredLibrarySpecification;
final Path desugaredLibrarySpecificationPath;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java
index 6eb7bca..21771de 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java
@@ -565,7 +565,9 @@
appView.dexItemFactory().createProto(appView.dexItemFactory().voidType),
appView,
methodBuilder -> createCompanionClassInitializer(iface, methodBuilder),
- eventConsumer::acceptCompanionClassClinit);
+ companionMethod ->
+ eventConsumer.acceptCompanionClassClinit(
+ iface.getProgramClassInitializer(), companionMethod));
}
private DexEncodedField ensureStaticClinitFieldToTriggerInterfaceInitialization(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodDesugaringBaseEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodDesugaringBaseEventConsumer.java
index 8b02f6c..76f0d7c 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodDesugaringBaseEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodDesugaringBaseEventConsumer.java
@@ -7,7 +7,7 @@
public interface InterfaceMethodDesugaringBaseEventConsumer {
- void acceptCompanionClassClinit(ProgramMethod method);
+ void acceptCompanionClassClinit(ProgramMethod method, ProgramMethod companionMethod);
void acceptDefaultAsCompanionMethod(ProgramMethod method, ProgramMethod companionMethod);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodDesugaringEventConsumer.java
index cea8eab..587dafa 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodDesugaringEventConsumer.java
@@ -24,7 +24,7 @@
private EmptyInterfaceMethodDesugaringEventConsumer() {}
@Override
- public void acceptCompanionClassClinit(ProgramMethod method) {
+ public void acceptCompanionClassClinit(ProgramMethod method, ProgramMethod companionMethod) {
// Intentionally empty.
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordFieldValuesRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordFieldValuesRewriter.java
index 0c1c56f..1653ae2 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordFieldValuesRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordFieldValuesRewriter.java
@@ -112,7 +112,7 @@
intConstantI.setPosition(constantPosition);
iterator.add(intConstantI);
ArrayPut arrayPut =
- new ArrayPut(
+ ArrayPut.create(
MemberType.OBJECT,
newArrayEmpty.outValue(),
intConstantI.outValue(),
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 9f985e9..692eeee 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
@@ -3567,8 +3567,8 @@
phiBlock.getInstructions().add(insertIndex++, cstToUse);
}
phi.replaceUsers(newOutValue);
- Instruction newInstruction = new Xor(NumericType.INT, newOutValue, testValue,
- cstToUse.outValue());
+ Instruction newInstruction =
+ Xor.create(NumericType.INT, newOutValue, testValue, cstToUse.outValue());
newInstruction.setBlock(phiBlock);
// The xor is replacing a phi so it does not have an actual position.
newInstruction.setPosition(phiPosition);
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 ee9878a..12e1f1e 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
@@ -1333,76 +1333,102 @@
}
return Reason.INVALID_INVOKE_ON_ARRAY;
}
- DexClassAndMethod singleTarget = invoke.lookupSingleTarget(appView, code.context());
- if (singleTarget == null) {
+
+ DexClassAndMethod resolvedMethod =
+ appView
+ .appInfo()
+ .resolveMethod(invoke.getInvokedMethod(), invoke.getInterfaceBit())
+ .getResolutionPair();
+ if (resolvedMethod == null) {
return Reason.INVALID_INVOKE;
}
- DexMethod singleTargetReference = singleTarget.getReference();
- DexClass targetHolder = singleTarget.getHolder();
- if (targetHolder.isProgramClass()) {
- if (targetHolder.isEnum() && singleTarget.getDefinition().isInstanceInitializer()) {
+ // The single target may be null if for example this is a virtual invoke into an abstract
+ // method.
+ DexClassAndMethod singleTarget = invoke.lookupSingleTarget(appView, code.context());
+ DexClassAndMethod mostAccurateTarget = singleTarget == null ? resolvedMethod : singleTarget;
+
+ if (mostAccurateTarget.isProgramMethod()) {
+ if (mostAccurateTarget.getHolder().isEnum()
+ && resolvedMethod.getDefinition().isInstanceInitializer()) {
// The enum instance initializer is only allowed to be called from an initializer of the
// enum itself.
- if (getEnumUnboxingCandidateOrNull(code.context().getHolder().getType())
- != getEnumUnboxingCandidateOrNull(targetHolder.getType())
+ if (getEnumUnboxingCandidateOrNull(code.context().getHolderType())
+ != getEnumUnboxingCandidateOrNull(mostAccurateTarget.getHolderType())
|| !context.getDefinition().isInitializer()) {
return Reason.INVALID_INIT;
}
- if (code.method().isInstanceInitializer() && !invoke.getFirstArgument().isThis()) {
+ if (context.getDefinition().isInstanceInitializer()
+ && !invoke.getFirstArgument().isThis()) {
return Reason.INVALID_INIT;
}
}
// Check if this is a checkNotNull() user. In this case, we can create a copy of the method
// that takes an int instead of java.lang.Object and call that method instead.
- EnumUnboxerMethodClassification classification =
- singleTarget.getOptimizationInfo().getEnumUnboxerMethodClassification();
- if (classification.isCheckNotNullClassification()) {
- CheckNotNullEnumUnboxerMethodClassification checkNotNullClassification =
- classification.asCheckNotNullClassification();
- if (checkNotNullClassification.isUseEligibleForUnboxing(
- invoke.asInvokeStatic(), enumValue)) {
- GraphLens graphLens = appView.graphLens();
- checkNotNullMethodsBuilder
- .computeIfAbsent(
- singleTarget.asProgramMethod(),
- ignoreKey(
- () ->
- LongLivedClassSetBuilder.createConcurrentBuilderForIdentitySet(
- graphLens)),
- graphLens)
- .add(enumClass, graphLens);
- return Reason.ELIGIBLE;
+ if (singleTarget != null) {
+ EnumUnboxerMethodClassification classification =
+ singleTarget.getOptimizationInfo().getEnumUnboxerMethodClassification();
+ if (classification.isCheckNotNullClassification()) {
+ assert singleTarget.getDefinition().isStatic();
+ CheckNotNullEnumUnboxerMethodClassification checkNotNullClassification =
+ classification.asCheckNotNullClassification();
+ if (checkNotNullClassification.isUseEligibleForUnboxing(
+ invoke.asInvokeStatic(), enumValue)) {
+ GraphLens graphLens = appView.graphLens();
+ checkNotNullMethodsBuilder
+ .computeIfAbsent(
+ singleTarget.asProgramMethod(),
+ ignoreKey(
+ () ->
+ LongLivedClassSetBuilder.createConcurrentBuilderForIdentitySet(
+ graphLens)),
+ graphLens)
+ .add(enumClass, graphLens);
+ return Reason.ELIGIBLE;
+ }
}
}
// Check that the enum-value only flows into parameters whose type exactly matches the
// enum's type.
- for (int i = 0; i < singleTarget.getParameters().size(); i++) {
+ for (int i = 0; i < mostAccurateTarget.getParameters().size(); i++) {
if (invoke.getArgumentForParameter(i) == enumValue
&& !enumUnboxingCandidatesInfo.isAssignableTo(
- singleTarget.getParameter(i).toBaseType(factory), enumClass.getType())) {
- return new IllegalInvokeWithImpreciseParameterTypeReason(singleTargetReference);
+ mostAccurateTarget.getParameter(i).toBaseType(factory), enumClass.getType())) {
+ return new IllegalInvokeWithImpreciseParameterTypeReason(
+ mostAccurateTarget.getReference());
}
}
if (invoke.isInvokeMethodWithReceiver()) {
Value receiver = invoke.asInvokeMethodWithReceiver().getReceiver();
- if (receiver == enumValue && targetHolder.isInterface()) {
+ if (receiver == enumValue && mostAccurateTarget.getHolder().isInterface()) {
return Reason.DEFAULT_METHOD_INVOKE;
+
}
}
return Reason.ELIGIBLE;
}
- if (targetHolder.isClasspathClass()) {
+ if (mostAccurateTarget.getHolder().isClasspathClass()) {
return Reason.INVALID_INVOKE_CLASSPATH;
}
- assert targetHolder.isLibraryClass();
+ assert mostAccurateTarget.getHolder().isLibraryClass();
+
+ if (singleTarget == null) {
+ // We don't attempt library modeling if we don't have a single target.
+ return Reason.INVALID_INVOKE;
+ }
Reason reason =
analyzeLibraryInvoke(
- invoke, code, context, enumClass, enumValue, singleTargetReference, targetHolder);
+ invoke,
+ code,
+ context,
+ enumClass,
+ enumValue,
+ singleTarget.getReference(),
+ singleTarget.getHolder());
if (reason == Reason.ELIGIBLE) {
markMethodDependsOnLibraryModelisation(context);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java
index da8ac44..3aaff49 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java
@@ -201,7 +201,7 @@
newMethodSignatures = new BidirectionalOneToManyRepresentativeHashMap<>();
private final Map<DexMethod, DexMethod> methodMap = new IdentityHashMap<>();
- private Map<DexMethod, RewrittenPrototypeDescription> prototypeChangesPerMethod =
+ private final Map<DexMethod, RewrittenPrototypeDescription> prototypeChangesPerMethod =
new IdentityHashMap<>();
Builder(AppView<AppInfoWithLiveness> appView) {
@@ -219,41 +219,31 @@
if (from == to) {
return;
}
- newFieldSignatures.put(from, to);
- }
-
- private RewrittenPrototypeDescription recordPrototypeChanges(
- DexMethod from,
- DexMethod to,
- boolean fromStatic,
- boolean toStatic,
- boolean virtualReceiverAlreadyRemapped,
- List<ExtraUnusedNullParameter> extraUnusedNullParameters) {
- assert from != to;
- RewrittenPrototypeDescription prototypeChanges =
- computePrototypeChanges(
- from,
- to,
- fromStatic,
- toStatic,
- virtualReceiverAlreadyRemapped,
- extraUnusedNullParameters);
- prototypeChangesPerMethod.put(to, prototypeChanges);
- return prototypeChanges;
+ synchronized (this) {
+ newFieldSignatures.put(from, to);
+ }
}
public void moveAndMap(DexMethod from, DexMethod to, boolean fromStatic) {
moveAndMap(from, to, fromStatic, true, Collections.emptyList());
}
- public RewrittenPrototypeDescription moveVirtual(DexMethod from, DexMethod to) {
- newMethodSignatures.put(from, to);
- return recordPrototypeChanges(from, to, false, true, false, Collections.emptyList());
+ public void moveVirtual(DexMethod from, DexMethod to) {
+ RewrittenPrototypeDescription prototypeChanges =
+ computePrototypeChanges(from, to, false, true, false, Collections.emptyList());
+ synchronized (this) {
+ newMethodSignatures.put(from, to);
+ prototypeChangesPerMethod.put(to, prototypeChanges);
+ }
}
- public RewrittenPrototypeDescription mapToDispatch(DexMethod from, DexMethod to) {
- methodMap.put(from, to);
- return recordPrototypeChanges(from, to, false, true, true, Collections.emptyList());
+ public void mapToDispatch(DexMethod from, DexMethod to) {
+ RewrittenPrototypeDescription prototypeChanges =
+ computePrototypeChanges(from, to, false, true, true, Collections.emptyList());
+ synchronized (this) {
+ methodMap.put(from, to);
+ prototypeChangesPerMethod.put(to, prototypeChanges);
+ }
}
public RewrittenPrototypeDescription moveAndMap(
@@ -262,10 +252,14 @@
boolean fromStatic,
boolean toStatic,
List<ExtraUnusedNullParameter> extraUnusedNullParameters) {
- newMethodSignatures.put(from, to);
- methodMap.put(from, to);
- return recordPrototypeChanges(
- from, to, fromStatic, toStatic, false, extraUnusedNullParameters);
+ RewrittenPrototypeDescription prototypeChanges =
+ computePrototypeChanges(from, to, fromStatic, toStatic, false, extraUnusedNullParameters);
+ synchronized (this) {
+ newMethodSignatures.put(from, to);
+ methodMap.put(from, to);
+ prototypeChangesPerMethod.put(to, prototypeChanges);
+ }
+ return prototypeChanges;
}
private RewrittenPrototypeDescription computePrototypeChanges(
@@ -275,6 +269,7 @@
boolean toStatic,
boolean virtualReceiverAlreadyRemapped,
List<ExtraUnusedNullParameter> extraUnusedNullParameters) {
+ assert from != to;
int offsetDiff = 0;
int toOffset = BooleanUtils.intValue(!toStatic);
ArgumentInfoCollection.Builder builder =
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
index 8d04a32..07a54e2 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
@@ -9,7 +9,6 @@
import com.android.tools.r8.contexts.CompilationContext.ProcessorContext;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedField.Builder;
@@ -26,6 +25,9 @@
import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.PrunedItems;
+import com.android.tools.r8.graph.fixup.ConcurrentMethodFixup;
+import com.android.tools.r8.graph.fixup.ConcurrentMethodFixup.ProgramClassFixer;
+import com.android.tools.r8.graph.fixup.MethodNamingUtility;
import com.android.tools.r8.graph.lens.MethodLookupResult;
import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
@@ -66,10 +68,11 @@
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.Sets;
-import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap;
-import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
+import it.unimi.dsi.fastutil.ints.Int2ObjectLinkedOpenHashMap;
+import it.unimi.dsi.fastutil.ints.Int2ObjectSortedMap;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Comparator;
import java.util.IdentityHashMap;
import java.util.LinkedHashMap;
import java.util.List;
@@ -81,7 +84,7 @@
import java.util.concurrent.ExecutorService;
import java.util.function.Predicate;
-class EnumUnboxingTreeFixer {
+class EnumUnboxingTreeFixer implements ProgramClassFixer {
private final EnumUnboxingLens.Builder lensBuilder;
private final AppView<AppInfoWithLiveness> appView;
@@ -94,7 +97,9 @@
// we duplicate that here as DexProgramClasses.
private final Map<DexProgramClass, Set<DexProgramClass>> unboxedEnumHierarchy;
private final EnumUnboxingUtilityClasses utilityClasses;
- private final ProgramMethodMap<CfCodeWithLens> dispatchMethods = ProgramMethodMap.create();
+ private final ProgramMethodMap<CfCodeWithLens> dispatchMethods =
+ ProgramMethodMap.createConcurrent();
+ private final PrunedItems.Builder prunedItemsBuilder;
EnumUnboxingTreeFixer(
AppView<AppInfoWithLiveness> appView,
@@ -110,6 +115,7 @@
this.lensBuilder =
EnumUnboxingLens.enumUnboxingLensBuilder(appView).mapUnboxedEnums(getUnboxedEnums());
this.utilityClasses = utilityClasses;
+ this.prunedItemsBuilder = PrunedItems.concurrentBuilder();
}
private Set<DexProgramClass> computeUnboxedEnumClasses() {
@@ -128,39 +134,13 @@
Result fixupTypeReferences(IRConverter converter, ExecutorService executorService)
throws ExecutionException {
- PrunedItems.Builder prunedItemsBuilder = PrunedItems.builder();
// We do this before so that we can still perform lookup of definitions.
fixupSuperEnumClassInitializers(converter, executorService);
// Fix all methods and fields using enums to unbox.
- // TODO(b/191617665): Parallelize this fixup.
- for (DexProgramClass clazz : appView.appInfo().classes()) {
- if (enumDataMap.isSuperUnboxedEnum(clazz.getType())) {
-
- // Clear the initializers and move the other methods to the new location.
- LocalEnumUnboxingUtilityClass localUtilityClass =
- utilityClasses.getLocalUtilityClass(clazz);
- Collection<DexEncodedField> localUtilityFields =
- createLocalUtilityFields(clazz, localUtilityClass, prunedItemsBuilder);
- Collection<DexEncodedMethod> localUtilityMethods =
- createLocalUtilityMethods(
- clazz, unboxedEnumHierarchy.get(clazz), localUtilityClass, prunedItemsBuilder);
-
- // Cleanup old classes.
- cleanUpOldClass(clazz);
- for (DexProgramClass subEnum : unboxedEnumHierarchy.get(clazz)) {
- cleanUpOldClass(subEnum);
- }
-
- // Update members on the local utility class.
- localUtilityClass.getDefinition().setDirectMethods(localUtilityMethods);
- localUtilityClass.getDefinition().setStaticFields(localUtilityFields);
- } else if (!enumDataMap.isUnboxedEnum(clazz.getType())) {
- clazz.getMethodCollection().replaceMethods(this::fixupEncodedMethod);
- clazz.getFieldCollection().replaceFields(this::fixupEncodedField);
- }
- }
+ new ConcurrentMethodFixup(appView, this)
+ .fixupClassesConcurrentlyByConnectedProgramComponents(Timing.empty(), executorService);
// Install the new graph lens before processing any checkNotZero() methods.
EnumUnboxingLens lens = lensBuilder.build(appView);
@@ -191,6 +171,38 @@
clazz.getMethodCollection().clearVirtualMethods();
}
+ @Override
+ public boolean shouldReserveAsIfPinned(ProgramMethod method) {
+ DexProto oldProto = method.getProto();
+ DexProto newProto = fixupProto(oldProto);
+ // We don't track nor reprocess dependencies of unchanged methods so we have to maintain them
+ // with the same signature.
+ return oldProto == newProto;
+ }
+
+ @Override
+ public void fixupProgramClass(DexProgramClass clazz, MethodNamingUtility utility) {
+ if (enumDataMap.isSuperUnboxedEnum(clazz.getType())) {
+ // Clear the initializers and move the other methods to the new location.
+ LocalEnumUnboxingUtilityClass localUtilityClass = utilityClasses.getLocalUtilityClass(clazz);
+ Collection<DexEncodedField> localUtilityFields =
+ createLocalUtilityFields(clazz, localUtilityClass);
+ Collection<DexEncodedMethod> localUtilityMethods =
+ createLocalUtilityMethods(clazz, unboxedEnumHierarchy.get(clazz), localUtilityClass);
+ // Cleanup old classes.
+ cleanUpOldClass(clazz);
+ for (DexProgramClass subEnum : unboxedEnumHierarchy.get(clazz)) {
+ cleanUpOldClass(subEnum);
+ }
+ // Update members on the local utility class.
+ localUtilityClass.getDefinition().setDirectMethods(localUtilityMethods);
+ localUtilityClass.getDefinition().setStaticFields(localUtilityFields);
+ } else if (!enumDataMap.isUnboxedEnum(clazz.getType())) {
+ clazz.getMethodCollection().replaceMethods(m -> fixupEncodedMethod(m, utility));
+ clazz.getFieldCollection().replaceFields(this::fixupEncodedField);
+ }
+ }
+
private BiMap<DexMethod, DexMethod> duplicateCheckNotNullMethods(
IRConverter converter, ExecutorService executorService) throws ExecutionException {
BiMap<DexMethod, DexMethod> checkNotNullToCheckNotZeroMapping = HashBiMap.create();
@@ -397,8 +409,11 @@
instructionsToRemove.put(constructorInvoke, Optional.empty());
}
- ProgramMethod constructor =
- unboxedEnum.lookupProgramMethod(lookupResult.getReference());
+ DexProgramClass holder =
+ newInstance.getType() == unboxedEnum.getType()
+ ? unboxedEnum
+ : appView.programDefinitionFor(newInstance.getType(), classInitializer);
+ ProgramMethod constructor = holder.lookupProgramMethod(lookupResult.getReference());
assert constructor != null;
InstanceFieldInitializationInfo ordinalInitializationInfo =
@@ -473,9 +488,7 @@
}
private Collection<DexEncodedField> createLocalUtilityFields(
- DexProgramClass unboxedEnum,
- LocalEnumUnboxingUtilityClass localUtilityClass,
- PrunedItems.Builder prunedItemsBuilder) {
+ DexProgramClass unboxedEnum, LocalEnumUnboxingUtilityClass localUtilityClass) {
EnumData enumData = enumDataMap.get(unboxedEnum);
Map<DexField, DexEncodedField> localUtilityFields =
new LinkedHashMap<>(unboxedEnum.staticFields().size());
@@ -532,7 +545,6 @@
private void processMethod(
ProgramMethod method,
- PrunedItems.Builder prunedItemsBuilder,
DexMethodSignatureSet nonPrivateVirtualMethods,
LocalEnumUnboxingUtilityClass localUtilityClass,
Map<DexMethod, DexEncodedMethod> localUtilityMethods) {
@@ -552,8 +564,7 @@
private Collection<DexEncodedMethod> createLocalUtilityMethods(
DexProgramClass unboxedEnum,
Set<DexProgramClass> subEnums,
- LocalEnumUnboxingUtilityClass localUtilityClass,
- PrunedItems.Builder prunedItemsBuilder) {
+ LocalEnumUnboxingUtilityClass localUtilityClass) {
Map<DexMethod, DexEncodedMethod> localUtilityMethods =
new LinkedHashMap<>(
localUtilityClass.getDefinition().getMethodCollection().size()
@@ -568,7 +579,6 @@
method ->
processMethod(
method,
- prunedItemsBuilder,
nonPrivateVirtualMethods,
localUtilityClass,
localUtilityMethods));
@@ -578,7 +588,6 @@
method ->
processMethod(
method,
- prunedItemsBuilder,
nonPrivateVirtualMethods,
localUtilityClass,
localUtilityMethods));
@@ -617,7 +626,7 @@
}
if (superMethod == null || subimplementations.isEmpty()) {
// No emulated dispatch is required, just move everything.
- if (superMethod != null) {
+ if (superMethod != null && !superMethod.getAccessFlags().isAbstract()) {
assert superMethod.isProgramMethod();
directMoveAndMap(localUtilityClass, localUtilityMethods, superMethod.asProgramMethod());
}
@@ -635,14 +644,13 @@
LocalEnumUnboxingUtilityClass localUtilityClass,
Map<DexMethod, DexEncodedMethod> localUtilityMethods,
DexClassAndMethod superMethod,
- ProgramMethodSet subimplementations) {
- assert !subimplementations.isEmpty();
+ ProgramMethodSet unorderedSubimplementations) {
+ assert !unorderedSubimplementations.isEmpty();
DexMethod superUtilityMethod;
if (superMethod.isProgramMethod()) {
superUtilityMethod =
installLocalUtilityMethod(
- localUtilityClass, localUtilityMethods, superMethod.asProgramMethod())
- .getReference();
+ localUtilityClass, localUtilityMethods, superMethod.asProgramMethod());
} else {
// All methods but toString() are final or non-virtual.
// We could support other cases by setting correctly the superUtilityMethod here.
@@ -650,17 +658,19 @@
superUtilityMethod = localUtilityClass.computeToStringUtilityMethod(factory);
}
Map<DexMethod, DexMethod> overrideToUtilityMethods = new IdentityHashMap<>();
- for (ProgramMethod subMethod : subimplementations) {
- DexEncodedMethod subEnumLocalUtilityMethod =
+ List<ProgramMethod> sortedSubimplementations = new ArrayList<>(unorderedSubimplementations);
+ sortedSubimplementations.sort(Comparator.comparing(ProgramMethod::getHolderType));
+ for (ProgramMethod subMethod : sortedSubimplementations) {
+ DexMethod subEnumLocalUtilityMethod =
installLocalUtilityMethod(localUtilityClass, localUtilityMethods, subMethod);
- overrideToUtilityMethods.put(
- subMethod.getReference(), subEnumLocalUtilityMethod.getReference());
+ assert subEnumLocalUtilityMethod != null;
+ overrideToUtilityMethods.put(subMethod.getReference(), subEnumLocalUtilityMethod);
}
DexMethod dispatch =
installDispatchMethod(
localUtilityClass,
localUtilityMethods,
- subimplementations.iterator().next(),
+ sortedSubimplementations.iterator().next(),
superUtilityMethod,
overrideToUtilityMethods)
.getReference();
@@ -682,16 +692,21 @@
LocalEnumUnboxingUtilityClass localUtilityClass,
Map<DexMethod, DexEncodedMethod> localUtilityMethods,
ProgramMethod method) {
- DexEncodedMethod utilityMethod =
+ assert !method.getAccessFlags().isAbstract();
+ DexMethod utilityMethod =
installLocalUtilityMethod(localUtilityClass, localUtilityMethods, method);
- lensBuilder.moveAndMap(
- method.getReference(), utilityMethod.getReference(), method.getDefinition().isStatic());
+ assert utilityMethod != null;
+ lensBuilder.moveAndMap(method.getReference(), utilityMethod, method.getDefinition().isStatic());
}
public void recordEmulatedDispatch(DexMethod from, DexMethod move, DexMethod dispatch) {
// Move is used for getRenamedSignature and to remap invoke-super.
// Map is used to remap all the other invokes.
- lensBuilder.moveVirtual(from, move);
+ assert from != null;
+ assert dispatch != null;
+ if (move != null) {
+ lensBuilder.moveVirtual(from, move);
+ }
lensBuilder.mapToDispatch(from, dispatch);
}
@@ -708,7 +723,7 @@
fixupProto(factory.prependHolderToProto(representative.getReference())),
localUtilityClass.getType(),
newMethodSignature -> !localUtilityMethods.containsKey(newMethodSignature));
- Int2ObjectMap<DexMethod> methodMap = new Int2ObjectArrayMap<>();
+ Int2ObjectSortedMap<DexMethod> methodMap = new Int2ObjectLinkedOpenHashMap<>();
IdentityHashMap<DexType, DexMethod> typeToMethod = new IdentityHashMap<>();
map.forEach(
(methodReference, newMethodReference) ->
@@ -744,10 +759,13 @@
return newLocalUtilityMethod;
}
- private DexEncodedMethod installLocalUtilityMethod(
+ private DexMethod installLocalUtilityMethod(
LocalEnumUnboxingUtilityClass localUtilityClass,
Map<DexMethod, DexEncodedMethod> localUtilityMethods,
ProgramMethod method) {
+ if (method.getAccessFlags().isAbstract()) {
+ return null;
+ }
DexEncodedMethod newLocalUtilityMethod =
createLocalUtilityMethod(
method,
@@ -755,7 +773,7 @@
newMethodSignature -> !localUtilityMethods.containsKey(newMethodSignature));
assert !localUtilityMethods.containsKey(newLocalUtilityMethod.getReference());
localUtilityMethods.put(newLocalUtilityMethod.getReference(), newLocalUtilityMethod);
- return newLocalUtilityMethod;
+ return newLocalUtilityMethod.getReference();
}
private DexEncodedMethod createLocalUtilityMethod(
@@ -804,20 +822,25 @@
&& !field.getDefinition().getOptimizationInfo().isDead());
}
- private DexEncodedMethod fixupEncodedMethod(DexEncodedMethod method) {
+ private DexEncodedMethod fixupEncodedMethod(
+ DexEncodedMethod method, MethodNamingUtility utility) {
DexProto oldProto = method.getProto();
DexProto newProto = fixupProto(oldProto);
- if (newProto == method.getProto()) {
+ if (oldProto == newProto) {
+ assert method.getReference()
+ == utility.nextUniqueMethod(
+ method, newProto, utilityClasses.getSharedUtilityClass().getType());
return method;
}
+
+ DexMethod newMethod =
+ utility.nextUniqueMethod(
+ method, newProto, utilityClasses.getSharedUtilityClass().getType());
+ assert newMethod != method.getReference();
assert !method.isClassInitializer();
assert !method.isLibraryMethodOverride().isTrue()
: "Enum unboxing is changing the signature of a library override in a non unboxed class.";
- // We add the $enumunboxing$ suffix to make sure we do not create a library override.
- String newMethodName =
- method.getName().toString() + (method.isNonPrivateVirtualMethod() ? "$enumunboxing$" : "");
- DexMethod newMethod = factory.createMethod(method.getHolderType(), newProto, newMethodName);
- newMethod = ensureUniqueMethod(method, newMethod);
+
List<ExtraUnusedNullParameter> extraUnusedNullParameters =
ExtraUnusedNullParameter.computeExtraUnusedNullParameters(method.getReference(), newMethod);
boolean isStatic = method.isStatic();
@@ -835,27 +858,6 @@
method.isNonPrivateVirtualMethod(), OptionalBool.FALSE));
}
- private DexMethod ensureUniqueMethod(DexEncodedMethod encodedMethod, DexMethod newMethod) {
- DexClass holder = appView.definitionFor(encodedMethod.getHolderType());
- assert holder != null;
- if (newMethod.isInstanceInitializer(appView.dexItemFactory())) {
- newMethod =
- factory.createInstanceInitializerWithFreshProto(
- newMethod,
- utilityClasses.getSharedUtilityClass().getType(),
- tryMethod -> holder.lookupMethod(tryMethod) == null);
- } else {
- int index = 0;
- while (holder.lookupMethod(newMethod) != null) {
- newMethod =
- newMethod.withName(
- encodedMethod.getName().toString() + "$enumunboxing$" + index++,
- appView.dexItemFactory());
- }
- }
- return newMethod;
- }
-
private DexEncodedField fixupEncodedField(DexEncodedField encodedField) {
DexField field = encodedField.getReference();
DexType newType = fixupType(field.type);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/outliner/OutlinerImpl.java b/src/main/java/com/android/tools/r8/ir/optimize/outliner/OutlinerImpl.java
index ef163ad..3b0caa2 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/outliner/OutlinerImpl.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/outliner/OutlinerImpl.java
@@ -300,10 +300,10 @@
Instruction newInstruction = null;
switch (type) {
case ADD:
- newInstruction = new Add(numericType, outValue, inValues.get(0), inValues.get(1));
+ newInstruction = Add.create(numericType, outValue, inValues.get(0), inValues.get(1));
break;
case MUL:
- newInstruction = new Mul(numericType, outValue, inValues.get(0), inValues.get(1));
+ newInstruction = Mul.create(numericType, outValue, inValues.get(0), inValues.get(1));
break;
case SUB:
newInstruction = new Sub(numericType, outValue, inValues.get(0), inValues.get(1));
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/EnumUnboxingCfCodeProvider.java b/src/main/java/com/android/tools/r8/ir/synthetic/EnumUnboxingCfCodeProvider.java
index af9e41c..755f207 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/EnumUnboxingCfCodeProvider.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/EnumUnboxingCfCodeProvider.java
@@ -36,7 +36,7 @@
import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.ir.optimize.enums.EnumDataMap.EnumData;
import com.android.tools.r8.ir.optimize.enums.EnumInstanceFieldData.EnumInstanceFieldMappingData;
-import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
+import it.unimi.dsi.fastutil.ints.Int2ObjectSortedMap;
import java.util.ArrayList;
import java.util.List;
import org.objectweb.asm.Opcodes;
@@ -72,13 +72,13 @@
private final GraphLens codeLens;
private final DexMethod superEnumMethod;
- private final Int2ObjectMap<DexMethod> methodMap;
+ private final Int2ObjectSortedMap<DexMethod> methodMap;
public EnumUnboxingMethodDispatchCfCodeProvider(
AppView<?> appView,
DexType holder,
DexMethod superEnumMethod,
- Int2ObjectMap<DexMethod> methodMap) {
+ Int2ObjectSortedMap<DexMethod> methodMap) {
super(appView, holder);
this.codeLens = appView.codeLens();
this.superEnumMethod = superEnumMethod;
@@ -90,27 +90,42 @@
// TODO(b/167942775): Should use a table-switch for large enums (maybe same threshold in the
// rewriter of switchmaps).
+ assert !methodMap.isEmpty();
DexItemFactory factory = appView.dexItemFactory();
- int returnInvokeSize = superEnumMethod.getParameters().size() + 2;
- List<CfInstruction> instructions =
- new ArrayList<>(methodMap.size() * (returnInvokeSize + 5) + returnInvokeSize);
+ boolean hasDefaultCase = superEnumMethod != null;
+ DexMethod representative = methodMap.values().iterator().next();
+
+ int invokeSize = representative.getParameters().size() + 2;
+ int branchSize = 5;
+ int instructionsSize =
+ methodMap.size() * (invokeSize + branchSize)
+ + (hasDefaultCase ? invokeSize : -branchSize);
+ List<CfInstruction> instructions = new ArrayList<>(instructionsSize);
CfFrame.Builder frameBuilder = CfFrame.builder();
- for (DexType parameter : superEnumMethod.getParameters()) {
+ for (DexType parameter : representative.getParameters()) {
frameBuilder.appendLocal(FrameType.initialized(parameter));
}
methodMap.forEach(
(unboxedEnumValue, method) -> {
- CfLabel dest = new CfLabel();
- instructions.add(new CfLoad(ValueType.fromDexType(factory.intType), 0));
- instructions.add(new CfConstNumber(unboxedEnumValue, ValueType.INT));
- instructions.add(new CfIfCmp(IfType.NE, ValueType.INT, dest));
- addReturnInvoke(instructions, method);
- instructions.add(dest);
- instructions.add(frameBuilder.build());
+ boolean lastCase = methodMap.lastIntKey() == unboxedEnumValue && !hasDefaultCase;
+ if (!lastCase) {
+ CfLabel dest = new CfLabel();
+ instructions.add(new CfLoad(ValueType.fromDexType(factory.intType), 0));
+ instructions.add(new CfConstNumber(unboxedEnumValue, ValueType.INT));
+ instructions.add(new CfIfCmp(IfType.NE, ValueType.INT, dest));
+ addReturnInvoke(instructions, method);
+ instructions.add(dest);
+ instructions.add(frameBuilder.build());
+ } else {
+ addReturnInvoke(instructions, method);
+ }
});
- addReturnInvoke(instructions, superEnumMethod);
+ if (hasDefaultCase) {
+ addReturnInvoke(instructions, superEnumMethod);
+ }
+ assert instructions.size() == instructionsSize;
return new CfCodeWithLens(getHolder(), defaultMaxStack(), defaultMaxLocals(), instructions);
}
diff --git a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
index 5c92ac9..19febfd 100644
--- a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
@@ -112,7 +112,7 @@
}
public void write(ClassFileConsumer consumer) {
- assert options.proguardMapConsumer == null;
+ assert options.mapConsumer == null;
write(consumer, null);
}
@@ -138,7 +138,7 @@
private void writeApplication(AndroidApp inputApp, ClassFileConsumer consumer) {
ProguardMapId proguardMapId = null;
- if (options.proguardMapConsumer != null) {
+ if (options.mapConsumer != null) {
assert marker.isPresent();
proguardMapId =
runAndWriteMap(
diff --git a/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java b/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java
index 70c442b..6d6d497 100644
--- a/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java
+++ b/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.lightir;
+import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexField;
@@ -11,12 +12,16 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.type.Nullability;
+import com.android.tools.r8.ir.analysis.type.PrimitiveTypeElement;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.Add;
import com.android.tools.r8.ir.code.Argument;
+import com.android.tools.r8.ir.code.ArrayGet;
import com.android.tools.r8.ir.code.ArrayLength;
+import com.android.tools.r8.ir.code.ArrayPut;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.CatchHandlers;
+import com.android.tools.r8.ir.code.CheckCast;
import com.android.tools.r8.ir.code.Cmp;
import com.android.tools.r8.ir.code.Cmp.Bias;
import com.android.tools.r8.ir.code.ConstNumber;
@@ -36,6 +41,7 @@
import com.android.tools.r8.ir.code.InvokeStatic;
import com.android.tools.r8.ir.code.InvokeSuper;
import com.android.tools.r8.ir.code.InvokeVirtual;
+import com.android.tools.r8.ir.code.MemberType;
import com.android.tools.r8.ir.code.Monitor;
import com.android.tools.r8.ir.code.MonitorType;
import com.android.tools.r8.ir.code.MoveException;
@@ -54,9 +60,12 @@
import com.android.tools.r8.ir.code.Sub;
import com.android.tools.r8.ir.code.Throw;
import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.code.ValueType;
+import com.android.tools.r8.ir.code.Xor;
import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
import com.android.tools.r8.lightir.LirCode.PositionEntry;
import com.android.tools.r8.utils.ListUtils;
+import com.google.common.collect.ImmutableList;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
import it.unimi.dsi.fastutil.ints.IntArrayList;
@@ -202,8 +211,7 @@
// LIR has no value-user info so after building is done, removed unused values.
for (Instruction instruction : block.getInstructions()) {
if (instruction.hasOutValue()
- && !instruction.isArgument()
- && !instruction.isMoveException()
+ && instruction.isInvoke()
&& instruction.hasUnusedOutValue()) {
instruction.clearOutValue();
}
@@ -330,6 +338,11 @@
}
@Override
+ public void onInstruction() {
+ throw new Unimplemented("Missing IR conversion");
+ }
+
+ @Override
public void onConstNull() {
Value dest = getOutValueForNextInstruction(TypeElement.getNull());
addInstruction(new ConstNumber(dest, 0));
@@ -342,36 +355,66 @@
}
@Override
+ public void onConstFloat(int value) {
+ Value dest = getOutValueForNextInstruction(TypeElement.getFloat());
+ addInstruction(new ConstNumber(dest, value));
+ }
+
+ @Override
+ public void onConstLong(long value) {
+ Value dest = getOutValueForNextInstruction(TypeElement.getLong());
+ addInstruction(new ConstNumber(dest, value));
+ }
+
+ @Override
+ public void onConstDouble(long value) {
+ Value dest = getOutValueForNextInstruction(TypeElement.getDouble());
+ addInstruction(new ConstNumber(dest, value));
+ }
+
+ TypeElement valueTypeElement(NumericType type) {
+ return PrimitiveTypeElement.fromNumericType(type);
+ }
+
+ @Override
public void onAdd(NumericType type, EV leftValueIndex, EV rightValueIndex) {
- Value dest = getOutValueForNextInstruction(TypeElement.getInt());
- addInstruction(new Add(type, dest, getValue(leftValueIndex), getValue(rightValueIndex)));
+ Value dest = getOutValueForNextInstruction(valueTypeElement(type));
+ addInstruction(
+ Add.createNonNormalized(type, dest, getValue(leftValueIndex), getValue(rightValueIndex)));
}
@Override
public void onSub(NumericType type, EV leftValueIndex, EV rightValueIndex) {
- Value dest = getOutValueForNextInstruction(TypeElement.getInt());
+ Value dest = getOutValueForNextInstruction(valueTypeElement(type));
addInstruction(new Sub(type, dest, getValue(leftValueIndex), getValue(rightValueIndex)));
}
@Override
public void onMul(NumericType type, EV leftValueIndex, EV rightValueIndex) {
- Value dest = getOutValueForNextInstruction(TypeElement.getInt());
- addInstruction(new Mul(type, dest, getValue(leftValueIndex), getValue(rightValueIndex)));
+ Value dest = getOutValueForNextInstruction(valueTypeElement(type));
+ addInstruction(
+ Mul.createNonNormalized(type, dest, getValue(leftValueIndex), getValue(rightValueIndex)));
}
@Override
public void onDiv(NumericType type, EV leftValueIndex, EV rightValueIndex) {
- Value dest = getOutValueForNextInstruction(TypeElement.getInt());
+ Value dest = getOutValueForNextInstruction(valueTypeElement(type));
addInstruction(new Div(type, dest, getValue(leftValueIndex), getValue(rightValueIndex)));
}
@Override
public void onRem(NumericType type, EV leftValueIndex, EV rightValueIndex) {
- Value dest = getOutValueForNextInstruction(TypeElement.getInt());
+ Value dest = getOutValueForNextInstruction(valueTypeElement(type));
addInstruction(new Rem(type, dest, getValue(leftValueIndex), getValue(rightValueIndex)));
}
@Override
+ public void onXor(NumericType type, EV left, EV right) {
+ Value dest = getOutValueForNextInstruction(valueTypeElement(type));
+ addInstruction(Xor.createNonNormalized(type, dest, getValue(left), getValue(right)));
+ }
+
+ @Override
public void onConstString(DexString string) {
Value dest = getOutValueForNextInstruction(TypeElement.stringClassType(appView));
addInstruction(new ConstString(dest, string));
@@ -396,6 +439,17 @@
}
@Override
+ public void onIfCmp(IfType ifKind, int blockIndex, EV leftValueIndex, EV rightValueIndex) {
+ BasicBlock targetBlock = getBasicBlock(blockIndex);
+ Value leftValue = getValue(leftValueIndex);
+ Value rightValue = getValue(rightValueIndex);
+ addInstruction(new If(ifKind, ImmutableList.of(leftValue, rightValue)));
+ currentBlock.link(targetBlock);
+ currentBlock.link(getBasicBlock(nextInstructionIndex));
+ closeCurrentBlock();
+ }
+
+ @Override
public void onFallthrough() {
int nextBlockIndex = peekNextInstructionIndex() + 1;
onGoto(nextBlockIndex);
@@ -498,6 +552,12 @@
}
@Override
+ public void onReturn(EV value) {
+ addInstruction(new Return(getValue(value)));
+ closeCurrentBlock();
+ }
+
+ @Override
public void onArrayLength(EV arrayValueIndex) {
Value dest = getOutValueForNextInstruction(TypeElement.getInt());
Value arrayValue = getValue(arrayValueIndex);
@@ -505,6 +565,12 @@
}
@Override
+ public void onCheckCast(DexType type, EV value) {
+ Value dest = getOutValueForNextInstruction(type.toTypeElement(appView));
+ addInstruction(new CheckCast(dest, getValue(value), type));
+ }
+
+ @Override
public void onDebugPosition() {
addInstruction(new DebugPosition());
}
@@ -578,5 +644,27 @@
public void onMonitorExit(EV value) {
addInstruction(new Monitor(MonitorType.EXIT, getValue(value)));
}
+
+ @Override
+ public void onArrayGetObject(DexType type, EV array, EV index) {
+ Value dest = getOutValueForNextInstruction(type.toTypeElement(appView));
+ addInstruction(new ArrayGet(MemberType.OBJECT, dest, getValue(array), getValue(index)));
+ }
+
+ @Override
+ public void onArrayGetPrimitive(MemberType type, EV array, EV index) {
+ // Convert the member type to a "stack value type", e.g., byte, char etc to int.
+ ValueType valueType = ValueType.fromMemberType(type);
+ DexType dexType = valueType.toDexType(appView.dexItemFactory());
+ Value dest = getOutValueForNextInstruction(dexType.toTypeElement(appView));
+ addInstruction(new ArrayGet(type, dest, getValue(array), getValue(index)));
+ }
+
+ @Override
+ public void onArrayPut(MemberType type, EV array, EV index, EV value) {
+ addInstruction(
+ ArrayPut.createWithoutVerification(
+ type, getValue(array), getValue(index), getValue(value)));
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/lightir/LirBuilder.java b/src/main/java/com/android/tools/r8/lightir/LirBuilder.java
index 91ce188..8f41773 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirBuilder.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirBuilder.java
@@ -5,6 +5,7 @@
import com.android.tools.r8.cf.code.CfArithmeticBinop;
import com.android.tools.r8.cf.code.CfArithmeticBinop.Opcode;
+import com.android.tools.r8.cf.code.CfLogicalBinop;
import com.android.tools.r8.cf.code.CfNumberConversion;
import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.errors.Unreachable;
@@ -22,6 +23,7 @@
import com.android.tools.r8.ir.code.Cmp.Bias;
import com.android.tools.r8.ir.code.IRMetadata;
import com.android.tools.r8.ir.code.IfType;
+import com.android.tools.r8.ir.code.MemberType;
import com.android.tools.r8.ir.code.MonitorType;
import com.android.tools.r8.ir.code.NumericType;
import com.android.tools.r8.ir.code.Position;
@@ -88,6 +90,10 @@
flushedPosition = currentPosition;
}
+ public DexItemFactory factory() {
+ return factory;
+ }
+
public boolean verifyCurrentValueIndex(int valueIndex) {
assert instructionCount + argumentCount == valueIndex;
return true;
@@ -109,7 +115,6 @@
public LirBuilder<V, EV> setCurrentPosition(Position position) {
assert position != null;
- assert position != Position.none();
currentPosition = position;
return this;
}
@@ -363,6 +368,11 @@
return addOneValueInstruction(LirOpcodes.ARRAYLENGTH, array);
}
+ public LirBuilder<V, EV> addCheckCast(DexType type, V value) {
+ return addInstructionTemplate(
+ LirOpcodes.CHECKCAST, Collections.singletonList(type), Collections.singletonList(value));
+ }
+
public LirBuilder<V, EV> addStaticGet(DexField field) {
return addOneItemInstruction(LirOpcodes.GETSTATIC, field);
}
@@ -566,7 +576,7 @@
}
public LirBuilder<V, EV> addArithmeticBinop(
- Opcode binop, NumericType type, V leftValue, V rightValue) {
+ CfArithmeticBinop.Opcode binop, NumericType type, V leftValue, V rightValue) {
// The LIR and CF opcodes are the same values, check that the two endpoints match.
assert LirOpcodes.IADD == CfArithmeticBinop.getAsmOpcode(Opcode.Add, NumericType.INT);
assert LirOpcodes.DREM == CfArithmeticBinop.getAsmOpcode(Opcode.Rem, NumericType.DOUBLE);
@@ -574,6 +584,17 @@
return addTwoValueInstruction(opcode, leftValue, rightValue);
}
+ public LirBuilder<V, EV> addLogicalBinop(
+ CfLogicalBinop.Opcode binop, NumericType type, V leftValue, V rightValue) {
+ // The LIR and CF opcodes are the same values, check that the two endpoints match.
+ assert LirOpcodes.ISHL
+ == CfLogicalBinop.getAsmOpcode(CfLogicalBinop.Opcode.Shl, NumericType.INT);
+ assert LirOpcodes.LXOR
+ == CfLogicalBinop.getAsmOpcode(CfLogicalBinop.Opcode.Xor, NumericType.LONG);
+ int opcode = CfLogicalBinop.getAsmOpcode(binop, type);
+ return addTwoValueInstruction(opcode, leftValue, rightValue);
+ }
+
public LirBuilder<V, EV> addMonitor(MonitorType type, V value) {
return addOneValueInstruction(
type == MonitorType.ENTER ? LirOpcodes.MONITORENTER : LirOpcodes.MONITOREXIT, value);
@@ -590,4 +611,73 @@
assert opcode <= LirOpcodes.I2S;
return addOneValueInstruction(opcode, value);
}
+
+ public LirBuilder<V, EV> addArrayGetObject(DexType destType, V array, V index) {
+ return addInstructionTemplate(
+ LirOpcodes.AALOAD, Collections.singletonList(destType), ImmutableList.of(array, index));
+ }
+
+ public LirBuilder<V, EV> addArrayGetPrimitive(MemberType memberType, V array, V index) {
+ int opcode;
+ switch (memberType) {
+ case BOOLEAN_OR_BYTE:
+ opcode = LirOpcodes.BALOAD;
+ break;
+ case CHAR:
+ opcode = LirOpcodes.CALOAD;
+ break;
+ case SHORT:
+ opcode = LirOpcodes.SALOAD;
+ break;
+ case INT:
+ opcode = LirOpcodes.IALOAD;
+ break;
+ case FLOAT:
+ opcode = LirOpcodes.FALOAD;
+ break;
+ case LONG:
+ opcode = LirOpcodes.LALOAD;
+ break;
+ case DOUBLE:
+ opcode = LirOpcodes.DALOAD;
+ break;
+ default:
+ throw new Unreachable("Unexpected object or imprecise member type: " + memberType);
+ }
+ return addInstructionTemplate(opcode, Collections.emptyList(), ImmutableList.of(array, index));
+ }
+
+ public LirBuilder<V, EV> addArrayPut(MemberType memberType, V array, V index, V value) {
+ int opcode;
+ switch (memberType) {
+ case BOOLEAN_OR_BYTE:
+ opcode = LirOpcodes.BASTORE;
+ break;
+ case CHAR:
+ opcode = LirOpcodes.CASTORE;
+ break;
+ case SHORT:
+ opcode = LirOpcodes.SASTORE;
+ break;
+ case INT:
+ opcode = LirOpcodes.IASTORE;
+ break;
+ case FLOAT:
+ opcode = LirOpcodes.FASTORE;
+ break;
+ case LONG:
+ opcode = LirOpcodes.LASTORE;
+ break;
+ case DOUBLE:
+ opcode = LirOpcodes.DASTORE;
+ break;
+ case OBJECT:
+ opcode = LirOpcodes.AASTORE;
+ break;
+ default:
+ throw new Unreachable("Unexpected imprecise member type: " + memberType);
+ }
+ return addInstructionTemplate(
+ opcode, Collections.emptyList(), ImmutableList.of(array, index, value));
+ }
}
diff --git a/src/main/java/com/android/tools/r8/lightir/LirParsedInstructionCallback.java b/src/main/java/com/android/tools/r8/lightir/LirParsedInstructionCallback.java
index de3a7b4..1fca6a6 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirParsedInstructionCallback.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirParsedInstructionCallback.java
@@ -5,12 +5,14 @@
import com.android.tools.r8.cf.code.CfNumberConversion;
import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItem;
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.code.IfType;
+import com.android.tools.r8.ir.code.MemberType;
import com.android.tools.r8.ir.code.NumericType;
import java.util.ArrayList;
import java.util.List;
@@ -49,6 +51,10 @@
return getActualValueIndex(view.getNextValueOperand());
}
+ private DexType getNextDexTypeOperand(LirInstructionView view) {
+ return (DexType) getConstantItem(view.getNextConstantOperand());
+ }
+
public void onInstruction() {}
public void onConstNull() {
@@ -79,6 +85,38 @@
onInstruction();
}
+ private void onArrayGetInternal(MemberType type, LirInstructionView view) {
+ if (type.isObject()) {
+ DexType destType = (DexType) getConstantItem(view.getNextConstantOperand());
+ EV array = getNextValueOperand(view);
+ EV index = getNextValueOperand(view);
+ onArrayGetObject(destType, array, index);
+ } else {
+ EV array = getNextValueOperand(view);
+ EV index = getNextValueOperand(view);
+ onArrayGetPrimitive(type, array, index);
+ }
+ }
+
+ public void onArrayGetPrimitive(MemberType type, EV array, EV index) {
+ onInstruction();
+ }
+
+ public void onArrayGetObject(DexType type, EV array, EV index) {
+ onInstruction();
+ }
+
+ private void onArrayPutInternal(MemberType type, LirInstructionView view) {
+ EV array = getNextValueOperand(view);
+ EV index = getNextValueOperand(view);
+ EV value = getNextValueOperand(view);
+ onArrayPut(type, array, index, value);
+ }
+
+ public void onArrayPut(MemberType type, EV array, EV index, EV value) {
+ onInstruction();
+ }
+
public void onAdd(NumericType type, EV leftValueIndex, EV rightValueIndex) {
onInstruction();
}
@@ -179,6 +217,36 @@
onRem(NumericType.DOUBLE, leftValueIndex, rightValueIndex);
}
+ private void onLogicalBinopInternal(int opcode, LirInstructionView view) {
+ EV left = getNextValueOperand(view);
+ EV right = getNextValueOperand(view);
+ switch (opcode) {
+ case LirOpcodes.ISHL:
+ case LirOpcodes.LSHL:
+ case LirOpcodes.ISHR:
+ case LirOpcodes.LSHR:
+ case LirOpcodes.IUSHR:
+ case LirOpcodes.LUSHR:
+ case LirOpcodes.IAND:
+ case LirOpcodes.LAND:
+ case LirOpcodes.IOR:
+ case LirOpcodes.LOR:
+ throw new Unimplemented();
+ case LirOpcodes.IXOR:
+ onXor(NumericType.INT, left, right);
+ return;
+ case LirOpcodes.LXOR:
+ onXor(NumericType.INT, left, right);
+ return;
+ default:
+ throw new Unreachable("Unexpected logical binop: " + opcode);
+ }
+ }
+
+ public void onXor(NumericType type, EV left, EV right) {
+ onInstruction();
+ }
+
public void onNumberConversion(int opcode, EV value) {
assert LirOpcodes.I2L <= opcode;
assert opcode <= LirOpcodes.I2S;
@@ -190,10 +258,27 @@
onInstruction();
}
+ private void onIfInternal(IfType ifKind, LirInstructionView view) {
+ int blockIndex = view.getNextBlockOperand();
+ EV valueIndex = getNextValueOperand(view);
+ onIf(ifKind, blockIndex, valueIndex);
+ }
+
public void onIf(IfType ifKind, int blockIndex, EV valueIndex) {
onInstruction();
}
+ private void onIfCmpInternal(IfType ifKind, LirInstructionView view) {
+ int blockIndex = view.getNextBlockOperand();
+ EV leftValueIndex = getNextValueOperand(view);
+ EV rightValueIndex = getNextValueOperand(view);
+ onIfCmp(ifKind, blockIndex, leftValueIndex, rightValueIndex);
+ }
+
+ public void onIfCmp(IfType ifKind, int blockIndex, EV leftValueIndex, EV rightValueIndex) {
+ onInstruction();
+ }
+
public void onGoto(int blockIndex) {
onInstruction();
}
@@ -258,10 +343,18 @@
onInstruction();
}
+ public void onReturn(EV value) {
+ onInstruction();
+ }
+
public void onArrayLength(EV arrayValueIndex) {
onInstruction();
}
+ public void onCheckCast(DexType type, EV value) {
+ onInstruction();
+ }
+
public void onDebugPosition() {
onInstruction();
}
@@ -365,6 +458,92 @@
onConstDouble(value);
return;
}
+ case LirOpcodes.IALOAD:
+ {
+ onArrayGetInternal(MemberType.INT, view);
+ break;
+ }
+ case LirOpcodes.LALOAD:
+ {
+ onArrayGetInternal(MemberType.LONG, view);
+ break;
+ }
+ case LirOpcodes.FALOAD:
+ {
+ onArrayGetInternal(MemberType.FLOAT, view);
+ break;
+ }
+ case LirOpcodes.DALOAD:
+ {
+ onArrayGetInternal(MemberType.DOUBLE, view);
+ break;
+ }
+ case LirOpcodes.AALOAD:
+ {
+ onArrayGetInternal(MemberType.OBJECT, view);
+ break;
+ }
+ case LirOpcodes.BALOAD:
+ {
+ onArrayGetInternal(MemberType.BOOLEAN_OR_BYTE, view);
+ break;
+ }
+ case LirOpcodes.CALOAD:
+ {
+ onArrayGetInternal(MemberType.CHAR, view);
+ break;
+ }
+ case LirOpcodes.SALOAD:
+ {
+ onArrayGetInternal(MemberType.SHORT, view);
+ break;
+ }
+ case LirOpcodes.IASTORE:
+ {
+ onArrayPutInternal(MemberType.INT, view);
+ break;
+ }
+ case LirOpcodes.LASTORE:
+ {
+ onArrayPutInternal(MemberType.LONG, view);
+ break;
+ }
+ case LirOpcodes.FASTORE:
+ {
+ onArrayPutInternal(MemberType.FLOAT, view);
+ break;
+ }
+
+ case LirOpcodes.DASTORE:
+ {
+ onArrayPutInternal(MemberType.DOUBLE, view);
+ break;
+ }
+
+ case LirOpcodes.AASTORE:
+ {
+ onArrayPutInternal(MemberType.OBJECT, view);
+ break;
+ }
+
+ case LirOpcodes.BASTORE:
+ {
+ onArrayPutInternal(MemberType.BOOLEAN_OR_BYTE, view);
+ break;
+ }
+
+ case LirOpcodes.CASTORE:
+ {
+ onArrayPutInternal(MemberType.CHAR, view);
+ break;
+ }
+
+ case LirOpcodes.SASTORE:
+ {
+ onArrayPutInternal(MemberType.SHORT, view);
+ break;
+ }
+
case LirOpcodes.IADD:
{
EV leftValueIndex = getNextValueOperand(view);
@@ -505,6 +684,22 @@
onRemDouble(leftValueIndex, rightValueIndex);
return;
}
+ case LirOpcodes.ISHL:
+ case LirOpcodes.LSHL:
+ case LirOpcodes.ISHR:
+ case LirOpcodes.LSHR:
+ case LirOpcodes.IUSHR:
+ case LirOpcodes.LUSHR:
+ case LirOpcodes.IAND:
+ case LirOpcodes.LAND:
+ case LirOpcodes.IOR:
+ case LirOpcodes.LOR:
+ case LirOpcodes.IXOR:
+ case LirOpcodes.LXOR:
+ {
+ onLogicalBinopInternal(opcode, view);
+ return;
+ }
case LirOpcodes.I2L:
case LirOpcodes.I2F:
case LirOpcodes.I2D:
@@ -525,11 +720,84 @@
onNumberConversion(opcode, value);
return;
}
+ case LirOpcodes.IFEQ:
+ {
+ onIfInternal(IfType.EQ, view);
+ return;
+ }
case LirOpcodes.IFNE:
{
- int blockIndex = view.getNextBlockOperand();
- EV valueIndex = getNextValueOperand(view);
- onIf(IfType.NE, blockIndex, valueIndex);
+ onIfInternal(IfType.NE, view);
+ return;
+ }
+ case LirOpcodes.IFLT:
+ {
+ onIfInternal(IfType.LT, view);
+ return;
+ }
+ case LirOpcodes.IFGE:
+ {
+ onIfInternal(IfType.GE, view);
+ return;
+ }
+ case LirOpcodes.IFGT:
+ {
+ onIfInternal(IfType.GT, view);
+ return;
+ }
+ case LirOpcodes.IFLE:
+ {
+ onIfInternal(IfType.LE, view);
+ return;
+ }
+ case LirOpcodes.IFNULL:
+ {
+ onIfInternal(IfType.EQ, view);
+ return;
+ }
+ case LirOpcodes.IFNONNULL:
+ {
+ onIfInternal(IfType.NE, view);
+ return;
+ }
+ case LirOpcodes.IF_ICMPEQ:
+ {
+ onIfCmpInternal(IfType.EQ, view);
+ return;
+ }
+ case LirOpcodes.IF_ICMPNE:
+ {
+ onIfCmpInternal(IfType.NE, view);
+ return;
+ }
+ case LirOpcodes.IF_ICMPLT:
+ {
+ onIfCmpInternal(IfType.LT, view);
+ return;
+ }
+ case LirOpcodes.IF_ICMPGE:
+ {
+ onIfCmpInternal(IfType.GE, view);
+ return;
+ }
+ case LirOpcodes.IF_ICMPGT:
+ {
+ onIfCmpInternal(IfType.GT, view);
+ return;
+ }
+ case LirOpcodes.IF_ICMPLE:
+ {
+ onIfCmpInternal(IfType.LE, view);
+ return;
+ }
+ case LirOpcodes.IF_ACMPEQ:
+ {
+ onIfCmpInternal(IfType.EQ, view);
+ return;
+ }
+ case LirOpcodes.IF_ACMPNE:
+ {
+ onIfCmpInternal(IfType.NE, view);
return;
}
case LirOpcodes.GOTO:
@@ -608,7 +876,7 @@
}
case LirOpcodes.NEWARRAY:
{
- DexType type = (DexType) getConstantItem(view.getNextConstantOperand());
+ DexType type = getNextDexTypeOperand(view);
EV size = getNextValueOperand(view);
onNewArrayEmpty(type, size);
return;
@@ -624,11 +892,24 @@
onReturnVoid();
return;
}
+ case LirOpcodes.ARETURN:
+ {
+ EV value = getNextValueOperand(view);
+ onReturn(value);
+ return;
+ }
case LirOpcodes.ARRAYLENGTH:
{
onArrayLength(getNextValueOperand(view));
return;
}
+ case LirOpcodes.CHECKCAST:
+ {
+ DexType type = getNextDexTypeOperand(view);
+ EV value = getNextValueOperand(view);
+ onCheckCast(type, value);
+ return;
+ }
case LirOpcodes.DEBUGPOS:
{
onDebugPosition();
@@ -636,7 +917,7 @@
}
case LirOpcodes.PHI:
{
- DexType type = (DexType) getConstantItem(view.getNextConstantOperand());
+ DexType type = getNextDexTypeOperand(view);
List<EV> operands = new ArrayList<>();
while (view.hasMoreOperands()) {
operands.add(getNextValueOperand(view));
@@ -651,7 +932,7 @@
}
case LirOpcodes.MOVEEXCEPTION:
{
- DexType type = (DexType) getConstantItem(view.getNextConstantOperand());
+ DexType type = getNextDexTypeOperand(view);
onMoveException(type);
return;
}
diff --git a/src/main/java/com/android/tools/r8/lightir/LirPrinter.java b/src/main/java/com/android/tools/r8/lightir/LirPrinter.java
index 19d5350..132fc61 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirPrinter.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirPrinter.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.code.IfType;
+import com.android.tools.r8.ir.code.MemberType;
import com.android.tools.r8.ir.code.NumericType;
import com.android.tools.r8.utils.StringUtils;
import java.util.Arrays;
@@ -129,18 +130,47 @@
}
@Override
+ public void onConstFloat(int value) {
+ appendOutValue().append(Float.intBitsToFloat(value));
+ }
+
+ @Override
+ public void onConstLong(long value) {
+ appendOutValue().append(value);
+ }
+
+ @Override
+ public void onConstDouble(long value) {
+ appendOutValue().append(Double.longBitsToDouble(value));
+ }
+
+ @Override
public void onConstString(DexString string) {
appendOutValue().append("str(").append(string).append(")");
}
@Override
+ public void onAdd(NumericType type, EV leftValueIndex, EV rightValueIndex) {
+ appendOutValue();
+ appendValueArguments(leftValueIndex, rightValueIndex);
+ }
+
+ @Override
+ public void onSub(NumericType type, EV leftValueIndex, EV rightValueIndex) {
+ appendOutValue();
+ appendValueArguments(leftValueIndex, rightValueIndex);
+ }
+
+ @Override
public void onDiv(NumericType type, EV leftValueIndex, EV rightValueIndex) {
- appendOutValue()
- .append(fmtValueIndex(leftValueIndex))
- .append(' ')
- .append(fmtValueIndex(rightValueIndex))
- .append(' ')
- .append(type);
+ appendOutValue();
+ appendValueArguments(leftValueIndex, rightValueIndex);
+ }
+
+ @Override
+ public void onXor(NumericType type, EV leftValueIndex, EV rightValueIndex) {
+ appendOutValue();
+ appendValueArguments(leftValueIndex, rightValueIndex);
}
@Override
@@ -151,7 +181,14 @@
@Override
public void onIf(IfType ifKind, int blockIndex, EV valueIndex) {
- builder.append(fmtValueIndex(valueIndex)).append(' ').append(fmtInsnIndex(blockIndex));
+ appendValueArguments(valueIndex);
+ builder.append(fmtInsnIndex(blockIndex));
+ }
+
+ @Override
+ public void onIfCmp(IfType ifKind, int blockIndex, EV leftValueIndex, EV rightValueIndex) {
+ appendValueArguments(leftValueIndex, rightValueIndex);
+ builder.append(fmtInsnIndex(blockIndex));
}
@Override
@@ -175,6 +212,12 @@
}
@Override
+ public void onNewInstance(DexType clazz) {
+ appendOutValue();
+ builder.append(clazz);
+ }
+
+ @Override
public void onInvokeMethodInstruction(DexMethod method, List<EV> arguments) {
if (!method.getReturnType().isVoidType()) {
appendOutValue();
@@ -225,11 +268,43 @@
}
@Override
+ public void onReturn(EV value) {
+ appendValueArguments(value);
+ }
+
+ @Override
public void onArrayLength(EV arrayValueIndex) {
appendOutValue().append(fmtValueIndex(arrayValueIndex));
}
@Override
+ public void onCheckCast(DexType type, EV value) {
+ appendOutValue();
+ appendValueArguments(value);
+ builder.append(type);
+ }
+
+ @Override
+ public void onArrayGetPrimitive(MemberType type, EV array, EV index) {
+ appendOutValue();
+ appendValueArguments(array, index);
+ builder.append(type);
+ }
+
+ @Override
+ public void onArrayGetObject(DexType type, EV array, EV index) {
+ appendOutValue();
+ appendValueArguments(array, index);
+ builder.append(type);
+ }
+
+ @Override
+ public void onArrayPut(MemberType type, EV array, EV index, EV value) {
+ appendValueArguments(array, index, value);
+ builder.append(type);
+ }
+
+ @Override
public void onDebugPosition() {
// Nothing to append.
}
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java b/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
index 7b17f6d..d5fd889 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
@@ -222,7 +222,7 @@
private final Map<Signature, Signature> signatureMap = new ConcurrentHashMap<>();
private final LinkedHashSet<MapVersionMappingInformation> mapVersions;
private final Map<String, String> originalSourceFiles;
- private final List<String> preamble;
+ private List<String> preamble;
private ClassNameMapper(
ImmutableMap<String, ClassNamingForNameMapper> classNameMappings,
@@ -243,6 +243,10 @@
return preamble;
}
+ public void setPreamble(List<String> preamble) {
+ this.preamble = preamble;
+ }
+
private Signature canonicalizeSignature(Signature signature) {
Signature result = signatureMap.get(signature);
if (result != null) {
diff --git a/src/main/java/com/android/tools/r8/naming/MapConsumer.java b/src/main/java/com/android/tools/r8/naming/MapConsumer.java
new file mode 100644
index 0000000..236c2b7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/MapConsumer.java
@@ -0,0 +1,20 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.naming;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.Finishable;
+
+/**
+ * This is an internal consumer that can accept our internal representation of a mapping format.
+ * This should not be exposed.
+ */
+public interface MapConsumer extends Finishable {
+
+ void accept(
+ DiagnosticsHandler diagnosticsHandler,
+ ProguardMapMarkerInfo makerInfo,
+ ClassNameMapper classNameMapper);
+}
diff --git a/src/main/java/com/android/tools/r8/naming/MultiProguardMapConsumer.java b/src/main/java/com/android/tools/r8/naming/MultiProguardMapConsumer.java
deleted file mode 100644
index 5901fd3..0000000
--- a/src/main/java/com/android/tools/r8/naming/MultiProguardMapConsumer.java
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.naming;
-
-import com.android.tools.r8.DiagnosticsHandler;
-import com.android.tools.r8.ProguardMapConsumer;
-import java.util.ArrayList;
-import java.util.List;
-
-public class MultiProguardMapConsumer extends ProguardMapConsumer {
-
- private final List<ProguardMapConsumer> proguardMapConsumers;
-
- public MultiProguardMapConsumer(List<ProguardMapConsumer> proguardMapConsumers) {
- this.proguardMapConsumers = proguardMapConsumers;
- }
-
- @Override
- public void finished(DiagnosticsHandler handler) {
- proguardMapConsumers.forEach(consumer -> consumer.finished(handler));
- }
-
- @Override
- public void accept(ProguardMapMarkerInfo markerInfo, ClassNameMapper classNameMapper) {
- proguardMapConsumers.forEach(consumer -> consumer.accept(markerInfo, classNameMapper));
- }
-
- public static Builder builder() {
- return new Builder();
- }
-
- public static class Builder {
-
- private final List<ProguardMapConsumer> proguardMapConsumers = new ArrayList<>();
-
- public Builder addProguardMapConsumer(ProguardMapConsumer consumer) {
- proguardMapConsumers.add(consumer);
- return this;
- }
-
- public MultiProguardMapConsumer build() {
- return new MultiProguardMapConsumer(proguardMapConsumers);
- }
- }
-}
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapPartitionConsumer.java b/src/main/java/com/android/tools/r8/naming/ProguardMapPartitionConsumer.java
deleted file mode 100644
index 451fd6c..0000000
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapPartitionConsumer.java
+++ /dev/null
@@ -1,96 +0,0 @@
-// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.naming;
-
-import com.android.tools.r8.DiagnosticsHandler;
-import com.android.tools.r8.ProguardMapConsumer;
-import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.retrace.MappingPartition;
-import com.android.tools.r8.retrace.MappingPartitionMetadata;
-import com.android.tools.r8.retrace.ProguardMapPartitioner;
-import com.android.tools.r8.retrace.internal.ProguardMapProducerInternal;
-import java.io.IOException;
-import java.util.function.Consumer;
-
-public class ProguardMapPartitionConsumer extends ProguardMapConsumer {
-
- private final Consumer<MappingPartition> mappingPartitionConsumer;
- private final Consumer<MappingPartitionMetadata> metadataConsumer;
- private final Runnable finishedConsumer;
- private final DiagnosticsHandler diagnosticsHandler;
-
- private ProguardMapPartitionConsumer(
- Consumer<MappingPartition> mappingPartitionConsumer,
- Consumer<MappingPartitionMetadata> metadataConsumer,
- Runnable finishedConsumer,
- DiagnosticsHandler diagnosticsHandler) {
- this.mappingPartitionConsumer = mappingPartitionConsumer;
- this.metadataConsumer = metadataConsumer;
- this.finishedConsumer = finishedConsumer;
- this.diagnosticsHandler = diagnosticsHandler;
- }
-
- @Override
- public void accept(ProguardMapMarkerInfo makerInfo, ClassNameMapper classNameMapper) {
- try {
- // TODO(b/274735214): Ensure we get markerInfo consumed as well.
- MappingPartitionMetadata run =
- ProguardMapPartitioner.builder(diagnosticsHandler)
- .setProguardMapProducer(new ProguardMapProducerInternal(classNameMapper))
- .setPartitionConsumer(mappingPartitionConsumer)
- // Setting these do not actually do anything currently since there is no parsing.
- .setAllowEmptyMappedRanges(false)
- .setAllowExperimentalMapping(false)
- .build()
- .run();
- metadataConsumer.accept(run);
- } catch (IOException exception) {
- throw new Unreachable("IOExceptions should only occur when parsing");
- }
- }
-
- @Override
- public void finished(DiagnosticsHandler handler) {
- finishedConsumer.run();
- }
-
- public static Builder builder() {
- return new Builder();
- }
-
- public static class Builder {
-
- private Consumer<MappingPartition> mappingPartitionConsumer;
- private Consumer<MappingPartitionMetadata> metadataConsumer;
- private Runnable finishedConsumer;
- private DiagnosticsHandler diagnosticsHandler;
-
- public Builder setMappingPartitionConsumer(
- Consumer<MappingPartition> mappingPartitionConsumer) {
- this.mappingPartitionConsumer = mappingPartitionConsumer;
- return this;
- }
-
- public Builder setMetadataConsumer(Consumer<MappingPartitionMetadata> metadataConsumer) {
- this.metadataConsumer = metadataConsumer;
- return this;
- }
-
- public Builder setFinishedConsumer(Runnable finishedConsumer) {
- this.finishedConsumer = finishedConsumer;
- return this;
- }
-
- public Builder setDiagnosticsHandler(DiagnosticsHandler diagnosticsHandler) {
- this.diagnosticsHandler = diagnosticsHandler;
- return this;
- }
-
- public ProguardMapPartitionConsumer build() {
- return new ProguardMapPartitionConsumer(
- mappingPartitionConsumer, metadataConsumer, finishedConsumer, diagnosticsHandler);
- }
- }
-}
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapStringConsumer.java b/src/main/java/com/android/tools/r8/naming/ProguardMapStringConsumer.java
index 95bbd38..be3d3ba 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapStringConsumer.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapStringConsumer.java
@@ -5,36 +5,36 @@
package com.android.tools.r8.naming;
import com.android.tools.r8.DiagnosticsHandler;
-import com.android.tools.r8.ProguardMapConsumer;
import com.android.tools.r8.StringConsumer;
import com.android.tools.r8.utils.ChainableStringConsumer;
/***
- * Default implementation of a ProguardMapConsumer that wraps around a string consumer for streamed
- * string output.
+ * Default implementation of a MapConsumer that wraps around a string consumer for streamed string
+ * output.
*/
-public class ProguardMapStringConsumer extends ProguardMapConsumer
- implements ChainableStringConsumer {
+public class ProguardMapStringConsumer implements MapConsumer, ChainableStringConsumer {
private final StringConsumer stringConsumer;
- private final DiagnosticsHandler diagnosticsHandler;
+ private DiagnosticsHandler diagnosticsHandler;
- private ProguardMapStringConsumer(
- StringConsumer stringConsumer, DiagnosticsHandler diagnosticsHandler) {
+ private ProguardMapStringConsumer(StringConsumer stringConsumer) {
assert stringConsumer != null;
- assert diagnosticsHandler != null;
this.stringConsumer = stringConsumer;
- this.diagnosticsHandler = diagnosticsHandler;
}
@Override
- public void accept(ProguardMapMarkerInfo markerInfo, ClassNameMapper classNameMapper) {
+ public void accept(
+ DiagnosticsHandler diagnosticsHandler,
+ ProguardMapMarkerInfo markerInfo,
+ ClassNameMapper classNameMapper) {
+ this.diagnosticsHandler = diagnosticsHandler;
accept(markerInfo.serializeToString());
classNameMapper.write(this);
}
@Override
public ChainableStringConsumer accept(String string) {
+ assert diagnosticsHandler != null;
stringConsumer.accept(string, diagnosticsHandler);
return this;
}
@@ -55,20 +55,14 @@
public static class Builder {
private StringConsumer stringConsumer;
- private DiagnosticsHandler diagnosticsHandler;
public Builder setStringConsumer(StringConsumer stringConsumer) {
this.stringConsumer = stringConsumer;
return this;
}
- public Builder setDiagnosticsHandler(DiagnosticsHandler diagnosticsHandler) {
- this.diagnosticsHandler = diagnosticsHandler;
- return this;
- }
-
public ProguardMapStringConsumer build() {
- return new ProguardMapStringConsumer(stringConsumer, diagnosticsHandler);
+ return new ProguardMapStringConsumer(stringConsumer);
}
}
}
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 1d92c11..0637406 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java
@@ -5,7 +5,6 @@
import com.android.tools.r8.MapIdEnvironment;
import com.android.tools.r8.MapIdProvider;
-import com.android.tools.r8.ProguardMapConsumer;
import com.android.tools.r8.dex.Marker.Tool;
import com.android.tools.r8.utils.ChainableStringConsumer;
import com.android.tools.r8.utils.ExceptionUtils;
@@ -44,7 +43,7 @@
private final ClassNameMapper classNameMapper;
private final InternalOptions options;
- private final ProguardMapConsumer consumer;
+ private final MapConsumer consumer;
private final Reporter reporter;
private final Tool compiler;
@@ -53,7 +52,7 @@
this.classNameMapper = classNameMapper.sorted();
// TODO(b/217111432): Validate Proguard using ProguardMapChecker without building the entire
// Proguard map in memory.
- this.consumer = options.proguardMapConsumer;
+ this.consumer = options.mapConsumer;
this.options = options;
this.reporter = options.reporter;
this.compiler = tool;
@@ -68,6 +67,7 @@
public ProguardMapId writeProguardMap() {
ProguardMapId proguardMapId = computeProguardMapId();
consumer.accept(
+ reporter,
ProguardMapMarkerInfo.builder()
.setCompilerName(compiler.name())
.setProguardMapId(proguardMapId)
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorApplicationFixer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorApplicationFixer.java
index eff4f92..1f5b5ff 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorApplicationFixer.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorApplicationFixer.java
@@ -18,7 +18,7 @@
import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.MethodCollection;
import com.android.tools.r8.graph.PrunedItems;
-import com.android.tools.r8.graph.TreeFixerBase;
+import com.android.tools.r8.graph.fixup.TreeFixerBase;
import com.android.tools.r8.graph.lens.GraphLens;
import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfoFixer;
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfileCollection.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfileCollection.java
index bc1dfda..6bb861a 100644
--- a/src/main/java/com/android/tools/r8/profile/art/ArtProfileCollection.java
+++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfileCollection.java
@@ -22,18 +22,15 @@
public static ArtProfileCollection createInitialArtProfileCollection(
AppInfo appInfo, InternalOptions options) {
ArtProfileOptions artProfileOptions = options.getArtProfileOptions();
- Collection<ArtProfileForRewriting> artProfilesForRewriting =
- artProfileOptions.getArtProfilesForRewriting();
+ Collection<ArtProfileProvider> artProfileProviders = artProfileOptions.getArtProfileProviders();
List<ArtProfile> artProfiles =
new ArrayList<>(
- artProfilesForRewriting.size()
+ artProfileProviders.size()
+ BooleanUtils.intValue(artProfileOptions.isCompletenessCheckForTestingEnabled()));
- for (ArtProfileForRewriting artProfileForRewriting :
- options.getArtProfileOptions().getArtProfilesForRewriting()) {
- ArtProfileProvider artProfileProvider = artProfileForRewriting.getArtProfileProvider();
+ for (ArtProfileProvider artProfileProvider : artProfileProviders) {
ArtProfile.Builder artProfileBuilder =
ArtProfile.builderForInitialArtProfile(artProfileProvider, options);
- artProfileForRewriting.getArtProfileProvider().getArtProfile(artProfileBuilder);
+ artProfileProvider.getArtProfile(artProfileBuilder);
artProfiles.add(artProfileBuilder.build());
}
if (artProfileOptions.isCompletenessCheckForTestingEnabled()) {
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfileOptions.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfileOptions.java
index d5f91da..4d60860 100644
--- a/src/main/java/com/android/tools/r8/profile/art/ArtProfileOptions.java
+++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfileOptions.java
@@ -7,6 +7,7 @@
import static com.android.tools.r8.utils.SystemPropertyUtils.parseSystemPropertyOrDefault;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.ListUtils;
import java.util.Collection;
import java.util.Collections;
@@ -18,6 +19,8 @@
private Collection<ArtProfileForRewriting> artProfilesForRewriting = Collections.emptyList();
private boolean enableCompletenessCheckForTesting =
parseSystemPropertyOrDefault(COMPLETENESS_PROPERTY_KEY, false);
+ private boolean hasReadArtProfileProviders = false;
+ private boolean allowReadingEmptyArtProfileProvidersMultipleTimesForTesting = false;
private final InternalOptions options;
@@ -29,6 +32,18 @@
return artProfilesForRewriting;
}
+ public Collection<ArtProfileProvider> getArtProfileProviders() {
+ assert !hasReadArtProfileProviders
+ || (allowReadingEmptyArtProfileProvidersMultipleTimesForTesting
+ && artProfilesForRewriting.isEmpty());
+ hasReadArtProfileProviders = true;
+ return ListUtils.map(artProfilesForRewriting, ArtProfileForRewriting::getArtProfileProvider);
+ }
+
+ public InternalOptions getOptions() {
+ return options;
+ }
+
public boolean isCompletenessCheckForTestingEnabled() {
return enableCompletenessCheckForTesting
&& !options.isDesugaredLibraryCompilation()
@@ -73,6 +88,13 @@
return enableCompletenessCheckForTesting;
}
+ public ArtProfileOptions setAllowReadingEmptyArtProfileProvidersMultipleTimesForTesting(
+ boolean allowReadingEmptyArtProfileProvidersMultipleTimesForTesting) {
+ this.allowReadingEmptyArtProfileProvidersMultipleTimesForTesting =
+ allowReadingEmptyArtProfileProvidersMultipleTimesForTesting;
+ return this;
+ }
+
public ArtProfileOptions setArtProfilesForRewriting(Collection<ArtProfileForRewriting> inputs) {
this.artProfilesForRewriting = inputs;
return this;
diff --git a/src/main/java/com/android/tools/r8/profile/rewriting/ProfileRewritingCfInstructionDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/profile/rewriting/ProfileRewritingCfInstructionDesugaringEventConsumer.java
index a9d901a..94232e2 100644
--- a/src/main/java/com/android/tools/r8/profile/rewriting/ProfileRewritingCfInstructionDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/profile/rewriting/ProfileRewritingCfInstructionDesugaringEventConsumer.java
@@ -91,8 +91,9 @@
}
@Override
- public void acceptCompanionClassClinit(ProgramMethod method) {
- parent.acceptCompanionClassClinit(method);
+ public void acceptCompanionClassClinit(ProgramMethod method, ProgramMethod companionMethod) {
+ additionsCollection.addMethodAndHolderIfContextIsInProfile(companionMethod, method);
+ parent.acceptCompanionClassClinit(method, companionMethod);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/profile/rewriting/ProfileRewritingRootSetBuilderEventConsumer.java b/src/main/java/com/android/tools/r8/profile/rewriting/ProfileRewritingRootSetBuilderEventConsumer.java
index 2a10ce0..22691ca 100644
--- a/src/main/java/com/android/tools/r8/profile/rewriting/ProfileRewritingRootSetBuilderEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/profile/rewriting/ProfileRewritingRootSetBuilderEventConsumer.java
@@ -28,8 +28,9 @@
}
@Override
- public void acceptCompanionClassClinit(ProgramMethod method) {
- parent.acceptCompanionClassClinit(method);
+ public void acceptCompanionClassClinit(ProgramMethod method, ProgramMethod companionMethod) {
+ additionsCollection.addMethodAndHolderIfContextIsInProfile(companionMethod, method);
+ parent.acceptCompanionClassClinit(method, companionMethod);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/repackaging/Repackaging.java b/src/main/java/com/android/tools/r8/repackaging/Repackaging.java
index cffb1cf..ff4fc92 100644
--- a/src/main/java/com/android/tools/r8/repackaging/Repackaging.java
+++ b/src/main/java/com/android/tools/r8/repackaging/Repackaging.java
@@ -20,7 +20,7 @@
import com.android.tools.r8.graph.ProgramPackage;
import com.android.tools.r8.graph.ProgramPackageCollection;
import com.android.tools.r8.graph.SortedProgramPackageCollection;
-import com.android.tools.r8.graph.TreeFixerBase;
+import com.android.tools.r8.graph.fixup.TreeFixerBase;
import com.android.tools.r8.graph.lens.NestedGraphLens;
import com.android.tools.r8.naming.Minifier.MinificationPackageNamingStrategy;
import com.android.tools.r8.repackaging.RepackagingLens.Builder;
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 add5db3..598f71f 100644
--- a/src/main/java/com/android/tools/r8/retrace/MappingSupplier.java
+++ b/src/main/java/com/android/tools/r8/retrace/MappingSupplier.java
@@ -6,25 +6,11 @@
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 java.util.Set;
+import com.android.tools.r8.MappingSupplierInternal;
@Keep
-public abstract class MappingSupplier<T extends MappingSupplier<T>> {
+public interface MappingSupplier<T extends MappingSupplier<T>>
+ extends MappingSupplierBase<T>, MappingSupplierInternal {
- /***
- * Register an allowed mapping lookup to allow for prefetching of resources.
- *
- * @param classReference The minified class reference allowed to be lookup up.
- */
- public abstract T registerClassUse(
- DiagnosticsHandler diagnosticsHandler, ClassReference classReference);
-
- public abstract void verifyMappingFileHash(DiagnosticsHandler diagnosticsHandler);
-
- public abstract Set<MapVersionMappingInformation> getMapVersions(
- DiagnosticsHandler diagnosticsHandler);
-
- public abstract Retracer createRetracer(DiagnosticsHandler diagnosticsHandler);
+ Retracer createRetracer(DiagnosticsHandler diagnosticsHandler);
}
diff --git a/src/main/java/com/android/tools/r8/retrace/MappingSupplierAsync.java b/src/main/java/com/android/tools/r8/retrace/MappingSupplierAsync.java
new file mode 100644
index 0000000..9ffa613
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/MappingSupplierAsync.java
@@ -0,0 +1,14 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.retrace;
+
+import com.android.tools.r8.DiagnosticsHandler;
+
+public interface MappingSupplierAsync<T extends MappingSupplierAsync<T>>
+ extends MappingSupplierBase<T> {
+
+ Retracer createRetracer(
+ DiagnosticsHandler diagnosticsHandler, MappingPartitionFromKeySupplier supplier);
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/MappingSupplierBase.java b/src/main/java/com/android/tools/r8/retrace/MappingSupplierBase.java
new file mode 100644
index 0000000..accecec
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/MappingSupplierBase.java
@@ -0,0 +1,36 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.retrace;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.FieldReference;
+import com.android.tools.r8.references.MethodReference;
+
+@Keep
+public interface MappingSupplierBase<T extends MappingSupplierBase<T>> {
+
+ /***
+ * Register an allowed mapping lookup to allow for prefetching of resources.
+ *
+ * @param classReference The minified class reference allowed to be lookup up.
+ */
+ T registerClassUse(DiagnosticsHandler diagnosticsHandler, ClassReference classReference);
+
+ /***
+ * Register an allowed mapping lookup to allow for prefetching of resources.
+ *
+ * @param methodReference The minified method reference allowed to be lookup up.
+ */
+ T registerMethodUse(DiagnosticsHandler diagnosticsHandler, MethodReference methodReference);
+
+ /***
+ * Register an allowed mapping lookup to allow for prefetching of resources.
+ *
+ * @param fieldReference The minified field reference allowed to be lookup up.
+ */
+ T registerFieldUse(DiagnosticsHandler diagnosticsHandler, FieldReference fieldReference);
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/PartitionMappingSupplier.java b/src/main/java/com/android/tools/r8/retrace/PartitionMappingSupplier.java
index d56aee5..ebcfaf9 100644
--- a/src/main/java/com/android/tools/r8/retrace/PartitionMappingSupplier.java
+++ b/src/main/java/com/android/tools/r8/retrace/PartitionMappingSupplier.java
@@ -4,63 +4,170 @@
package com.android.tools.r8.retrace;
+import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.Keep;
import com.android.tools.r8.naming.MapVersion;
-import com.android.tools.r8.retrace.internal.PartitionMappingSupplierBuilderImpl;
+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.retrace.internal.PartitionMappingSupplierBase;
@Keep
-public abstract class PartitionMappingSupplier extends MappingSupplier<PartitionMappingSupplier> {
+public class PartitionMappingSupplier extends PartitionMappingSupplierBase<PartitionMappingSupplier>
+ implements MappingSupplier<PartitionMappingSupplier> {
+
+ private final MappingPartitionFromKeySupplier partitionSupplier;
+
+ private PartitionMappingSupplier(
+ RegisterMappingPartitionCallback registerCallback,
+ PrepareMappingPartitionsCallback prepareCallback,
+ MappingPartitionFromKeySupplier partitionSupplier,
+ boolean allowExperimental,
+ byte[] metadata,
+ MapVersion fallbackMapVersion) {
+ super(registerCallback, prepareCallback, allowExperimental, metadata, fallbackMapVersion);
+ this.partitionSupplier = partitionSupplier;
+ }
+
+ /***
+ * Register an allowed mapping lookup to allow for prefetching of resources.
+ *
+ * @param classReference The minified class reference allowed to be lookup up.
+ */
+ @Keep
+ @Override
+ public PartitionMappingSupplier registerClassUse(
+ DiagnosticsHandler diagnosticsHandler, ClassReference classReference) {
+ return super.registerClassUse(diagnosticsHandler, classReference);
+ }
+
+ /***
+ * Register an allowed mapping lookup to allow for prefetching of resources.
+ *
+ * @param methodReference The minified method reference allowed to be lookup up.
+ */
+ @Keep
+ @Override
+ public PartitionMappingSupplier registerMethodUse(
+ DiagnosticsHandler diagnosticsHandler, MethodReference methodReference) {
+ return super.registerMethodUse(diagnosticsHandler, methodReference);
+ }
+
+ /***
+ * Register an allowed mapping lookup to allow for prefetching of resources.
+ *
+ * @param fieldReference The minified field reference allowed to be lookup up.
+ */
+ @Keep
+ @Override
+ public PartitionMappingSupplier registerFieldUse(
+ DiagnosticsHandler diagnosticsHandler, FieldReference fieldReference) {
+ return super.registerFieldUse(diagnosticsHandler, fieldReference);
+ }
+
+ @Override
+ public Retracer createRetracer(DiagnosticsHandler diagnosticsHandler) {
+ return createRetracerFromPartitionSupplier(diagnosticsHandler, partitionSupplier);
+ }
+
+ @Override
+ public PartitionMappingSupplier self() {
+ return this;
+ }
public static Builder builder() {
- return new PartitionMappingSupplierBuilderImpl(MapVersion.MAP_VERSION_NONE);
+ return new Builder();
}
- public static NoMetadataBuilder<?> noMetadataBuilder(MapVersion mapVersion) {
- return new PartitionMappingSupplierBuilderImpl(mapVersion);
+ public static NoMetadataBuilder noMetadataBuilder(MapVersion mapVersion) {
+ return new NoMetadataBuilder(mapVersion);
}
@Keep
- public abstract static class NoMetadataBuilder<B extends NoMetadataBuilder<B>>
- extends MappingSupplierBuilder<PartitionMappingSupplier, B> {
+ public abstract static class NoMetadataBuilderBase<B extends NoMetadataBuilderBase<B>>
+ extends PartitionMappingSupplierBuilderBase<B> {
+
+ protected MappingPartitionFromKeySupplier partitionSupplier;
+
+ private NoMetadataBuilderBase(MapVersion fallbackMapVersion) {
+ super(fallbackMapVersion);
+ }
/***
- * Callback to be notified of a partition that is later going to be needed. When all needed
- * partitions are found the callback specified to {@code setPrepareMappingPartitionsCallback} is
- * called.
+ * Sets the partition supplier.
*
- * @param registerPartitionCallback the consumer to get keys for partitions.
+ * @param partitionSupplier the supplier of partitions for requested keys.
*/
- public abstract Builder setRegisterMappingPartitionCallback(
- RegisterMappingPartitionCallback registerPartitionCallback);
-
- /***
- * A callback notifying that all partitions should be prepared. The prepare callback is
- * guaranteed to be called prior to any calls to the partition supplier.
- *
- * @param prepare the callback to listen for when partitions should be prepared.
- */
- public abstract Builder setPrepareMappingPartitionsCallback(
- PrepareMappingPartitionsCallback prepare);
-
- /***
- * Set the partition supplier that is needed for retracing. All partitions needed has been
- * declared earlier and this should block until the bytes associated with the partition is
- * ready.
- *
- * @param partitionSupplier the function to return a partition to retrace
- */
- public abstract Builder setMappingPartitionFromKeySupplier(
- MappingPartitionFromKeySupplier partitionSupplier);
+ public B setMappingPartitionFromKeySupplier(MappingPartitionFromKeySupplier partitionSupplier) {
+ this.partitionSupplier = partitionSupplier;
+ return self();
+ }
}
@Keep
- public abstract static class Builder extends NoMetadataBuilder<Builder> {
+ public static class NoMetadataBuilder extends NoMetadataBuilderBase<NoMetadataBuilder> {
+
+ private NoMetadataBuilder(MapVersion fallbackMapVersion) {
+ super(fallbackMapVersion);
+ }
+
+ @Override
+ protected NoMetadataBuilder self() {
+ return this;
+ }
+
+ public PartitionMappingSupplier build() {
+ if (partitionSupplier == null) {
+ throw new RuntimeException("Cannot build without providing a partition supplier.");
+ }
+ return new PartitionMappingSupplier(
+ registerCallback,
+ prepareCallback,
+ partitionSupplier,
+ allowExperimental,
+ null,
+ fallbackMapVersion);
+ }
+ }
+
+ @Keep
+ public static class Builder extends NoMetadataBuilderBase<Builder> {
+
+ private byte[] metadata;
+
+ private Builder() {
+ super(MapVersion.MAP_VERSION_NONE);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
/***
* Sets the serialized metadata that was obtained when partitioning.
*
* @param metadata the serialized metadata
*/
- public abstract Builder setMetadata(byte[] metadata);
+ public Builder setMetadata(byte[] metadata) {
+ this.metadata = metadata;
+ return self();
+ }
+
+ public PartitionMappingSupplier build() {
+ if (partitionSupplier == null) {
+ throw new RuntimeException("Cannot build without providing a partition supplier");
+ }
+ if (metadata == null) {
+ throw new RuntimeException("Cannot build without providing metadata.");
+ }
+ return new PartitionMappingSupplier(
+ registerCallback,
+ prepareCallback,
+ partitionSupplier,
+ allowExperimental,
+ metadata,
+ fallbackMapVersion);
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/retrace/PartitionMappingSupplierAsync.java b/src/main/java/com/android/tools/r8/retrace/PartitionMappingSupplierAsync.java
new file mode 100644
index 0000000..41acee5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/PartitionMappingSupplierAsync.java
@@ -0,0 +1,113 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.retrace;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.naming.MapVersion;
+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.retrace.internal.PartitionMappingSupplierBase;
+import com.android.tools.r8.retrace.internal.RetracerImpl;
+
+@Keep
+public class PartitionMappingSupplierAsync
+ extends PartitionMappingSupplierBase<PartitionMappingSupplierAsync>
+ implements MappingSupplierAsync<PartitionMappingSupplierAsync> {
+
+ private PartitionMappingSupplierAsync(
+ RegisterMappingPartitionCallback registerCallback,
+ PrepareMappingPartitionsCallback prepareCallback,
+ boolean allowExperimental,
+ byte[] metadata,
+ MapVersion fallbackMapVersion) {
+ super(registerCallback, prepareCallback, allowExperimental, metadata, fallbackMapVersion);
+ }
+
+ /***
+ * Register an allowed mapping lookup to allow for prefetching of resources.
+ *
+ * @param classReference The minified class reference allowed to be lookup up.
+ */
+ @Keep
+ @Override
+ public PartitionMappingSupplierAsync registerClassUse(
+ DiagnosticsHandler diagnosticsHandler, ClassReference classReference) {
+ return super.registerClassUse(diagnosticsHandler, classReference);
+ }
+
+ /***
+ * Register an allowed mapping lookup to allow for prefetching of resources.
+ *
+ * @param methodReference The minified method reference allowed to be lookup up.
+ */
+ @Keep
+ @Override
+ public PartitionMappingSupplierAsync registerMethodUse(
+ DiagnosticsHandler diagnosticsHandler, MethodReference methodReference) {
+ return super.registerMethodUse(diagnosticsHandler, methodReference);
+ }
+
+ /***
+ * Register an allowed mapping lookup to allow for prefetching of resources.
+ *
+ * @param fieldReference The minified field reference allowed to be lookup up.
+ */
+ @Keep
+ @Override
+ public PartitionMappingSupplierAsync registerFieldUse(
+ DiagnosticsHandler diagnosticsHandler, FieldReference fieldReference) {
+ return super.registerFieldUse(diagnosticsHandler, fieldReference);
+ }
+
+ @Override
+ public PartitionMappingSupplierAsync self() {
+ return this;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ public RetracerImpl createRetracer(
+ DiagnosticsHandler diagnosticsHandler, MappingPartitionFromKeySupplier supplier) {
+ return createRetracerFromPartitionSupplier(diagnosticsHandler, supplier);
+ }
+
+ @Keep
+ public static class Builder extends PartitionMappingSupplierBuilderBase<Builder> {
+
+ private byte[] metadata;
+
+ private Builder() {
+ super(MapVersion.MAP_VERSION_NONE);
+ }
+
+ /***
+ * Sets the serialized metadata that was obtained when partitioning.
+ *
+ * @param metadata the serialized metadata
+ */
+ public Builder setMetadata(byte[] metadata) {
+ this.metadata = metadata;
+ return self();
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+
+ public PartitionMappingSupplierAsync build() {
+ if (metadata == null) {
+ throw new RuntimeException("Cannot build without providing metadata.");
+ }
+ return new PartitionMappingSupplierAsync(
+ registerCallback, prepareCallback, allowExperimental, metadata, fallbackMapVersion);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/PartitionMappingSupplierBuilderBase.java b/src/main/java/com/android/tools/r8/retrace/PartitionMappingSupplierBuilderBase.java
new file mode 100644
index 0000000..f5b020b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/PartitionMappingSupplierBuilderBase.java
@@ -0,0 +1,41 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.retrace;
+
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.naming.MapVersion;
+
+@Keep
+public abstract class PartitionMappingSupplierBuilderBase<
+ T extends PartitionMappingSupplierBuilderBase<T>> {
+
+ protected RegisterMappingPartitionCallback registerCallback =
+ RegisterMappingPartitionCallback.empty();
+ protected PrepareMappingPartitionsCallback prepareCallback =
+ PrepareMappingPartitionsCallback.empty();
+ protected final MapVersion fallbackMapVersion;
+ protected boolean allowExperimental = false;
+
+ public PartitionMappingSupplierBuilderBase(MapVersion fallbackMapVersion) {
+ this.fallbackMapVersion = fallbackMapVersion;
+ }
+
+ public T setRegisterMappingPartitionCallback(RegisterMappingPartitionCallback registerCallback) {
+ this.registerCallback = registerCallback;
+ return self();
+ }
+
+ public T setPrepareMappingPartitionsCallback(PrepareMappingPartitionsCallback prepareCallback) {
+ this.prepareCallback = prepareCallback;
+ return self();
+ }
+
+ public T setAllowExperimental(boolean allowExperimental) {
+ this.allowExperimental = allowExperimental;
+ return self();
+ }
+
+ protected abstract T self();
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/PrepareMappingPartitionsCallback.java b/src/main/java/com/android/tools/r8/retrace/PrepareMappingPartitionsCallback.java
index b19babd..89c6161 100644
--- a/src/main/java/com/android/tools/r8/retrace/PrepareMappingPartitionsCallback.java
+++ b/src/main/java/com/android/tools/r8/retrace/PrepareMappingPartitionsCallback.java
@@ -15,5 +15,11 @@
@Keep
public interface PrepareMappingPartitionsCallback {
+ PrepareMappingPartitionsCallback EMPTY_INSTANCE = () -> {};
+
+ static PrepareMappingPartitionsCallback empty() {
+ return EMPTY_INSTANCE;
+ }
+
void prepare();
}
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 28b95a0..7b3e657 100644
--- a/src/main/java/com/android/tools/r8/retrace/ProguardMappingSupplier.java
+++ b/src/main/java/com/android/tools/r8/retrace/ProguardMappingSupplier.java
@@ -8,7 +8,7 @@
import com.android.tools.r8.retrace.internal.ProguardMappingSupplierBuilderImpl;
@Keep
-public abstract class ProguardMappingSupplier extends MappingSupplier<ProguardMappingSupplier> {
+public abstract class ProguardMappingSupplier implements MappingSupplier<ProguardMappingSupplier> {
public static Builder builder() {
return new ProguardMappingSupplierBuilderImpl();
diff --git a/src/main/java/com/android/tools/r8/retrace/RegisterMappingPartitionCallback.java b/src/main/java/com/android/tools/r8/retrace/RegisterMappingPartitionCallback.java
index df5c943..e411883 100644
--- a/src/main/java/com/android/tools/r8/retrace/RegisterMappingPartitionCallback.java
+++ b/src/main/java/com/android/tools/r8/retrace/RegisterMappingPartitionCallback.java
@@ -13,5 +13,11 @@
@Keep
public interface RegisterMappingPartitionCallback {
+ RegisterMappingPartitionCallback EMPTY_INSTANCE = key -> {};
+
+ static RegisterMappingPartitionCallback empty() {
+ return EMPTY_INSTANCE;
+ }
+
void register(String key);
}
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 a8cabe6..5cca6e8 100644
--- a/src/main/java/com/android/tools/r8/retrace/Retrace.java
+++ b/src/main/java/com/android/tools/r8/retrace/Retrace.java
@@ -10,22 +10,17 @@
import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.Keep;
import com.android.tools.r8.Version;
-import com.android.tools.r8.retrace.internal.ResultWithContextImpl;
import com.android.tools.r8.retrace.internal.RetraceAbortException;
+import com.android.tools.r8.retrace.internal.RetraceBase;
import com.android.tools.r8.retrace.internal.StackTraceElementStringProxy;
import com.android.tools.r8.retrace.internal.StackTraceRegularExpressionParser;
-import com.android.tools.r8.utils.Box;
import com.android.tools.r8.utils.ExceptionDiagnostic;
-import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.OptionsParsing;
import com.android.tools.r8.utils.OptionsParsing.ParseContext;
-import com.android.tools.r8.utils.Pair;
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.Timing;
import com.google.common.base.Charsets;
-import com.google.common.base.Equivalence;
-import com.google.common.base.Equivalence.Wrapper;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
@@ -35,16 +30,8 @@
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
-import java.util.Map;
import java.util.Scanner;
-import java.util.Set;
-import java.util.function.Function;
-import java.util.stream.Collectors;
/**
* A retrace tool for obfuscated stack traces.
@@ -53,7 +40,7 @@
* tool.
*/
@Keep
-public class Retrace<T, ST extends StackTraceElementProxy<T, ST>> {
+public class Retrace<T, ST extends StackTraceElementProxy<T, ST>> extends RetraceBase<T, ST> {
public static final String USAGE_MESSAGE =
StringUtils.lines(
@@ -129,7 +116,7 @@
return builder;
}
- private static MappingSupplier<?> getMappingSupplier(
+ private static ProguardMappingSupplier getMappingSupplier(
String mappingPath, DiagnosticsHandler diagnosticsHandler) {
Path path = Paths.get(mappingPath);
if (!Files.exists(path)) {
@@ -156,20 +143,17 @@
}
}
- private final StackTraceLineParser<T, ST> stackTraceLineParser;
private final MappingSupplier<?> mappingSupplier;
private final DiagnosticsHandler diagnosticsHandler;
- protected final boolean isVerbose;
Retrace(
StackTraceLineParser<T, ST> stackTraceLineParser,
MappingSupplier<?> mappingSupplier,
DiagnosticsHandler diagnosticsHandler,
boolean isVerbose) {
- this.stackTraceLineParser = stackTraceLineParser;
+ super(stackTraceLineParser, mappingSupplier, diagnosticsHandler, isVerbose);
this.mappingSupplier = mappingSupplier;
this.diagnosticsHandler = diagnosticsHandler;
- this.isVerbose = isVerbose;
}
/**
@@ -179,19 +163,9 @@
* @param context The context to retrace the stack trace in
* @return list of potentially ambiguous stack traces.
*/
- public ResultWithContext<List<List<T>>> retraceStackTrace(
+ public RetraceStackTraceResult<T> retraceStackTrace(
List<T> stackTrace, RetraceStackTraceContext context) {
- ListUtils.forEachWithIndex(
- stackTrace,
- (line, lineNumber) -> {
- if (line == null) {
- diagnosticsHandler.error(
- RetraceInvalidStackTraceLineDiagnostics.createNull(lineNumber));
- throw new RetraceAbortException();
- }
- });
- List<ST> parsed = ListUtils.map(stackTrace, stackTraceLineParser::parse);
- return retraceStackTraceParsed(parsed, context);
+ return retraceStackTraceParsed(parse(stackTrace), context);
}
/**
@@ -201,53 +175,11 @@
* @param context The context to retrace the stack trace in
* @return list of potentially ambiguous stack traces.
*/
- public ResultWithContext<List<List<T>>> retraceStackTraceParsed(
+ public RetraceStackTraceResult<T> retraceStackTraceParsed(
List<ST> stackTrace, RetraceStackTraceContext context) {
- RetraceStackTraceElementProxyEquivalence<T, ST> equivalence =
- new RetraceStackTraceElementProxyEquivalence<>(isVerbose);
- stackTrace.forEach(proxy -> proxy.registerUses(mappingSupplier, diagnosticsHandler));
- StackTraceElementProxyRetracer<T, ST> proxyRetracer =
- StackTraceElementProxyRetracer.createDefault(
- mappingSupplier.createRetracer(diagnosticsHandler));
- List<List<List<T>>> finalResult = new ArrayList<>();
- RetraceStackTraceContext finalContext =
- ListUtils.fold(
- stackTrace,
- context,
- (newContext, stackTraceLine) -> {
- List<Pair<RetraceStackTraceElementProxy<T, ST>, List<T>>> resultsForLine =
- new ArrayList<>();
- Box<List<T>> currentList = new Box<>();
- Set<Wrapper<RetraceStackTraceElementProxy<T, ST>>> seen = new HashSet<>();
- List<RetraceStackTraceContext> contexts = new ArrayList<>();
- RetraceStackTraceElementProxyResult<T, ST> retraceResult =
- proxyRetracer.retrace(stackTraceLine, newContext);
- retraceResult.stream()
- .forEach(
- retracedElement -> {
- if (retracedElement.isTopFrame() || !retracedElement.hasRetracedClass()) {
- if (seen.add(equivalence.wrap(retracedElement))) {
- currentList.set(new ArrayList<>());
- resultsForLine.add(Pair.create(retracedElement, currentList.get()));
- contexts.add(retracedElement.getContext());
- } else {
- currentList.clear();
- }
- }
- if (currentList.isSet()) {
- currentList
- .get()
- .add(stackTraceLine.toRetracedItem(retracedElement, isVerbose));
- }
- });
- resultsForLine.sort(Comparator.comparing(Pair::getFirst));
- finalResult.add(ListUtils.map(resultsForLine, Pair::getSecond));
- if (contexts.isEmpty()) {
- return retraceResult.getResultContext();
- }
- return contexts.size() == 1 ? contexts.get(0) : RetraceStackTraceContext.empty();
- });
- return ResultWithContextImpl.create(finalResult, finalContext);
+ registerUses(stackTrace);
+ return retraceStackTraceParsedWithRetracer(
+ mappingSupplier.createRetracer(diagnosticsHandler), stackTrace, context);
}
/**
@@ -255,34 +187,14 @@
*
* @param stackTraceFrame The frame to retrace that can give rise to ambiguous results
* @param context The context to retrace the stack trace in
- * @return A collection of retraced frame where each entry in the outer list is ambiguous
+ * @return A collection of potentially ambiguous retraced frames
*/
- public ResultWithContext<List<T>> retraceFrame(
+ public RetraceStackFrameAmbiguousResultWithContext<T> retraceFrame(
T stackTraceFrame, RetraceStackTraceContext context) {
- Map<RetraceStackTraceElementProxy<T, ST>, List<T>> ambiguousBlocks = new HashMap<>();
- List<RetraceStackTraceElementProxy<T, ST>> ambiguousKeys = new ArrayList<>();
- ST parsedLine = stackTraceLineParser.parse(stackTraceFrame);
- parsedLine.registerUses(mappingSupplier, diagnosticsHandler);
- StackTraceElementProxyRetracer<T, ST> proxyRetracer =
- StackTraceElementProxyRetracer.createDefault(
- mappingSupplier.createRetracer(diagnosticsHandler));
- Box<RetraceStackTraceContext> contextBox = new Box<>(context);
- proxyRetracer.retrace(parsedLine, context).stream()
- .forEach(
- retracedElement -> {
- if (retracedElement.isTopFrame() || !retracedElement.hasRetracedClass()) {
- ambiguousKeys.add(retracedElement);
- ambiguousBlocks.put(retracedElement, new ArrayList<>());
- }
- ambiguousBlocks
- .get(ListUtils.last(ambiguousKeys))
- .add(parsedLine.toRetracedItem(retracedElement, isVerbose));
- contextBox.set(retracedElement.getContext());
- });
- Collections.sort(ambiguousKeys);
- List<List<T>> retracedList = new ArrayList<>();
- ambiguousKeys.forEach(key -> retracedList.add(ambiguousBlocks.get(key)));
- return ResultWithContextImpl.create(retracedList, contextBox.get());
+ ST parsedFrame = parse(stackTraceFrame);
+ registerUses(parsedFrame);
+ return retraceFrameWithRetracer(
+ mappingSupplier.createRetracer(diagnosticsHandler), parsedFrame, context);
}
/**
@@ -293,22 +205,12 @@
* @param context The context to retrace the stack trace in
* @return the retraced stack trace line
*/
- public ResultWithContext<T> retraceLine(T stackTraceLine, RetraceStackTraceContext context) {
- ST parsedLine = stackTraceLineParser.parse(stackTraceLine);
- parsedLine.registerUses(mappingSupplier, diagnosticsHandler);
- StackTraceElementProxyRetracer<T, ST> proxyRetracer =
- StackTraceElementProxyRetracer.createDefault(
- mappingSupplier.createRetracer(diagnosticsHandler));
- Box<RetraceStackTraceContext> contextBox = new Box<>(context);
- List<T> result =
- proxyRetracer.retrace(parsedLine, context).stream()
- .map(
- retraceFrame -> {
- contextBox.set(retraceFrame.getContext());
- return parsedLine.toRetracedItem(retraceFrame, isVerbose);
- })
- .collect(Collectors.toList());
- return ResultWithContextImpl.create(result, contextBox.get());
+ public RetraceStackFrameResultWithContext<T> retraceLine(
+ T stackTraceLine, RetraceStackTraceContext context) {
+ ST parsedFrame = parse(stackTraceLine);
+ registerUses(parsedFrame);
+ return retraceLineWithRetracer(
+ mappingSupplier.createRetracer(diagnosticsHandler), parsedFrame, context);
}
/**
@@ -351,12 +253,13 @@
stackTraceLineParser, mappingSupplier, diagnosticsHandler, options.isVerbose());
timing.end();
timing.begin("Retracing");
- ResultWithContext<String> result = stringRetracer.retraceParsed(parsedStackTrace, context);
+ RetraceStackFrameResultWithContext<String> result =
+ stringRetracer.retraceParsed(parsedStackTrace, context);
timing.end();
timing.begin("Report result");
context = result.getContext();
if (!result.isEmpty() || currentStackTrace.isEmpty()) {
- command.getRetracedStackTraceConsumer().accept(result.getLines());
+ command.getRetracedStackTraceConsumer().accept(result.getResult());
}
timing.end();
}
@@ -477,16 +380,13 @@
}
@Keep
- public static class Builder<T, ST extends StackTraceElementProxy<T, ST>> {
+ public static class Builder<T, ST extends StackTraceElementProxy<T, ST>>
+ extends RetraceBuilderBase<Builder<T, ST>, T, ST> {
- private StackTraceLineParser<T, ST> stackTraceLineParser;
private MappingSupplier<?> mappingSupplier;
- private DiagnosticsHandler diagnosticsHandler;
- protected boolean isVerbose;
- public Builder<T, ST> setStackTraceLineParser(
- StackTraceLineParser<T, ST> stackTraceLineParser) {
- this.stackTraceLineParser = stackTraceLineParser;
+ @Override
+ public Builder<T, ST> self() {
return this;
}
@@ -495,16 +395,6 @@
return this;
}
- public Builder<T, ST> setDiagnosticsHandler(DiagnosticsHandler diagnosticsHandler) {
- this.diagnosticsHandler = diagnosticsHandler;
- return this;
- }
-
- public Builder<T, ST> setVerbose(boolean isVerbose) {
- this.isVerbose = isVerbose;
- return this;
- }
-
public Retrace<T, ST> build() {
return new Retrace<>(stackTraceLineParser, mappingSupplier, diagnosticsHandler, isVerbose);
}
@@ -538,144 +428,4 @@
}
}
}
-
- private static class RetraceStackTraceElementProxyEquivalence<
- T, ST extends StackTraceElementProxy<T, ST>>
- extends Equivalence<RetraceStackTraceElementProxy<T, ST>> {
-
- private final boolean isVerbose;
-
- public RetraceStackTraceElementProxyEquivalence(boolean isVerbose) {
- this.isVerbose = isVerbose;
- }
-
- @Override
- protected boolean doEquivalent(
- RetraceStackTraceElementProxy<T, ST> one, RetraceStackTraceElementProxy<T, ST> other) {
- if (one == other) {
- return true;
- }
- if (testNotEqualProperty(
- one,
- other,
- RetraceStackTraceElementProxy::hasRetracedClass,
- r -> r.getRetracedClass().getTypeName())
- || testNotEqualProperty(
- one,
- other,
- RetraceStackTraceElementProxy::hasSourceFile,
- RetraceStackTraceElementProxy::getSourceFile)) {
- return false;
- }
- assert one.getOriginalItem() == other.getOriginalItem();
- if (isVerbose
- || (one.getOriginalItem().hasLineNumber() && one.getOriginalItem().getLineNumber() > 0)) {
- if (testNotEqualProperty(
- one,
- other,
- RetraceStackTraceElementProxy::hasLineNumber,
- RetraceStackTraceElementProxy::getLineNumber)) {
- return false;
- }
- }
- if (one.hasRetracedMethod() != other.hasRetracedMethod()) {
- return false;
- }
- if (one.hasRetracedMethod()) {
- RetracedMethodReference oneMethod = one.getRetracedMethod();
- RetracedMethodReference otherMethod = other.getRetracedMethod();
- if (oneMethod.isKnown() != otherMethod.isKnown()) {
- return false;
- }
- // In verbose mode we check the signature, otherwise we only check the name
- if (!oneMethod.getMethodName().equals(otherMethod.getMethodName())) {
- return false;
- }
- if (isVerbose
- && ((oneMethod.isKnown()
- && !oneMethod
- .asKnown()
- .getMethodReference()
- .toString()
- .equals(otherMethod.asKnown().getMethodReference().toString()))
- || (!oneMethod.isKnown()
- && !oneMethod.getMethodName().equals(otherMethod.getMethodName())))) {
- return false;
- }
- }
- if (one.hasRetracedField() != other.hasRetracedField()) {
- return false;
- }
- if (one.hasRetracedField()) {
- RetracedFieldReference oneField = one.getRetracedField();
- RetracedFieldReference otherField = other.getRetracedField();
- if (oneField.isKnown() != otherField.isKnown()) {
- return false;
- }
- if (!oneField.getFieldName().equals(otherField.getFieldName())) {
- return false;
- }
- if (isVerbose
- && ((oneField.isKnown()
- && !oneField
- .asKnown()
- .getFieldReference()
- .toString()
- .equals(otherField.asKnown().getFieldReference().toString()))
- || (oneField.isUnknown()
- && !oneField.getFieldName().equals(otherField.getFieldName())))) {
- return false;
- }
- }
- if (one.hasRetracedFieldOrReturnType() != other.hasRetracedFieldOrReturnType()) {
- return false;
- }
- if (one.hasRetracedFieldOrReturnType()) {
- RetracedTypeReference oneFieldOrReturn = one.getRetracedFieldOrReturnType();
- RetracedTypeReference otherFieldOrReturn = other.getRetracedFieldOrReturnType();
- if (!compareRetracedTypeReference(oneFieldOrReturn, otherFieldOrReturn)) {
- return false;
- }
- }
- if (one.hasRetracedMethodArguments() != other.hasRetracedMethodArguments()) {
- return false;
- }
- if (one.hasRetracedMethodArguments()) {
- List<RetracedTypeReference> oneMethodArguments = one.getRetracedMethodArguments();
- List<RetracedTypeReference> otherMethodArguments = other.getRetracedMethodArguments();
- if (oneMethodArguments.size() != otherMethodArguments.size()) {
- return false;
- }
- for (int i = 0; i < oneMethodArguments.size(); i++) {
- if (compareRetracedTypeReference(
- oneMethodArguments.get(i), otherMethodArguments.get(i))) {
- return false;
- }
- }
- }
- return true;
- }
-
- private boolean compareRetracedTypeReference(
- RetracedTypeReference one, RetracedTypeReference other) {
- return one.isVoid() == other.isVoid()
- && (one.isVoid() || one.getTypeName().equals(other.getTypeName()));
- }
-
- @Override
- protected int doHash(RetraceStackTraceElementProxy<T, ST> proxy) {
- return 0;
- }
-
- private <V extends Comparable<V>> boolean testNotEqualProperty(
- RetraceStackTraceElementProxy<T, ST> one,
- RetraceStackTraceElementProxy<T, ST> other,
- Function<RetraceStackTraceElementProxy<T, ST>, Boolean> predicate,
- Function<RetraceStackTraceElementProxy<T, ST>, V> getter) {
- return Comparator.comparing(predicate)
- .thenComparing(getter, Comparator.nullsFirst(V::compareTo))
- .compare(one, other)
- != 0;
- }
- }
}
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceAsync.java b/src/main/java/com/android/tools/r8/retrace/RetraceAsync.java
new file mode 100644
index 0000000..00e07bd
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceAsync.java
@@ -0,0 +1,121 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.retrace;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.retrace.internal.RetraceBase;
+import java.util.List;
+
+/** An async retrace tool for obfuscated stack traces. */
+@Keep
+public class RetraceAsync<T, ST extends StackTraceElementProxy<T, ST>> extends RetraceBase<T, ST> {
+
+ private final MappingSupplierAsync<?> mappingSupplier;
+ private final DiagnosticsHandler diagnosticsHandler;
+
+ RetraceAsync(
+ StackTraceLineParser<T, ST> stackTraceLineParser,
+ MappingSupplierAsync<?> mappingSupplier,
+ DiagnosticsHandler diagnosticsHandler,
+ boolean isVerbose) {
+ super(stackTraceLineParser, mappingSupplier, diagnosticsHandler, isVerbose);
+ this.mappingSupplier = mappingSupplier;
+ this.diagnosticsHandler = diagnosticsHandler;
+ }
+
+ public static <T, ST extends StackTraceElementProxy<T, ST>>
+ RetraceAsync.Builder<T, ST> builder() {
+ return new Builder<>();
+ }
+
+ /**
+ * Retraces a complete stack frame and returns a list of retraced stack traces.
+ *
+ * @param stackTrace the stack trace to be retrace
+ * @param context The context to retrace the stack trace in
+ * @return list of potentially ambiguous stack traces.
+ */
+ public RetraceAsyncResult<RetraceStackTraceResult<T>> retraceStackTrace(
+ List<T> stackTrace, RetraceStackTraceContext context) {
+ return retraceStackTraceParsed(parse(stackTrace), context);
+ }
+
+ /**
+ * Retraces a complete stack frame and returns a list of retraced stack traces.
+ *
+ * @param stackTrace the stack trace to be retrace
+ * @param context The context to retrace the stack trace in
+ * @return list of potentially ambiguous stack traces.
+ */
+ public RetraceAsyncResult<RetraceStackTraceResult<T>> retraceStackTraceParsed(
+ List<ST> stackTrace, RetraceStackTraceContext context) {
+ registerUses(stackTrace);
+ return partitionSupplier ->
+ retraceStackTraceParsedWithRetracer(
+ mappingSupplier.createRetracer(diagnosticsHandler, partitionSupplier),
+ stackTrace,
+ context);
+ }
+
+ /**
+ * Retraces a stack trace frame with support for splitting up ambiguous results.
+ *
+ * @param stackTraceFrame The frame to retrace that can give rise to ambiguous results
+ * @param context The context to retrace the stack trace in
+ * @return A collection of retraced frame where each entry in the outer list is ambiguous
+ */
+ public RetraceAsyncResult<RetraceStackFrameAmbiguousResultWithContext<T>> retraceFrame(
+ T stackTraceFrame, RetraceStackTraceContext context) {
+ ST parsedFrame = parse(stackTraceFrame);
+ registerUses(parsedFrame);
+ return partitionSupplier ->
+ retraceFrameWithRetracer(
+ mappingSupplier.createRetracer(diagnosticsHandler, partitionSupplier),
+ parsedFrame,
+ context);
+ }
+
+ /**
+ * Utility method for tracing a single line that also retraces ambiguous lines without being able
+ * to distinguish them. For retracing with ambiguous results separated, use {@link #retraceFrame}
+ *
+ * @param stackTraceLine the stack trace line to retrace
+ * @param context The context to retrace the stack trace in
+ * @return the retraced stack trace line
+ */
+ public RetraceAsyncResult<RetraceStackFrameResultWithContext<T>> retraceLine(
+ T stackTraceLine, RetraceStackTraceContext context) {
+ ST parsedFrame = parse(stackTraceLine);
+ registerUses(parsedFrame);
+ return partitionSupplier ->
+ retraceLineWithRetracer(
+ mappingSupplier.createRetracer(diagnosticsHandler, partitionSupplier),
+ parsedFrame,
+ context);
+ }
+
+ @Keep
+ public static class Builder<T, ST extends StackTraceElementProxy<T, ST>>
+ extends RetraceBuilderBase<Builder<T, ST>, T, ST> {
+
+ private MappingSupplierAsync<?> mappingSupplier;
+
+ @Override
+ public RetraceAsync.Builder<T, ST> self() {
+ return this;
+ }
+
+ public RetraceAsync.Builder<T, ST> setMappingSupplier(MappingSupplierAsync<?> mappingSupplier) {
+ this.mappingSupplier = mappingSupplier;
+ return self();
+ }
+
+ public RetraceAsync<T, ST> build() {
+ return new RetraceAsync<>(
+ stackTraceLineParser, mappingSupplier, diagnosticsHandler, isVerbose);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceAsyncResult.java b/src/main/java/com/android/tools/r8/retrace/RetraceAsyncResult.java
new file mode 100644
index 0000000..146c3df
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceAsyncResult.java
@@ -0,0 +1,13 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.retrace;
+
+import com.android.tools.r8.Keep;
+
+@Keep
+public interface RetraceAsyncResult<T> {
+
+ T getResult(MappingPartitionFromKeySupplier supplier);
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceBuilderBase.java b/src/main/java/com/android/tools/r8/retrace/RetraceBuilderBase.java
new file mode 100644
index 0000000..1deadfe
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceBuilderBase.java
@@ -0,0 +1,34 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.retrace;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.Keep;
+
+@Keep
+public abstract class RetraceBuilderBase<
+ B extends RetraceBuilderBase<B, T, ST>, T, ST extends StackTraceElementProxy<T, ST>> {
+
+ protected StackTraceLineParser<T, ST> stackTraceLineParser;
+ protected DiagnosticsHandler diagnosticsHandler;
+ protected boolean isVerbose;
+
+ public abstract B self();
+
+ public B setStackTraceLineParser(StackTraceLineParser<T, ST> stackTraceLineParser) {
+ this.stackTraceLineParser = stackTraceLineParser;
+ return self();
+ }
+
+ public B setDiagnosticsHandler(DiagnosticsHandler diagnosticsHandler) {
+ this.diagnosticsHandler = diagnosticsHandler;
+ return self();
+ }
+
+ public B setVerbose(boolean isVerbose) {
+ this.isVerbose = isVerbose;
+ return self();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceResultWithContext.java b/src/main/java/com/android/tools/r8/retrace/RetraceResultWithContext.java
new file mode 100644
index 0000000..f9dfcdb
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceResultWithContext.java
@@ -0,0 +1,13 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.retrace;
+
+import com.android.tools.r8.Keep;
+
+@Keep
+public interface RetraceResultWithContext {
+
+ RetraceStackTraceContext getContext();
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceStackFrameAmbiguousResult.java b/src/main/java/com/android/tools/r8/retrace/RetraceStackFrameAmbiguousResult.java
new file mode 100644
index 0000000..50e59c1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceStackFrameAmbiguousResult.java
@@ -0,0 +1,28 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.retrace;
+
+import com.android.tools.r8.Keep;
+import java.util.List;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+
+@Keep
+public interface RetraceStackFrameAmbiguousResult<T> {
+
+ boolean isAmbiguous();
+
+ List<RetraceStackFrameResult<T>> getAmbiguousResult();
+
+ void forEach(Consumer<RetraceStackFrameResult<T>> consumer);
+
+ void forEachWithIndex(BiConsumer<RetraceStackFrameResult<T>, Integer> consumer);
+
+ int size();
+
+ boolean isEmpty();
+
+ RetraceStackFrameResult<T> get(int i);
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceStackFrameAmbiguousResultWithContext.java b/src/main/java/com/android/tools/r8/retrace/RetraceStackFrameAmbiguousResultWithContext.java
new file mode 100644
index 0000000..f856cbd
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceStackFrameAmbiguousResultWithContext.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.retrace;
+
+import com.android.tools.r8.Keep;
+
+@Keep
+public interface RetraceStackFrameAmbiguousResultWithContext<T>
+ extends RetraceStackFrameAmbiguousResult<T>, RetraceResultWithContext {}
diff --git a/src/main/java/com/android/tools/r8/retrace/ResultWithContext.java b/src/main/java/com/android/tools/r8/retrace/RetraceStackFrameResult.java
similarity index 80%
rename from src/main/java/com/android/tools/r8/retrace/ResultWithContext.java
rename to src/main/java/com/android/tools/r8/retrace/RetraceStackFrameResult.java
index 4949aec..dcdbf0f 100644
--- a/src/main/java/com/android/tools/r8/retrace/ResultWithContext.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceStackFrameResult.java
@@ -9,13 +9,15 @@
import java.util.function.Consumer;
@Keep
-public interface ResultWithContext<T> {
+public interface RetraceStackFrameResult<T> {
- RetraceStackTraceContext getContext();
-
- List<T> getLines();
+ List<T> getResult();
void forEach(Consumer<T> consumer);
+ int size();
+
+ T get(int i);
+
boolean isEmpty();
}
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceStackFrameResultWithContext.java b/src/main/java/com/android/tools/r8/retrace/RetraceStackFrameResultWithContext.java
new file mode 100644
index 0000000..4a63db0
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceStackFrameResultWithContext.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.retrace;
+
+import com.android.tools.r8.Keep;
+
+@Keep
+public interface RetraceStackFrameResultWithContext<T>
+ extends RetraceStackFrameResult<T>, RetraceResultWithContext {}
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceStackTraceResult.java b/src/main/java/com/android/tools/r8/retrace/RetraceStackTraceResult.java
new file mode 100644
index 0000000..ab1e9eb
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceStackTraceResult.java
@@ -0,0 +1,19 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.retrace;
+
+import com.android.tools.r8.Keep;
+import java.util.List;
+import java.util.function.Consumer;
+
+@Keep
+public interface RetraceStackTraceResult<T> extends RetraceResultWithContext {
+
+ List<RetraceStackFrameAmbiguousResult<T>> getResult();
+
+ void forEach(Consumer<RetraceStackFrameAmbiguousResult<T>> consumer);
+
+ boolean isEmpty();
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/StackTraceElementProxy.java b/src/main/java/com/android/tools/r8/retrace/StackTraceElementProxy.java
index d18f748..08598e7 100644
--- a/src/main/java/com/android/tools/r8/retrace/StackTraceElementProxy.java
+++ b/src/main/java/com/android/tools/r8/retrace/StackTraceElementProxy.java
@@ -46,7 +46,7 @@
RetraceStackTraceElementProxy<T, ST> retracedProxy, boolean verbose);
public void registerUses(
- MappingSupplier<?> mappingSupplier, DiagnosticsHandler diagnosticsHandler) {
+ MappingSupplierBase<?> mappingSupplier, DiagnosticsHandler diagnosticsHandler) {
if (hasClassName()) {
mappingSupplier.registerClassUse(diagnosticsHandler, getClassReference());
}
@@ -62,7 +62,9 @@
}
private static void registerUseFromTypeReference(
- MappingSupplier<?> mappingSupplier, String typeName, DiagnosticsHandler diagnosticsHandler) {
+ MappingSupplierBase<?> mappingSupplier,
+ String typeName,
+ DiagnosticsHandler diagnosticsHandler) {
TypeReference typeReference = Reference.typeFromTypeName(typeName);
if (typeReference.isArray()) {
typeReference = typeReference.asArray().getBaseType();
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 2810d8b..2c16ea1 100644
--- a/src/main/java/com/android/tools/r8/retrace/StringRetrace.java
+++ b/src/main/java/com/android/tools/r8/retrace/StringRetrace.java
@@ -8,9 +8,8 @@
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.RetraceStackFrameResultWithContextImpl;
import com.android.tools.r8.retrace.internal.StackTraceElementStringProxy;
-import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.StringUtils;
import java.util.ArrayList;
import java.util.HashSet;
@@ -77,36 +76,26 @@
* @param context The context to retrace the stack trace in
* @return the retraced stack trace
*/
- public ResultWithContext<String> retrace(
+ public RetraceStackFrameResultWithContext<String> retrace(
List<String> stackTrace, RetraceStackTraceContext context) {
- ResultWithContext<List<List<String>>> listResultWithContext =
+ RetraceStackTraceResult<String> listRetraceStackTraceResult =
retraceStackTrace(stackTrace, context);
List<String> retracedStrings = new ArrayList<>();
- for (List<List<String>> newLines : listResultWithContext.getLines()) {
- ListUtils.forEachWithIndex(
- newLines,
- (inlineFrames, ambiguousIndex) -> {
- for (int i = 0; i < inlineFrames.size(); i++) {
- String stackTraceLine = inlineFrames.get(i);
- if (i == 0 && ambiguousIndex > 0) {
- // We are reporting an ambiguous frame. To support retracing tools that retrace line
- // by line we have to emit <OR> at the point of the first 'at ' if we can find it.
- int indexToInsertOr = stackTraceLine.indexOf("at ");
- if (indexToInsertOr < 0) {
- indexToInsertOr =
- Math.max(StringUtils.firstNonWhitespaceCharacter(stackTraceLine), 0);
- }
- retracedStrings.add(
- stackTraceLine.substring(0, indexToInsertOr)
- + "<OR> "
- + stackTraceLine.substring(indexToInsertOr));
- } else {
- retracedStrings.add(stackTraceLine);
- }
- }
- });
- }
- return ResultWithContextImpl.create(retracedStrings, listResultWithContext.getContext());
+ listRetraceStackTraceResult.forEach(
+ newLines ->
+ newLines.forEachWithIndex(
+ (inlineFrames, ambiguousIndex) -> {
+ for (int i = 0; i < inlineFrames.size(); i++) {
+ String stackTraceLine = inlineFrames.get(i);
+ if (i == 0 && ambiguousIndex > 0) {
+ insertOrIntoStackTraceLine(stackTraceLine, retracedStrings);
+ } else {
+ retracedStrings.add(stackTraceLine);
+ }
+ }
+ }));
+ return RetraceStackFrameResultWithContextImpl.create(
+ retracedStrings, listRetraceStackTraceResult.getContext());
}
/**
@@ -117,36 +106,40 @@
* @param context The context to retrace the stack trace in
* @return the retraced stack trace
*/
- public ResultWithContext<String> retraceParsed(
+ public RetraceStackFrameResultWithContext<String> retraceParsed(
List<StackTraceElementStringProxy> stackTrace, RetraceStackTraceContext context) {
- ResultWithContext<List<List<String>>> listResultWithContext =
+ RetraceStackTraceResult<String> listRetraceStackTraceResult =
retraceStackTraceParsed(stackTrace, context);
List<String> retracedStrings = new ArrayList<>();
- for (List<List<String>> newLines : listResultWithContext.getLines()) {
- ListUtils.forEachWithIndex(
- newLines,
- (inlineFrames, ambiguousIndex) -> {
- for (int i = 0; i < inlineFrames.size(); i++) {
- String stackTraceLine = inlineFrames.get(i);
- if (i == 0 && ambiguousIndex > 0) {
- // We are reporting an ambiguous frame. To support retracing tools that retrace line
- // by line we have to emit <OR> at the point of the first 'at ' if we can find it.
- int indexToInsertOr = stackTraceLine.indexOf("at ");
- if (indexToInsertOr < 0) {
- indexToInsertOr =
- Math.max(StringUtils.firstNonWhitespaceCharacter(stackTraceLine), 0);
- }
- retracedStrings.add(
- stackTraceLine.substring(0, indexToInsertOr)
- + "<OR> "
- + stackTraceLine.substring(indexToInsertOr));
- } else {
- retracedStrings.add(stackTraceLine);
- }
- }
- });
+ listRetraceStackTraceResult.forEach(
+ newLines ->
+ newLines.forEachWithIndex(
+ (inlineFrames, ambiguousIndex) -> {
+ for (int i = 0; i < inlineFrames.size(); i++) {
+ String stackTraceLine = inlineFrames.get(i);
+ if (i == 0 && ambiguousIndex > 0) {
+ insertOrIntoStackTraceLine(stackTraceLine, retracedStrings);
+ } else {
+ retracedStrings.add(stackTraceLine);
+ }
+ }
+ }));
+ return RetraceStackFrameResultWithContextImpl.create(
+ retracedStrings, listRetraceStackTraceResult.getContext());
+ }
+
+ private void insertOrIntoStackTraceLine(String stackTraceLine, List<String> retracedStrings) {
+ // We are reporting an ambiguous frame. To support retracing tools that
+ // retrace line by line we have to emit <OR> at the point of the first 'at '
+ // if we can find it.
+ int indexToInsertOr = stackTraceLine.indexOf("at ");
+ if (indexToInsertOr < 0) {
+ indexToInsertOr = Math.max(StringUtils.firstNonWhitespaceCharacter(stackTraceLine), 0);
}
- return ResultWithContextImpl.create(retracedStrings, listResultWithContext.getContext());
+ retracedStrings.add(
+ stackTraceLine.substring(0, indexToInsertOr)
+ + "<OR> "
+ + stackTraceLine.substring(indexToInsertOr));
}
/**
@@ -156,12 +149,14 @@
* @param context The context to retrace the stack trace in
* @return the retraced frames
*/
- public ResultWithContext<String> retrace(
+ public RetraceStackFrameResultWithContext<String> retrace(
String stackTraceLine, RetraceStackTraceContext context) {
- ResultWithContext<List<String>> listResultWithContext = retraceFrame(stackTraceLine, context);
+ RetraceStackFrameAmbiguousResultWithContext<String> listRetraceStackTraceResult =
+ retraceFrame(stackTraceLine, context);
List<String> result = new ArrayList<>();
- joinAmbiguousLines(listResultWithContext.getLines(), result::add);
- return ResultWithContextImpl.create(result, listResultWithContext.getContext());
+ joinAmbiguousLines(listRetraceStackTraceResult.getAmbiguousResult(), result::add);
+ return RetraceStackFrameResultWithContextImpl.create(
+ result, listRetraceStackTraceResult.getContext());
}
/**
@@ -175,22 +170,21 @@
RetraceStackTraceContext context = RetraceStackTraceContext.empty();
String retraceLine;
while ((retraceLine = lineSupplier.getNext()) != null) {
- ResultWithContext<String> result = retrace(retraceLine, context);
+ RetraceStackFrameResultWithContext<String> result = retrace(retraceLine, context);
context = result.getContext();
result.forEach(lineConsumer);
}
}
private void joinAmbiguousLines(
- List<List<String>> retracedResult, Consumer<String> joinedConsumer) {
+ List<RetraceStackFrameResult<String>> retracedResult, Consumer<String> joinedConsumer) {
if (retracedResult.isEmpty()) {
// The result is empty, likely it maps to compiler synthesized items.
return;
}
Set<String> reportedFrames = new HashSet<>();
- ListUtils.forEachWithIndex(
- retracedResult,
- (potentialResults, index) -> {
+ retracedResult.forEach(
+ potentialResults -> {
assert !potentialResults.isEmpty();
// Check if we already reported position.
if (reportedFrames.add(potentialResults.get(0))) {
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/MappingPartitionKeyStrategy.java b/src/main/java/com/android/tools/r8/retrace/internal/MappingPartitionKeyStrategy.java
index 6b050c6..b3012cf 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/MappingPartitionKeyStrategy.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/MappingPartitionKeyStrategy.java
@@ -9,7 +9,8 @@
OBFUSCATED_TYPE_NAME_AS_KEY(0),
OBFUSCATED_TYPE_NAME_AS_KEY_WITH_PARTITIONS(1);
- private static final MappingPartitionKeyStrategy DEFAULT_STRATEGY = OBFUSCATED_TYPE_NAME_AS_KEY;
+ private static final MappingPartitionKeyStrategy DEFAULT_STRATEGY =
+ OBFUSCATED_TYPE_NAME_AS_KEY_WITH_PARTITIONS;
private final int serializedKey;
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/PartitionMappingSupplierImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/PartitionMappingSupplierBase.java
similarity index 70%
rename from src/main/java/com/android/tools/r8/retrace/internal/PartitionMappingSupplierImpl.java
rename to src/main/java/com/android/tools/r8/retrace/internal/PartitionMappingSupplierBase.java
index de94d1e..5a9fc41 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/PartitionMappingSupplierImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/PartitionMappingSupplierBase.java
@@ -1,4 +1,4 @@
-// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
@@ -13,9 +13,10 @@
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.FieldReference;
+import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.retrace.InvalidMappingFileException;
import com.android.tools.r8.retrace.MappingPartitionFromKeySupplier;
-import com.android.tools.r8.retrace.PartitionMappingSupplier;
import com.android.tools.r8.retrace.PrepareMappingPartitionsCallback;
import com.android.tools.r8.retrace.RegisterMappingPartitionCallback;
import com.android.tools.r8.retrace.internal.ProguardMapReaderWithFiltering.ProguardMapReaderWithFilteringInputBuffer;
@@ -27,17 +28,12 @@
import java.util.LinkedHashSet;
import java.util.Set;
-/**
- * IntelliJ highlights the class as being invalid because it cannot see getClassNameMapper is
- * defined on the class for some reason.
- */
-public class PartitionMappingSupplierImpl extends PartitionMappingSupplier {
+public abstract class PartitionMappingSupplierBase<T extends PartitionMappingSupplierBase<T>> {
- private final byte[] metadata;
- private final RegisterMappingPartitionCallback registerPartitionCallback;
- private final PrepareMappingPartitionsCallback prepare;
- private final MappingPartitionFromKeySupplier partitionSupplier;
+ private final RegisterMappingPartitionCallback registerCallback;
+ private final PrepareMappingPartitionsCallback prepareCallback;
private final boolean allowExperimental;
+ private final byte[] metadata;
private final MapVersion fallbackMapVersion;
private ClassNameMapper classNameMapper;
@@ -46,22 +42,20 @@
private MappingPartitionMetadataInternal mappingPartitionMetadataCache;
- PartitionMappingSupplierImpl(
- byte[] metadata,
- RegisterMappingPartitionCallback registerPartitionCallback,
- PrepareMappingPartitionsCallback prepare,
- MappingPartitionFromKeySupplier partitionSupplier,
+ protected PartitionMappingSupplierBase(
+ RegisterMappingPartitionCallback registerCallback,
+ PrepareMappingPartitionsCallback prepareCallback,
boolean allowExperimental,
+ byte[] metadata,
MapVersion fallbackMapVersion) {
- this.metadata = metadata;
- this.registerPartitionCallback = registerPartitionCallback;
- this.prepare = prepare;
- this.partitionSupplier = partitionSupplier;
+ this.registerCallback = registerCallback;
+ this.prepareCallback = prepareCallback;
this.allowExperimental = allowExperimental;
+ this.metadata = metadata;
this.fallbackMapVersion = fallbackMapVersion;
}
- private MappingPartitionMetadataInternal getMetadata(DiagnosticsHandler diagnosticsHandler) {
+ protected MappingPartitionMetadataInternal getMetadata(DiagnosticsHandler diagnosticsHandler) {
if (mappingPartitionMetadataCache != null) {
return mappingPartitionMetadataCache;
}
@@ -70,38 +64,42 @@
CompatByteBuffer.wrapOrNull(metadata), fallbackMapVersion, diagnosticsHandler);
}
- @Override
- public PartitionMappingSupplier registerClassUse(
- DiagnosticsHandler diagnosticsHandler, ClassReference classReference) {
- registerKeyUse(getMetadata(diagnosticsHandler).getKey(classReference));
- return this;
+ public T registerClassUse(DiagnosticsHandler diagnosticsHandler, ClassReference classReference) {
+ return registerKeyUse(classReference.getTypeName());
}
- private void registerKeyUse(String key) {
+ public T registerMethodUse(
+ DiagnosticsHandler diagnosticsHandler, MethodReference methodReference) {
+ return registerClassUse(diagnosticsHandler, methodReference.getHolderClass());
+ }
+
+ public T registerFieldUse(DiagnosticsHandler diagnosticsHandler, FieldReference fieldReference) {
+ return registerClassUse(diagnosticsHandler, fieldReference.getHolderClass());
+ }
+
+ public T registerKeyUse(String key) {
// TODO(b/274735214): only call the register partition if we have a partition for it.
if (!builtKeys.contains(key) && pendingKeys.add(key)) {
- registerPartitionCallback.register(key);
+ registerCallback.register(key);
}
+ return self();
}
- @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(
getMetadata(diagnosticsHandler).getMapVersion().toMapVersionMappingInformation());
}
- @Override
- public RetracerImpl createRetracer(DiagnosticsHandler diagnosticsHandler) {
- MappingPartitionMetadataInternal metadata = getMetadata(diagnosticsHandler);
+ protected RetracerImpl createRetracerFromPartitionSupplier(
+ DiagnosticsHandler diagnosticsHandler, MappingPartitionFromKeySupplier partitionSupplier) {
if (!pendingKeys.isEmpty()) {
- prepare.prepare();
+ prepareCallback.prepare();
}
for (String pendingKey : pendingKeys) {
try {
@@ -116,7 +114,7 @@
classNameMapper =
ClassNameMapper.mapperFromLineReaderWithFiltering(
reader,
- metadata.getMapVersion(),
+ getMetadata(diagnosticsHandler).getMapVersion(),
diagnosticsHandler,
true,
allowExperimental,
@@ -134,4 +132,6 @@
return RetracerImpl.createInternal(
MappingSupplierInternalImpl.createInternal(classNameMapper), diagnosticsHandler);
}
+
+ public abstract T self();
}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/PartitionMappingSupplierBuilderImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/PartitionMappingSupplierBuilderImpl.java
deleted file mode 100644
index 2f69645..0000000
--- a/src/main/java/com/android/tools/r8/retrace/internal/PartitionMappingSupplierBuilderImpl.java
+++ /dev/null
@@ -1,78 +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.internal;
-
-import com.android.tools.r8.naming.MapVersion;
-import com.android.tools.r8.retrace.MappingPartitionFromKeySupplier;
-import com.android.tools.r8.retrace.PartitionMappingSupplier;
-import com.android.tools.r8.retrace.PrepareMappingPartitionsCallback;
-import com.android.tools.r8.retrace.RegisterMappingPartitionCallback;
-
-public class PartitionMappingSupplierBuilderImpl extends PartitionMappingSupplier.Builder {
-
- private MappingPartitionFromKeySupplier partitionSupplier;
- private RegisterMappingPartitionCallback registerPartitionCallback = key -> {};
- private PrepareMappingPartitionsCallback prepare = () -> {};
- private byte[] metadata;
- private final MapVersion fallbackMapVersion;
- private boolean allowExperimental = false;
-
- public PartitionMappingSupplierBuilderImpl(MapVersion fallbackMapVersion) {
- this.fallbackMapVersion = fallbackMapVersion;
- }
-
- @Override
- public PartitionMappingSupplier.Builder self() {
- return this;
- }
-
- @Override
- public PartitionMappingSupplier.Builder setAllowExperimental(boolean allowExperimental) {
- this.allowExperimental = allowExperimental;
- return self();
- }
-
- @Override
- public PartitionMappingSupplier.Builder setMetadata(byte[] metadata) {
- this.metadata = metadata;
- return self();
- }
-
- @Override
- public PartitionMappingSupplier.Builder setRegisterMappingPartitionCallback(
- RegisterMappingPartitionCallback registerPartitionCallback) {
- this.registerPartitionCallback = registerPartitionCallback;
- return self();
- }
-
- @Override
- public PartitionMappingSupplier.Builder setPrepareMappingPartitionsCallback(
- PrepareMappingPartitionsCallback prepare) {
- this.prepare = prepare;
- return self();
- }
-
- @Override
- public PartitionMappingSupplier.Builder setMappingPartitionFromKeySupplier(
- MappingPartitionFromKeySupplier partitionSupplier) {
- this.partitionSupplier = partitionSupplier;
- return self();
- }
-
- @Override
- public PartitionMappingSupplier build() {
- if (partitionSupplier == null) {
- throw new RuntimeException(
- "Cannot build without providing a mapping partition from key supplier.");
- }
- return new PartitionMappingSupplierImpl(
- metadata,
- registerPartitionCallback,
- prepare,
- partitionSupplier,
- allowExperimental,
- fallbackMapVersion);
- }
-}
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 f2d647a..8fe45e5 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
@@ -12,6 +12,8 @@
import com.android.tools.r8.naming.ProguardMapChecker.VerifyMappingFileHashResult;
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.retrace.InvalidMappingFileException;
import com.android.tools.r8.retrace.ProguardMapProducer;
import com.android.tools.r8.retrace.ProguardMappingSupplier;
@@ -72,6 +74,18 @@
}
@Override
+ public ProguardMappingSupplier registerMethodUse(
+ DiagnosticsHandler diagnosticsHandler, MethodReference methodReference) {
+ return registerClassUse(diagnosticsHandler, methodReference.getHolderClass());
+ }
+
+ @Override
+ public ProguardMappingSupplier registerFieldUse(
+ DiagnosticsHandler diagnosticsHandler, FieldReference fieldReference) {
+ return registerClassUse(diagnosticsHandler, fieldReference.getHolderClass());
+ }
+
+ @Override
public void verifyMappingFileHash(DiagnosticsHandler diagnosticsHandler) {
try (InputStream reader = proguardMapProducer.get()) {
VerifyMappingFileHashResult checkResult =
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/ResultWithContextImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/ResultWithContextImpl.java
deleted file mode 100644
index e65ecd5..0000000
--- a/src/main/java/com/android/tools/r8/retrace/internal/ResultWithContextImpl.java
+++ /dev/null
@@ -1,45 +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.internal;
-
-import com.android.tools.r8.retrace.ResultWithContext;
-import com.android.tools.r8.retrace.RetraceStackTraceContext;
-import java.util.List;
-import java.util.function.Consumer;
-
-public class ResultWithContextImpl<T> implements ResultWithContext<T> {
-
- private final List<T> result;
- private final RetraceStackTraceContext context;
-
- private ResultWithContextImpl(List<T> result, RetraceStackTraceContext context) {
- this.result = result;
- this.context = context;
- }
-
- public static <T> ResultWithContext<T> create(List<T> result, RetraceStackTraceContext context) {
- return new ResultWithContextImpl<>(result, context);
- }
-
- @Override
- public RetraceStackTraceContext getContext() {
- return context;
- }
-
- @Override
- public List<T> getLines() {
- return result;
- }
-
- @Override
- public void forEach(Consumer<T> consumer) {
- result.forEach(consumer);
- }
-
- @Override
- public boolean isEmpty() {
- return result.isEmpty();
- }
-}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetraceBase.java b/src/main/java/com/android/tools/r8/retrace/internal/RetraceBase.java
new file mode 100644
index 0000000..7820fce
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetraceBase.java
@@ -0,0 +1,322 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.retrace.internal;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.retrace.MappingSupplierBase;
+import com.android.tools.r8.retrace.RetraceInvalidStackTraceLineDiagnostics;
+import com.android.tools.r8.retrace.RetraceStackFrameAmbiguousResult;
+import com.android.tools.r8.retrace.RetraceStackFrameAmbiguousResultWithContext;
+import com.android.tools.r8.retrace.RetraceStackFrameResult;
+import com.android.tools.r8.retrace.RetraceStackFrameResultWithContext;
+import com.android.tools.r8.retrace.RetraceStackTraceContext;
+import com.android.tools.r8.retrace.RetraceStackTraceElementProxy;
+import com.android.tools.r8.retrace.RetraceStackTraceElementProxyResult;
+import com.android.tools.r8.retrace.RetraceStackTraceResult;
+import com.android.tools.r8.retrace.RetracedFieldReference;
+import com.android.tools.r8.retrace.RetracedMethodReference;
+import com.android.tools.r8.retrace.RetracedTypeReference;
+import com.android.tools.r8.retrace.Retracer;
+import com.android.tools.r8.retrace.StackTraceElementProxy;
+import com.android.tools.r8.retrace.StackTraceElementProxyRetracer;
+import com.android.tools.r8.retrace.StackTraceLineParser;
+import com.android.tools.r8.utils.Box;
+import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.Pair;
+import com.google.common.base.Equivalence;
+import com.google.common.base.Equivalence.Wrapper;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+public class RetraceBase<T, ST extends StackTraceElementProxy<T, ST>> {
+
+ private final StackTraceLineParser<T, ST> stackTraceLineParser;
+ private final MappingSupplierBase<?> mappingSupplier;
+ private final DiagnosticsHandler diagnosticsHandler;
+ protected final boolean isVerbose;
+
+ protected RetraceBase(
+ StackTraceLineParser<T, ST> stackTraceLineParser,
+ MappingSupplierBase<?> mappingSupplier,
+ DiagnosticsHandler diagnosticsHandler,
+ boolean isVerbose) {
+ this.stackTraceLineParser = stackTraceLineParser;
+ this.mappingSupplier = mappingSupplier;
+ this.diagnosticsHandler = diagnosticsHandler;
+ this.isVerbose = isVerbose;
+ }
+
+ protected List<ST> parse(List<T> stackTrace) {
+ ListUtils.forEachWithIndex(
+ stackTrace,
+ (line, lineNumber) -> {
+ if (line == null) {
+ diagnosticsHandler.error(
+ RetraceInvalidStackTraceLineDiagnostics.createNull(lineNumber));
+ throw new RetraceAbortException();
+ }
+ });
+ return ListUtils.map(stackTrace, stackTraceLineParser::parse);
+ }
+
+ protected ST parse(T obfuscated) {
+ return stackTraceLineParser.parse(obfuscated);
+ }
+
+ protected void registerUses(List<ST> parsed) {
+ parsed.forEach(this::registerUses);
+ }
+
+ protected void registerUses(ST parsed) {
+ parsed.registerUses(mappingSupplier, diagnosticsHandler);
+ }
+
+ protected RetraceStackTraceResult<T> retraceStackTraceParsedWithRetracer(
+ Retracer retracer, List<ST> stackTrace, RetraceStackTraceContext context) {
+ RetraceStackTraceElementProxyEquivalence<T, ST> equivalence =
+ new RetraceStackTraceElementProxyEquivalence<>(isVerbose);
+ StackTraceElementProxyRetracer<T, ST> proxyRetracer =
+ StackTraceElementProxyRetracer.createDefault(retracer);
+ List<RetraceStackFrameAmbiguousResult<T>> finalResult = new ArrayList<>();
+ RetraceStackTraceContext finalContext =
+ ListUtils.fold(
+ stackTrace,
+ context,
+ (newContext, stackTraceLine) -> {
+ List<Pair<RetraceStackTraceElementProxy<T, ST>, RetraceStackFrameResult<T>>>
+ resultsForLine = new ArrayList<>();
+ Box<List<T>> currentList = new Box<>();
+ Set<Wrapper<RetraceStackTraceElementProxy<T, ST>>> seen = new HashSet<>();
+ List<RetraceStackTraceContext> contexts = new ArrayList<>();
+ RetraceStackTraceElementProxyResult<T, ST> retraceResult =
+ proxyRetracer.retrace(stackTraceLine, newContext);
+ retraceResult.stream()
+ .forEach(
+ retracedElement -> {
+ if (retracedElement.isTopFrame() || !retracedElement.hasRetracedClass()) {
+ if (seen.add(equivalence.wrap(retracedElement))) {
+ currentList.set(new ArrayList<>());
+ resultsForLine.add(
+ Pair.create(
+ retracedElement,
+ RetraceStackFrameResultWithContextImpl.create(
+ currentList.get(), RetraceStackTraceContext.empty())));
+ contexts.add(retracedElement.getContext());
+ } else {
+ currentList.clear();
+ }
+ }
+ if (currentList.isSet()) {
+ currentList
+ .get()
+ .add(stackTraceLine.toRetracedItem(retracedElement, isVerbose));
+ }
+ });
+ resultsForLine.sort(Comparator.comparing(Pair::getFirst));
+ finalResult.add(
+ RetraceStackFrameAmbiguousResultWithContextImpl.create(
+ ListUtils.map(resultsForLine, Pair::getSecond),
+ RetraceStackTraceContext.empty()));
+ if (contexts.isEmpty()) {
+ return retraceResult.getResultContext();
+ }
+ return contexts.size() == 1 ? contexts.get(0) : RetraceStackTraceContext.empty();
+ });
+ return RetraceStackTraceResultImpl.create(finalResult, finalContext);
+ }
+
+ protected RetraceStackFrameAmbiguousResultWithContext<T> retraceFrameWithRetracer(
+ Retracer retracer, ST parsedFrame, RetraceStackTraceContext context) {
+ Map<RetraceStackTraceElementProxy<T, ST>, List<T>> ambiguousBlocks = new HashMap<>();
+ List<RetraceStackTraceElementProxy<T, ST>> ambiguousKeys = new ArrayList<>();
+ StackTraceElementProxyRetracer<T, ST> proxyRetracer =
+ StackTraceElementProxyRetracer.createDefault(retracer);
+ Box<RetraceStackTraceContext> contextBox = new Box<>(context);
+ proxyRetracer.retrace(parsedFrame, context).stream()
+ .forEach(
+ retracedElement -> {
+ if (retracedElement.isTopFrame() || !retracedElement.hasRetracedClass()) {
+ ambiguousKeys.add(retracedElement);
+ ambiguousBlocks.put(retracedElement, new ArrayList<>());
+ }
+ ambiguousBlocks
+ .get(ListUtils.last(ambiguousKeys))
+ .add(parsedFrame.toRetracedItem(retracedElement, isVerbose));
+ contextBox.set(retracedElement.getContext());
+ });
+ Collections.sort(ambiguousKeys);
+ List<RetraceStackFrameResult<T>> retracedList = new ArrayList<>();
+ ambiguousKeys.forEach(
+ key ->
+ retracedList.add(
+ RetraceStackFrameResultWithContextImpl.create(
+ ambiguousBlocks.get(key), RetraceStackTraceContext.empty())));
+ return RetraceStackFrameAmbiguousResultWithContextImpl.create(retracedList, contextBox.get());
+ }
+
+ protected RetraceStackFrameResultWithContext<T> retraceLineWithRetracer(
+ Retracer retracer, ST parsedFrame, RetraceStackTraceContext context) {
+ StackTraceElementProxyRetracer<T, ST> proxyRetracer =
+ StackTraceElementProxyRetracer.createDefault(retracer);
+ Box<RetraceStackTraceContext> contextBox = new Box<>(context);
+ List<T> result =
+ proxyRetracer.retrace(parsedFrame, context).stream()
+ .map(
+ retraceFrame -> {
+ contextBox.set(retraceFrame.getContext());
+ return parsedFrame.toRetracedItem(retraceFrame, isVerbose);
+ })
+ .collect(Collectors.toList());
+ return RetraceStackFrameResultWithContextImpl.create(result, contextBox.get());
+ }
+
+ private static class RetraceStackTraceElementProxyEquivalence<
+ T, ST extends StackTraceElementProxy<T, ST>>
+ extends Equivalence<RetraceStackTraceElementProxy<T, ST>> {
+
+ private final boolean isVerbose;
+
+ public RetraceStackTraceElementProxyEquivalence(boolean isVerbose) {
+ this.isVerbose = isVerbose;
+ }
+
+ @Override
+ protected boolean doEquivalent(
+ RetraceStackTraceElementProxy<T, ST> one, RetraceStackTraceElementProxy<T, ST> other) {
+ if (one == other) {
+ return true;
+ }
+ if (testNotEqualProperty(
+ one,
+ other,
+ RetraceStackTraceElementProxy::hasRetracedClass,
+ r -> r.getRetracedClass().getTypeName())
+ || testNotEqualProperty(
+ one,
+ other,
+ RetraceStackTraceElementProxy::hasSourceFile,
+ RetraceStackTraceElementProxy::getSourceFile)) {
+ return false;
+ }
+ assert one.getOriginalItem() == other.getOriginalItem();
+ if (isVerbose
+ || (one.getOriginalItem().hasLineNumber() && one.getOriginalItem().getLineNumber() > 0)) {
+ if (testNotEqualProperty(
+ one,
+ other,
+ RetraceStackTraceElementProxy::hasLineNumber,
+ RetraceStackTraceElementProxy::getLineNumber)) {
+ return false;
+ }
+ }
+ if (one.hasRetracedMethod() != other.hasRetracedMethod()) {
+ return false;
+ }
+ if (one.hasRetracedMethod()) {
+ RetracedMethodReference oneMethod = one.getRetracedMethod();
+ RetracedMethodReference otherMethod = other.getRetracedMethod();
+ if (oneMethod.isKnown() != otherMethod.isKnown()) {
+ return false;
+ }
+ // In verbose mode we check the signature, otherwise we only check the name
+ if (!oneMethod.getMethodName().equals(otherMethod.getMethodName())) {
+ return false;
+ }
+ if (isVerbose
+ && ((oneMethod.isKnown()
+ && !oneMethod
+ .asKnown()
+ .getMethodReference()
+ .toString()
+ .equals(otherMethod.asKnown().getMethodReference().toString()))
+ || (!oneMethod.isKnown()
+ && !oneMethod.getMethodName().equals(otherMethod.getMethodName())))) {
+ return false;
+ }
+ }
+ if (one.hasRetracedField() != other.hasRetracedField()) {
+ return false;
+ }
+ if (one.hasRetracedField()) {
+ RetracedFieldReference oneField = one.getRetracedField();
+ RetracedFieldReference otherField = other.getRetracedField();
+ if (oneField.isKnown() != otherField.isKnown()) {
+ return false;
+ }
+ if (!oneField.getFieldName().equals(otherField.getFieldName())) {
+ return false;
+ }
+ if (isVerbose
+ && ((oneField.isKnown()
+ && !oneField
+ .asKnown()
+ .getFieldReference()
+ .toString()
+ .equals(otherField.asKnown().getFieldReference().toString()))
+ || (oneField.isUnknown()
+ && !oneField.getFieldName().equals(otherField.getFieldName())))) {
+ return false;
+ }
+ }
+ if (one.hasRetracedFieldOrReturnType() != other.hasRetracedFieldOrReturnType()) {
+ return false;
+ }
+ if (one.hasRetracedFieldOrReturnType()) {
+ RetracedTypeReference oneFieldOrReturn = one.getRetracedFieldOrReturnType();
+ RetracedTypeReference otherFieldOrReturn = other.getRetracedFieldOrReturnType();
+ if (!compareRetracedTypeReference(oneFieldOrReturn, otherFieldOrReturn)) {
+ return false;
+ }
+ }
+ if (one.hasRetracedMethodArguments() != other.hasRetracedMethodArguments()) {
+ return false;
+ }
+ if (one.hasRetracedMethodArguments()) {
+ List<RetracedTypeReference> oneMethodArguments = one.getRetracedMethodArguments();
+ List<RetracedTypeReference> otherMethodArguments = other.getRetracedMethodArguments();
+ if (oneMethodArguments.size() != otherMethodArguments.size()) {
+ return false;
+ }
+ for (int i = 0; i < oneMethodArguments.size(); i++) {
+ if (compareRetracedTypeReference(
+ oneMethodArguments.get(i), otherMethodArguments.get(i))) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ private boolean compareRetracedTypeReference(
+ RetracedTypeReference one, RetracedTypeReference other) {
+ return one.isVoid() == other.isVoid()
+ && (one.isVoid() || one.getTypeName().equals(other.getTypeName()));
+ }
+
+ @Override
+ protected int doHash(RetraceStackTraceElementProxy<T, ST> proxy) {
+ return 0;
+ }
+
+ private <V extends Comparable<V>> boolean testNotEqualProperty(
+ RetraceStackTraceElementProxy<T, ST> one,
+ RetraceStackTraceElementProxy<T, ST> other,
+ Function<RetraceStackTraceElementProxy<T, ST>, Boolean> predicate,
+ Function<RetraceStackTraceElementProxy<T, ST>, V> getter) {
+ return Comparator.comparing(predicate)
+ .thenComparing(getter, Comparator.nullsFirst(V::compareTo))
+ .compare(one, other)
+ != 0;
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetraceStackFrameAmbiguousResultWithContextImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetraceStackFrameAmbiguousResultWithContextImpl.java
new file mode 100644
index 0000000..cabb3f7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetraceStackFrameAmbiguousResultWithContextImpl.java
@@ -0,0 +1,72 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.retrace.internal;
+
+import com.android.tools.r8.retrace.RetraceStackFrameAmbiguousResultWithContext;
+import com.android.tools.r8.retrace.RetraceStackFrameResult;
+import com.android.tools.r8.retrace.RetraceStackTraceContext;
+import java.util.List;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+
+public class RetraceStackFrameAmbiguousResultWithContextImpl<T>
+ implements RetraceStackFrameAmbiguousResultWithContext<T> {
+
+ private final RetraceStackTraceContext context;
+ private final List<RetraceStackFrameResult<T>> result;
+
+ private RetraceStackFrameAmbiguousResultWithContextImpl(
+ List<RetraceStackFrameResult<T>> result, RetraceStackTraceContext context) {
+ this.result = result;
+ this.context = context;
+ }
+
+ public static <T> RetraceStackFrameAmbiguousResultWithContextImpl<T> create(
+ List<RetraceStackFrameResult<T>> result, RetraceStackTraceContext context) {
+ return new RetraceStackFrameAmbiguousResultWithContextImpl<>(result, context);
+ }
+
+ @Override
+ public RetraceStackTraceContext getContext() {
+ return context;
+ }
+
+ @Override
+ public boolean isAmbiguous() {
+ return result.size() > 1;
+ }
+
+ @Override
+ public List<RetraceStackFrameResult<T>> getAmbiguousResult() {
+ return result;
+ }
+
+ @Override
+ public void forEach(Consumer<RetraceStackFrameResult<T>> consumer) {
+ result.forEach(consumer);
+ }
+
+ @Override
+ public void forEachWithIndex(BiConsumer<RetraceStackFrameResult<T>, Integer> consumer) {
+ for (int i = 0; i < result.size(); i++) {
+ consumer.accept(result.get(i), i);
+ }
+ }
+
+ @Override
+ public int size() {
+ return result.size();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return result.isEmpty();
+ }
+
+ @Override
+ public RetraceStackFrameResult<T> get(int i) {
+ return result.get(i);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetraceStackFrameResultWithContextImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetraceStackFrameResultWithContextImpl.java
new file mode 100644
index 0000000..974d80f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetraceStackFrameResultWithContextImpl.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.retrace.internal;
+
+import com.android.tools.r8.retrace.RetraceStackFrameResultWithContext;
+import com.android.tools.r8.retrace.RetraceStackTraceContext;
+import java.util.List;
+import java.util.function.Consumer;
+
+public class RetraceStackFrameResultWithContextImpl<T>
+ implements RetraceStackFrameResultWithContext<T> {
+
+ private final RetraceStackTraceContext context;
+ private final List<T> result;
+
+ private RetraceStackFrameResultWithContextImpl(List<T> result, RetraceStackTraceContext context) {
+ this.result = result;
+ this.context = context;
+ }
+
+ public static <T> RetraceStackFrameResultWithContextImpl<T> create(
+ List<T> result, RetraceStackTraceContext context) {
+ return new RetraceStackFrameResultWithContextImpl<>(result, context);
+ }
+
+ @Override
+ public RetraceStackTraceContext getContext() {
+ return context;
+ }
+
+ @Override
+ public List<T> getResult() {
+ return result;
+ }
+
+ @Override
+ public void forEach(Consumer<T> consumer) {
+ result.forEach(consumer);
+ }
+
+ @Override
+ public int size() {
+ return result.size();
+ }
+
+ @Override
+ public T get(int i) {
+ return result.get(i);
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return result.isEmpty();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetraceStackTraceResultImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetraceStackTraceResultImpl.java
new file mode 100644
index 0000000..4a4ea6f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetraceStackTraceResultImpl.java
@@ -0,0 +1,48 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.retrace.internal;
+
+import com.android.tools.r8.retrace.RetraceStackFrameAmbiguousResult;
+import com.android.tools.r8.retrace.RetraceStackTraceContext;
+import com.android.tools.r8.retrace.RetraceStackTraceResult;
+import java.util.List;
+import java.util.function.Consumer;
+
+public class RetraceStackTraceResultImpl<T> implements RetraceStackTraceResult<T> {
+
+ private final List<RetraceStackFrameAmbiguousResult<T>> result;
+ private final RetraceStackTraceContext context;
+
+ private RetraceStackTraceResultImpl(
+ List<RetraceStackFrameAmbiguousResult<T>> result, RetraceStackTraceContext context) {
+ this.result = result;
+ this.context = context;
+ }
+
+ public static <T> RetraceStackTraceResult<T> create(
+ List<RetraceStackFrameAmbiguousResult<T>> result, RetraceStackTraceContext context) {
+ return new RetraceStackTraceResultImpl<>(result, context);
+ }
+
+ @Override
+ public RetraceStackTraceContext getContext() {
+ return context;
+ }
+
+ @Override
+ public List<RetraceStackFrameAmbiguousResult<T>> getResult() {
+ return result;
+ }
+
+ @Override
+ public void forEach(Consumer<RetraceStackFrameAmbiguousResult<T>> consumer) {
+ getResult().forEach(consumer);
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return result.isEmpty();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/StackTraceRegularExpressionParser.java b/src/main/java/com/android/tools/r8/retrace/internal/StackTraceRegularExpressionParser.java
index b73c9bc..0e01d09 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/StackTraceRegularExpressionParser.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/StackTraceRegularExpressionParser.java
@@ -300,7 +300,7 @@
boolean insertSeparatorForRetraced = false;
// We need to include ':' in the group since we may want to rewrite '(SourceFile:0)` into
// (SourceFile) and not (SourceFile:)
- if (startOfGroup > 0 && builder.getLine().charAt(startOfGroup + -1) == ':') {
+ if (startOfGroup > 0 && builder.getLine().charAt(startOfGroup - 1) == ':') {
startOfGroup = startOfGroup - 1;
insertSeparatorForRetraced = true;
}
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetBuilderEventConsumer.java b/src/main/java/com/android/tools/r8/shaking/RootSetBuilderEventConsumer.java
index b59961e..6bf84a6 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilderEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilderEventConsumer.java
@@ -31,7 +31,7 @@
}
@Override
- public void acceptCompanionClassClinit(ProgramMethod method) {
+ public void acceptCompanionClassClinit(ProgramMethod method, ProgramMethod companionMethod) {
// Intentionally empty.
}
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index 9b437c1..150e7c8 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -56,10 +56,10 @@
import com.android.tools.r8.graph.PrunedItems;
import com.android.tools.r8.graph.SubtypingInfo;
import com.android.tools.r8.graph.TopDownClassHierarchyTraversal;
-import com.android.tools.r8.graph.TreeFixerBase;
import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.graph.UseRegistryWithResult;
import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
+import com.android.tools.r8.graph.fixup.TreeFixerBase;
import com.android.tools.r8.graph.lens.FieldLookupResult;
import com.android.tools.r8.graph.lens.GraphLens;
import com.android.tools.r8.graph.lens.MethodLookupResult;
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
index 68c2918..c955fad 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
@@ -20,7 +20,7 @@
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.PrunedItems;
-import com.android.tools.r8.graph.TreeFixerBase;
+import com.android.tools.r8.graph.fixup.TreeFixerBase;
import com.android.tools.r8.graph.lens.GraphLens;
import com.android.tools.r8.graph.lens.NestedGraphLens;
import com.android.tools.r8.graph.lens.NonIdentityGraphLens;
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidAppConsumers.java b/src/main/java/com/android/tools/r8/utils/AndroidAppConsumers.java
index 653e567..8142f7b 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidAppConsumers.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidAppConsumers.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.utils;
+import static com.android.tools.r8.utils.MapConsumerUtils.wrapExistingMapConsumer;
+
import com.android.tools.r8.BaseCompilerCommand;
import com.android.tools.r8.ByteDataView;
import com.android.tools.r8.ClassFileConsumer;
@@ -14,10 +16,9 @@
import com.android.tools.r8.DexIndexedConsumer.ForwardingConsumer;
import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.ProgramConsumer;
-import com.android.tools.r8.ProguardMapConsumer;
import com.android.tools.r8.ResourceException;
import com.android.tools.r8.StringConsumer;
-import com.android.tools.r8.naming.MultiProguardMapConsumer;
+import com.android.tools.r8.naming.MapConsumer;
import com.android.tools.r8.naming.ProguardMapStringConsumer;
import com.android.tools.r8.origin.Origin;
import com.google.common.io.ByteStreams;
@@ -36,20 +37,19 @@
private boolean closed = false;
private ProgramConsumer programConsumer = null;
- private ProguardMapConsumer proguardMapConsumer = null;
+ private MapConsumer mapConsumer = null;
public AndroidAppConsumers() {
// Nothing to do.
}
- public AndroidAppConsumers(BaseCompilerCommand.Builder builder) {
+ public AndroidAppConsumers(BaseCompilerCommand.Builder<?, ?> builder) {
builder.setProgramConsumer(wrapProgramConsumer(builder.getProgramConsumer()));
}
public AndroidAppConsumers(InternalOptions options) {
options.programConsumer = wrapProgramConsumer(options.programConsumer);
- options.proguardMapConsumer =
- wrapProguardMapConsumer(options.proguardMapConsumer, options.reporter);
+ options.mapConsumer = wrapProguardMapConsumer(options.mapConsumer);
}
public ProgramConsumer wrapProgramConsumer(ProgramConsumer consumer) {
@@ -69,39 +69,35 @@
return programConsumer;
}
- public ProguardMapConsumer wrapProguardMapConsumer(
- ProguardMapConsumer consumer, DiagnosticsHandler diagnosticsHandler) {
- assert proguardMapConsumer == null;
+ public MapConsumer wrapProguardMapConsumer(MapConsumer consumer) {
+ assert mapConsumer == null;
if (consumer != null) {
- proguardMapConsumer =
- MultiProguardMapConsumer.builder()
- .addProguardMapConsumer(consumer)
- .addProguardMapConsumer(
- ProguardMapStringConsumer.builder()
- .setStringConsumer(
- new StringConsumer() {
- StringBuilder stringBuilder = null;
+ mapConsumer =
+ wrapExistingMapConsumer(
+ consumer,
+ ProguardMapStringConsumer.builder()
+ .setStringConsumer(
+ new StringConsumer() {
+ StringBuilder stringBuilder = null;
- @Override
- public void accept(String string, DiagnosticsHandler handler) {
- if (stringBuilder == null) {
- stringBuilder = new StringBuilder();
- }
- stringBuilder.append(string);
- }
+ @Override
+ public void accept(String string, DiagnosticsHandler handler) {
+ if (stringBuilder == null) {
+ stringBuilder = new StringBuilder();
+ }
+ stringBuilder.append(string);
+ }
- @Override
- public void finished(DiagnosticsHandler handler) {
- if (stringBuilder != null) {
- builder.setProguardMapOutputData(stringBuilder.toString());
- }
- }
- })
- .setDiagnosticsHandler(diagnosticsHandler)
- .build())
- .build();
+ @Override
+ public void finished(DiagnosticsHandler handler) {
+ if (stringBuilder != null) {
+ builder.setProguardMapOutputData(stringBuilder.toString());
+ }
+ }
+ })
+ .build());
}
- return proguardMapConsumer;
+ return mapConsumer;
}
public DexIndexedConsumer wrapDexIndexedConsumer(DexIndexedConsumer consumer) {
diff --git a/src/main/java/com/android/tools/r8/utils/Box.java b/src/main/java/com/android/tools/r8/utils/Box.java
index 00fde15..621d108 100644
--- a/src/main/java/com/android/tools/r8/utils/Box.java
+++ b/src/main/java/com/android/tools/r8/utils/Box.java
@@ -32,6 +32,13 @@
return super.computeIfAbsent(supplier);
}
+ public <E extends Exception> T computeIfAbsentThrowing(ThrowingSupplier<T, E> supplier) throws E {
+ if (!isSet()) {
+ set(supplier.get());
+ }
+ return get();
+ }
+
@Override
public T get() {
return super.get();
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 67f4fda..cd3c1c5 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -17,7 +17,6 @@
import com.android.tools.r8.GlobalSyntheticsConsumer;
import com.android.tools.r8.MapIdProvider;
import com.android.tools.r8.ProgramConsumer;
-import com.android.tools.r8.ProguardMapConsumer;
import com.android.tools.r8.SourceFileProvider;
import com.android.tools.r8.StringConsumer;
import com.android.tools.r8.SyntheticInfoConsumer;
@@ -72,6 +71,7 @@
import com.android.tools.r8.ir.optimize.Inliner.Reason;
import com.android.tools.r8.ir.optimize.enums.EnumDataMap;
import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.naming.MapConsumer;
import com.android.tools.r8.naming.MapVersion;
import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagatorEventConsumer;
import com.android.tools.r8.optimize.redundantbridgeremoval.RedundantBridgeRemovalOptions;
@@ -1051,7 +1051,7 @@
// If null, no proguard map needs to be computed.
// If non null it must be and passed to the consumer.
- public ProguardMapConsumer proguardMapConsumer = null;
+ public MapConsumer mapConsumer = null;
// If null, no usage information needs to be computed.
// If non-null, it must be and is passed to the consumer.
diff --git a/src/main/java/com/android/tools/r8/utils/MapConsumerUtils.java b/src/main/java/com/android/tools/r8/utils/MapConsumerUtils.java
new file mode 100644
index 0000000..5f8d295
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/MapConsumerUtils.java
@@ -0,0 +1,90 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.PartitionMapConsumer;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.naming.MapConsumer;
+import com.android.tools.r8.naming.ProguardMapMarkerInfo;
+import com.android.tools.r8.retrace.MappingPartition;
+import com.android.tools.r8.retrace.MappingPartitionMetadata;
+import com.android.tools.r8.utils.ZipUtils.ZipBuilder;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.function.Function;
+
+public class MapConsumerUtils {
+
+ public static MapConsumer wrapExistingMapConsumer(
+ MapConsumer existingMapConsumer, MapConsumer newConsumer) {
+ if (existingMapConsumer == null) {
+ return newConsumer;
+ }
+ return new MapConsumer() {
+ @Override
+ public void accept(
+ DiagnosticsHandler diagnosticsHandler,
+ ProguardMapMarkerInfo makerInfo,
+ ClassNameMapper classNameMapper) {
+ existingMapConsumer.accept(diagnosticsHandler, makerInfo, classNameMapper);
+ newConsumer.accept(diagnosticsHandler, makerInfo, classNameMapper);
+ }
+
+ @Override
+ public void finished(DiagnosticsHandler handler) {
+ existingMapConsumer.finished(handler);
+ newConsumer.finished(handler);
+ }
+ };
+ }
+
+ public static <T> MapConsumer wrapExistingMapConsumerIfNotNull(
+ MapConsumer existingMapConsumer, T object, Function<T, MapConsumer> producer) {
+ if (object == null) {
+ return existingMapConsumer;
+ }
+ return wrapExistingMapConsumer(existingMapConsumer, producer.apply(object));
+ }
+
+ public static PartitionMapConsumer createZipConsumer(Path path) {
+ return new PartitionMapConsumer() {
+
+ private final Box<ZipBuilder> zipBuilderBox = new Box<>();
+
+ @Override
+ public void acceptMappingPartition(MappingPartition mappingPartition) {
+ try {
+ zipBuilderBox
+ .computeIfAbsentThrowing(() -> ZipBuilder.builder(path))
+ .addBytes(mappingPartition.getKey(), mappingPartition.getPayload());
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public void acceptMappingPartitionMetadata(
+ MappingPartitionMetadata mappingPartitionMetadata) {
+ try {
+ zipBuilderBox
+ .computeIfAbsentThrowing(() -> ZipBuilder.builder(path))
+ .addBytes("METADATA", mappingPartitionMetadata.getBytes());
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public void finished(DiagnosticsHandler handler) {
+ try {
+ zipBuilderBox.computeIfAbsentThrowing(() -> ZipBuilder.builder(path)).build();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ };
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/LongLivedProgramMethodSetBuilder.java b/src/main/java/com/android/tools/r8/utils/collections/LongLivedProgramMethodSetBuilder.java
index 81578f8..604695c 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/LongLivedProgramMethodSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/LongLivedProgramMethodSetBuilder.java
@@ -168,7 +168,7 @@
appView.graphLens().getRenamedMethodSignature(method, appliedGraphLens);
DexProgramClass holder = appView.definitionForHolder(rewrittenMethod).asProgramClass();
DexEncodedMethod definition = holder.lookupMethod(rewrittenMethod);
- assert definition != null;
+ assert definition != null : "Missing method: " + rewrittenMethod;
result.createAndAdd(holder, definition);
}
return result;
diff --git a/src/main/java/com/android/tools/r8/utils/positions/LineNumberOptimizer.java b/src/main/java/com/android/tools/r8/utils/positions/LineNumberOptimizer.java
index 0181e06..881fbe2 100644
--- a/src/main/java/com/android/tools/r8/utils/positions/LineNumberOptimizer.java
+++ b/src/main/java/com/android/tools/r8/utils/positions/LineNumberOptimizer.java
@@ -46,7 +46,7 @@
Timing timing,
OriginalSourceFiles originalSourceFiles,
DebugRepresentationPredicate representation) {
- assert appView.options().proguardMapConsumer != null;
+ assert appView.options().mapConsumer != null;
if (shouldEmitOriginalMappingFile(appView)) {
appView.options().reporter.warning(new NotSupportedMapVersionForMappingComposeDiagnostic());
timing.begin("Write proguard map");
diff --git a/src/test/examples/catchhandleroverlap/CatchHandlerOverlap.java b/src/test/examples/catchhandleroverlap/CatchHandlerOverlap.java
deleted file mode 100644
index 7a4950c..0000000
--- a/src/test/examples/catchhandleroverlap/CatchHandlerOverlap.java
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright (c) 2018, 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 catchhandleroverlap;
-
-public class CatchHandlerOverlap {
- private static void f() throws Exception {
- throw new Exception("f");
- }
-
- private static void g() throws Exception {
- throw new Exception("g");
- }
-
- private static void h(int i1, int i2, int i3, int i4, int i5, int i6, int i7, int i8, int i9,
- int i10, int i11, int i12, int i13, int i14, int i15, int i16, int i17) {
- System.out.println(i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9 + i10 + i11 +
- i12 + i13 + i14 + i15 + i16 + i17);
- try {
- f();
- } catch (Exception e0) {
- try {
- g();
- } catch (Exception e1) {
- System.out.println(e0.getMessage() + " " + e1.getMessage());
- }
- }
- }
-
- public static void main(String[] args) {
- h(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17);
- }
-}
diff --git a/src/test/examplesJava17/enum_sealed/Enum.java b/src/test/examplesJava17/enum_sealed/Enum.java
new file mode 100644
index 0000000..7483363
--- /dev/null
+++ b/src/test/examplesJava17/enum_sealed/Enum.java
@@ -0,0 +1,15 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package enum_sealed;
+
+public enum Enum {
+ A,
+ B {
+ @Override
+ public String toString() {
+ return "a B";
+ }
+ }
+}
diff --git a/src/test/examplesJava17/enum_sealed/Main.java b/src/test/examplesJava17/enum_sealed/Main.java
new file mode 100644
index 0000000..8a4d42a
--- /dev/null
+++ b/src/test/examplesJava17/enum_sealed/Main.java
@@ -0,0 +1,13 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package enum_sealed;
+
+public class Main {
+
+ public static void main(String[] args) {
+ System.out.println(Enum.A);
+ System.out.println(Enum.B);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/CfFrontendExamplesTest.java b/src/test/java/com/android/tools/r8/CfFrontendExamplesTest.java
deleted file mode 100644
index 95dd9bd..0000000
--- a/src/test/java/com/android/tools/r8/CfFrontendExamplesTest.java
+++ /dev/null
@@ -1,437 +0,0 @@
-// Copyright (c) 2018, 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;
-
-import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
-import static com.google.common.io.ByteStreams.toByteArray;
-import static org.junit.Assert.assertEquals;
-
-import com.android.tools.r8.origin.Origin;
-import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.StringUtils;
-import com.google.common.collect.ImmutableList;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-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;
-import org.objectweb.asm.ClassReader;
-import org.objectweb.asm.util.ASMifier;
-import org.objectweb.asm.util.TraceClassVisitor;
-
-@RunWith(Parameterized.class)
-public class CfFrontendExamplesTest extends TestBase {
-
- @Parameters(name = "{0}")
- public static TestParametersCollection data() {
- return getTestParameters().withNoneRuntime().build();
- }
-
- public CfFrontendExamplesTest(TestParameters parameters) {
- parameters.assertNoneRuntime();
- }
-
- @Test
- public void testArithmetic() throws Exception {
- runTest("arithmetic.Arithmetic");
- }
-
- @Test
- public void testBridgeMethod() throws Exception {
- runTest("bridge.BridgeMethod");
- }
-
- @Test
- public void testCommonSubexpressionElimination() throws Exception {
- runTest("cse.CommonSubexpressionElimination");
- }
-
- @Test
- public void testConstants() throws Exception {
- runTest("constants.Constants");
- }
-
- @Test
- public void testControlFlow() throws Exception {
- runTest("controlflow.ControlFlow");
- }
-
- @Test
- public void testConversions() throws Exception {
- runTest("conversions.Conversions");
- }
-
- @Test
- public void testFloatingPointValuedAnnotation() throws Exception {
- runTest("floating_point_annotations.FloatingPointValuedAnnotationTest");
- }
-
- @Test
- public void testFilledArray() throws Exception {
- runTest("filledarray.FilledArray");
- }
-
- @Test
- public void testHello() throws Exception {
- runTest("hello.Hello");
- }
-
- @Test
- public void testIfStatements() throws Exception {
- runTest("ifstatements.IfStatements");
- }
-
- @Test
- public void testInlining() throws Exception {
- runTest("inlining.Inlining");
- }
-
- @Test
- public void testInstanceVariable() throws Exception {
- runTest("instancevariable.InstanceVariable");
- }
-
- @Test
- public void testInstanceofString() throws Exception {
- runTest("instanceofstring.InstanceofString");
- }
-
- @Test
- public void testInvoke() throws Exception {
- runTest("invoke.Invoke");
- }
-
- @Test
- public void testJumboString() throws Exception {
- runTest("jumbostring.JumboString");
- }
-
- @Test
- public void testLoadConst() throws Exception {
- runTest("loadconst.LoadConst");
- }
-
- @Test
- public void testUdpServer() throws Exception {
- runTest("loop.UdpServer");
- }
-
- @Test
- public void testRegAlloc() throws Exception {
- runTest("regalloc.RegAlloc");
- }
-
- @Test
- public void testReturns() throws Exception {
- runTest("returns.Returns");
- }
-
- @Test
- public void testStaticField() throws Exception {
- runTest("staticfield.StaticField");
- }
-
- @Test
- public void testStringBuilding() throws Exception {
- runTest("stringbuilding.StringBuilding");
- }
-
- @Test
- public void testSwitches() throws Exception {
- runTest("switches.Switches");
- }
-
- @Test
- public void testSync() throws Exception {
- runTest("sync.Sync");
- }
-
- @Test
- public void testThrowing() throws Exception {
- runTest("throwing.Throwing");
- }
-
- @Test
- public void testTrivial() throws Exception {
- runTest("trivial.Trivial");
- }
-
- @Test
- public void testTryCatch() throws Exception {
- runTest("trycatch.TryCatch");
- }
-
- @Test
- public void testNestedTryCatches() throws Exception {
- runTest("nestedtrycatches.NestedTryCatches");
- }
-
- @Test
- public void testTryCatchMany() throws Exception {
- runTest("trycatchmany.TryCatchMany");
- }
-
- @Test
- public void testInvokeEmpty() throws Exception {
- runTest("invokeempty.InvokeEmpty");
- }
-
- @Test
- public void testRegress() throws Exception {
- runTest("regress.Regress");
- }
-
- @Test
- public void testRegress2() throws Exception {
- runTest("regress2.Regress2");
- }
-
- @Test
- public void testRegress37726195() throws Exception {
- runTest("regress_37726195.Regress");
- }
-
- @Test
- public void testRegress37658666() throws Exception {
- runTest(
- "regress_37658666.Regress",
- (expected, actual) -> {
- // javac emits LDC(-0.0f) instead of the shorter FCONST_0 FNEG emitted by CfConstNumber.
- String ldc = "methodVisitor.visitLdcInsn(new Float(\"-0.0\"));";
- String constNeg = "methodVisitor.visitInsn(FCONST_0);\nmethodVisitor.visitInsn(FNEG);";
- assertEquals(expected.replace(ldc, constNeg), actual);
- });
- }
-
- @Test
- public void testRegress37875803() throws Exception {
- runTest("regress_37875803.Regress");
- }
-
- @Test
- public void testRegress37955340() throws Exception {
- runTest("regress_37955340.Regress");
- }
-
- @Test
- public void testRegress62300145() throws Exception {
- runTest("regress_62300145.Regress");
- }
-
- @Test
- public void testRegress64881691() throws Exception {
- runTest("regress_64881691.Regress");
- }
-
- @Test
- public void testRegress65104300() throws Exception {
- runTest("regress_65104300.Regress");
- }
-
- @Test
- public void testRegress70703087() throws Exception {
- runTest("regress_70703087.Test");
- }
-
- @Test
- public void testRegress70736958() throws Exception {
- runTest("regress_70736958.Test");
- }
-
- @Test
- public void testRegress70737019() throws Exception {
- runTest("regress_70737019.Test");
- }
-
- @Test
- public void testRegress72361252() throws Exception {
- runTest("regress_72361252.Test");
- }
-
- @Test
- public void testMemberrebinding2() throws Exception {
- runTest("memberrebinding2.Memberrebinding");
- }
-
- @Test
- public void testMemberrebinding3() throws Exception {
- runTest("memberrebinding3.Memberrebinding");
- }
-
- @Test
- public void testMinification() throws Exception {
- runTest("minification.Minification");
- }
-
- @Test
- public void testEnclosingmethod() throws Exception {
- runTest("enclosingmethod.Main");
- }
-
- @Test
- public void testEnclosingmethodProguarded() throws Exception {
- runTest("enclosingmethod_proguarded.Main");
- }
-
- @Test
- public void testInterfaceInlining() throws Exception {
- runTest("interfaceinlining.Main");
- }
-
- @Test
- public void testSwitchmaps() throws Exception {
- runTest("switchmaps.Switches");
- }
-
- private void runTest(String clazz) throws Exception {
- runTest(
- clazz, (expected, actual) -> assertEquals("Class " + clazz + " differs", expected, actual));
- }
-
- private void runTest(String clazz, BiConsumer<String, String> comparator) throws Exception {
- assert comparator != null;
- String pkg = clazz.substring(0, clazz.lastIndexOf('.'));
- String suffix = "_debuginfo_all";
- Path inputJar = Paths.get(ToolHelper.EXAMPLES_BUILD_DIR, pkg + suffix + JAR_EXTENSION);
- Path outputJar = temp.getRoot().toPath().resolve("output.jar");
- R8Command command =
- R8Command.builder()
- .addProgramFiles(inputJar)
- .addLibraryFiles(ToolHelper.getJava8RuntimeJar())
- .setMode(CompilationMode.DEBUG)
- .setDisableTreeShaking(true)
- .setDisableMinification(true)
- .addProguardConfiguration(ImmutableList.of("-keepattributes *"), Origin.unknown())
- .setOutput(outputJar, OutputMode.ClassFile)
- .build();
- ToolHelper.runR8(command, options -> options.skipIR = true);
- ArchiveClassFileProvider expected = new ArchiveClassFileProvider(inputJar);
- ArchiveClassFileProvider actual = new ArchiveClassFileProvider(outputJar);
- assertEquals(getSortedDescriptorList(expected), getSortedDescriptorList(actual));
- for (String descriptor : expected.getClassDescriptors()) {
- byte[] expectedBytes = getClassAsBytes(expected, descriptor);
- byte[] actualBytes = getClassAsBytes(actual, descriptor);
- if (!Arrays.equals(expectedBytes, actualBytes)) {
- String expectedString = replaceCatchThrowableByCatchAll(asmToString(expectedBytes));
- String actualString = asmToString(actualBytes);
- comparator.accept(expectedString, actualString);
- }
- }
- }
-
- private static String replaceCatchThrowableByCatchAll(String content) {
- String catchThrowablePrefix = "methodVisitor.visitTryCatchBlock(";
- String catchThrowableSuffix = ", \"java/lang/Throwable\");";
- StringBuilder expected = new StringBuilder();
- List<String> expectedLines = StringUtils.splitLines(content);
- for (String line : expectedLines) {
- if (line.startsWith(catchThrowablePrefix) && line.endsWith(catchThrowableSuffix)) {
- expected.append(line.replace("\"java/lang/Throwable\"", "null"));
- } else {
- expected.append(line);
- }
- expected.append('\n');
- }
- return expected.toString();
- }
-
- private static List<String> getSortedDescriptorList(ArchiveClassFileProvider inputJar) {
- ArrayList<String> descriptorList = new ArrayList<>(inputJar.getClassDescriptors());
- Collections.sort(descriptorList);
- return descriptorList;
- }
-
- public static byte[] getClassAsBytes(ClassFileResourceProvider inputJar, String descriptor)
- throws Exception {
- return toByteArray(inputJar.getProgramResource(descriptor).getByteStream());
- }
-
- public static String asmToString(byte[] clazz) {
- StringWriter stringWriter = new StringWriter();
- printAsm(new PrintWriter(stringWriter), clazz);
- return stringWriter.toString();
- }
-
- public static void printAsm(PrintWriter pw, byte[] clazz) {
- new ClassReader(clazz).accept(new TraceClassVisitor(null, new ASMifierSorted(), pw), 0);
- }
-
- /** Sort methods and fields in the output of ASMifier to make diffing possible. */
- private static class ASMifierSorted extends ASMifier {
- private static class Part implements Comparable<Part> {
-
- private final String key;
- private final int start;
- private final int end;
-
- Part(String key, int start, int end) {
- this.key = key;
- this.start = start;
- this.end = end;
- }
-
- @Override
- public int compareTo(Part part) {
- int i = key.compareTo(part.key);
- return i != 0 ? i : Integer.compare(start, part.start);
- }
- }
-
- private final List<Part> parts = new ArrayList<>();
-
- ASMifierSorted() {
- super(InternalOptions.ASM_VERSION, "cw", 0);
- }
-
- @Override
- public ASMifier visitField(
- int access, String name, String desc, String signature, Object value) {
- init();
- int i = text.size();
- ASMifier res = super.visitField(access, name, desc, signature, value);
- parts.add(new Part((String) text.get(i), i, text.size()));
- return res;
- }
-
- @Override
- public ASMifier visitMethod(
- int access, String name, String desc, String signature, String[] exceptions) {
- init();
- int i = text.size();
- ASMifier res = super.visitMethod(access, name, desc, signature, exceptions);
- parts.add(new Part((String) text.get(i), i, text.size()));
- return res;
- }
-
- private void init() {
- if (parts.isEmpty()) {
- parts.add(new Part("", 0, text.size()));
- }
- }
-
- @Override
- public void print(PrintWriter pw) {
- init();
- int end = parts.get(parts.size() - 1).end;
- Collections.sort(parts);
- parts.add(new Part("", end, text.size()));
- ArrayList<Object> tmp = new ArrayList<>(text);
- text.clear();
- for (Part part : parts) {
- for (int i = part.start; i < part.end; i++) {
- text.add(tmp.get(i));
- }
- }
- super.print(pw);
- }
- }
-}
diff --git a/src/test/java/com/android/tools/r8/L8CommandTest.java b/src/test/java/com/android/tools/r8/L8CommandTest.java
index 6f5f226..a5ec18b 100644
--- a/src/test/java/com/android/tools/r8/L8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/L8CommandTest.java
@@ -192,9 +192,9 @@
assertNotNull(parsedCommand.getR8Command());
InternalOptions internalOptions = parsedCommand.getR8Command().getInternalOptions();
assertNotNull(internalOptions);
- assertTrue(internalOptions.proguardMapConsumer instanceof ProguardMapStringConsumer);
+ assertTrue(internalOptions.mapConsumer instanceof ProguardMapStringConsumer);
ProguardMapStringConsumer mapStringConsumer =
- (ProguardMapStringConsumer) internalOptions.proguardMapConsumer;
+ (ProguardMapStringConsumer) internalOptions.mapConsumer;
FileConsumer proguardMapConsumer = (FileConsumer) mapStringConsumer.getStringConsumer();
assertEquals(pgMap, proguardMapConsumer.getOutputPath());
}
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
index 1cfd1a1..c2a1ba2 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
@@ -26,9 +26,6 @@
public static Collection<String[]> data() {
String[] tests = {
"arithmetic.Arithmetic",
- "bridge.BridgeMethod",
- "catchhandleroverlap.CatchHandlerOverlap",
- "cse.CommonSubexpressionElimination",
"constants.Constants",
"controlflow.ControlFlow",
"conversions.Conversions",
diff --git a/src/test/java/com/android/tools/r8/R8TestBuilder.java b/src/test/java/com/android/tools/r8/R8TestBuilder.java
index 55e0435..b9c6c781 100644
--- a/src/test/java/com/android/tools/r8/R8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/R8TestBuilder.java
@@ -74,13 +74,12 @@
private boolean allowUnusedProguardConfigurationRules = false;
private boolean enableMissingLibraryApiModeling = true;
private CollectingGraphConsumer graphConsumer = null;
- private List<ExternalArtProfile> residualArtProfiles = new ArrayList<>();
- private List<String> keepRules = new ArrayList<>();
- private List<Path> mainDexRulesFiles = new ArrayList<>();
- private List<String> applyMappingMaps = new ArrayList<>();
+ private final List<ExternalArtProfile> residualArtProfiles = new ArrayList<>();
+ private final List<String> keepRules = new ArrayList<>();
+ private final List<Path> mainDexRulesFiles = new ArrayList<>();
+ private final List<String> applyMappingMaps = new ArrayList<>();
private final List<Path> features = new ArrayList<>();
-
- private boolean createDefaultProguardMapConsumer = true;
+ private PartitionMapConsumer partitionMapConsumer = null;
@Override
public boolean isR8TestBuilder() {
@@ -110,20 +109,19 @@
builder.setDisableMinification(true);
}
StringBuilder proguardMapBuilder = new StringBuilder();
- if (createDefaultProguardMapConsumer) {
- builder.setProguardMapConsumer(
- new StringConsumer() {
- @Override
- public void accept(String string, DiagnosticsHandler handler) {
- proguardMapBuilder.append(string);
- }
+ builder.setProguardMapConsumer(
+ new StringConsumer() {
+ @Override
+ public void accept(String string, DiagnosticsHandler handler) {
+ proguardMapBuilder.append(string);
+ }
- @Override
- public void finished(DiagnosticsHandler handler) {
- // Nothing to do.
- }
- });
- }
+ @Override
+ public void finished(DiagnosticsHandler handler) {
+ // Nothing to do.
+ }
+ });
+ builder.setPartitionMapConsumer(partitionMapConsumer);
if (!applyMappingMaps.isEmpty()) {
try {
@@ -162,7 +160,7 @@
app.get(),
box.proguardConfiguration,
box.syntheticProguardRules,
- createDefaultProguardMapConsumer ? proguardMapBuilder.toString() : null,
+ proguardMapBuilder.toString(),
graphConsumer,
getMinApiLevel(),
features,
@@ -803,11 +801,6 @@
return self();
}
- public T noDefaultProguardMapConsumer() {
- createDefaultProguardMapConsumer = false;
- return self();
- }
-
public T addArtProfileForRewriting(ArtProfileProvider artProfileProvider) {
return addArtProfileForRewriting(
artProfileProvider,
@@ -838,4 +831,9 @@
getBuilder().setFakeCompilerVersion(version);
return self();
}
+
+ public T setPartitionMapConsumer(PartitionMapConsumer partitionMapConsumer) {
+ this.partitionMapConsumer = partitionMapConsumer;
+ return self();
+ }
}
diff --git a/src/test/java/com/android/tools/r8/code/PassThroughTest.java b/src/test/java/com/android/tools/r8/code/PassThroughTest.java
deleted file mode 100644
index acbb529..0000000
--- a/src/test/java/com/android/tools/r8/code/PassThroughTest.java
+++ /dev/null
@@ -1,207 +0,0 @@
-// Copyright (c) 2020, 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.code;
-
-import static junit.framework.Assert.assertSame;
-import static org.junit.Assume.assumeTrue;
-
-import com.android.tools.r8.ArchiveClassFileProvider;
-import com.android.tools.r8.CfFrontendExamplesTest;
-import com.android.tools.r8.ClassFileResourceProvider;
-import com.android.tools.r8.DirectoryClassFileProvider;
-import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.TestBase;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestShrinkerBuilder;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.utils.BooleanUtils;
-import com.android.tools.r8.utils.DescriptorUtils;
-import com.android.tools.r8.utils.StringUtils;
-import java.nio.file.Path;
-import java.util.Arrays;
-import java.util.List;
-import java.util.function.Predicate;
-import java.util.stream.Collectors;
-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 PassThroughTest extends TestBase {
-
- private final String EXPECTED = StringUtils.lines("0", "foo", "0", "foo", "foo");
-
- @Parameter(0)
- public TestParameters parameters;
-
- @Parameter(1)
- public boolean keepDebug;
-
- @Parameters(name = "{0}, keep-debug: {1}")
- public static List<Object[]> data() {
- return buildParameters(getTestParameters().withCfRuntimes().build(), BooleanUtils.values());
- }
-
- @Test
- public void testJvm() throws Exception {
- assumeTrue(keepDebug);
- testForJvm(parameters)
- .addProgramClasses(Main.class)
- .run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutput(EXPECTED);
-
- // Check that reading the same input is actual matches.
- ClassFileResourceProvider original =
- DirectoryClassFileProvider.fromDirectory(ToolHelper.getClassPathForTests());
- verifyInstructionsForMethodMatchingExpectation(original, "main", true, true);
- verifyInstructionsForMethodMatchingExpectation(original, "exceptionTest", true, true);
- }
-
- @Test
- public void testR8() throws Exception {
- Path outputJar = temp.newFile("output.jar").toPath();
- testForR8(parameters.getBackend())
- .addProgramClasses(Main.class)
- .addKeepAllClassesRule()
- .enableInliningAnnotations()
- .applyIf(keepDebug, TestShrinkerBuilder::addKeepAllAttributes)
- .compile()
- .writeToZip(outputJar)
- .run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutput(EXPECTED);
- ArchiveClassFileProvider actual = new ArchiveClassFileProvider(outputJar);
- verifyInstructionsForMethodMatchingExpectation(actual, "main", keepDebug, false);
- verifyInstructionsForMethodMatchingExpectation(actual, "exceptionTest", keepDebug, false);
- }
-
- @Test
- public void testR8ByteCodePassThrough() throws Exception {
- Path outputJar = temp.newFile("output.jar").toPath();
- testForR8(parameters.getBackend())
- .addProgramClasses(Main.class)
- .addKeepAllClassesRule()
- .enableInliningAnnotations()
- .applyIf(keepDebug, TestShrinkerBuilder::addKeepAllAttributes)
- .addOptionsModification(
- internalOptions -> {
- internalOptions.testing.cfByteCodePassThrough =
- method -> !method.name.toString().equals("<init>");
- })
- .compile()
- .writeToZip(outputJar)
- .run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutput(EXPECTED);
- ArchiveClassFileProvider actual = new ArchiveClassFileProvider(outputJar);
- verifyInstructionsForMethodMatchingExpectation(actual, "main", keepDebug, true);
- verifyInstructionsForMethodMatchingExpectation(actual, "exceptionTest", keepDebug, true);
- }
-
- private void verifyInstructionsForMethodMatchingExpectation(
- ClassFileResourceProvider actual, String methodName, boolean checkDebug, boolean expectation)
- throws Exception {
- ClassFileResourceProvider original =
- DirectoryClassFileProvider.fromDirectory(ToolHelper.getClassPathForTests());
- String descriptor = DescriptorUtils.javaTypeToDescriptor(Main.class.getTypeName());
- byte[] expectedBytes = CfFrontendExamplesTest.getClassAsBytes(original, descriptor);
- byte[] actualBytes = CfFrontendExamplesTest.getClassAsBytes(actual, descriptor);
- if (!Arrays.equals(expectedBytes, actualBytes)) {
- String expectedString = CfFrontendExamplesTest.asmToString(expectedBytes);
- String actualString = CfFrontendExamplesTest.asmToString(actualBytes);
- verifyInstructionsForMethodMatchingExpectation(
- getMethodInstructions(expectedString, methodName),
- getMethodInstructions(actualString, methodName),
- checkDebug,
- expectation);
- }
- }
-
- private String getMethodInstructions(String asm, String methodName) {
- int methodIndexStart =
- asm.indexOf(
- "methodVisitor = classWriter.visitMethod(ACC_PUBLIC | ACC_STATIC, \""
- + methodName
- + "\",");
- methodIndexStart = asm.indexOf("methodVisitor.visitCode();", methodIndexStart);
- int methodIndexEnd = asm.indexOf("methodVisitor.visitEnd();", methodIndexStart);
- return asm.substring(methodIndexStart, methodIndexEnd);
- }
-
- private void verifyInstructionsForMethodMatchingExpectation(
- String originalInstructions,
- String actualInstructions,
- boolean checkDebug,
- boolean expectation) {
- if (checkDebug) {
- // We may rewrite jump instructions, so filter those out.
- originalInstructions = filter(originalInstructions, this::isNotLabelOrJumpInstruction);
- actualInstructions = filter(actualInstructions, this::isNotLabelOrJumpInstruction);
- } else {
- originalInstructions = filter(originalInstructions, this::isNotDebugInstructionOrJump);
- actualInstructions = filter(actualInstructions, this::isNotLabelOrJumpInstruction);
- }
- assertSame(expectation, actualInstructions.equals(originalInstructions));
- }
-
- private String filter(String instructions, Predicate<String> predicate) {
- return StringUtils.splitLines(instructions).stream()
- .filter(predicate)
- .map(instr -> instr + "\n")
- .collect(Collectors.joining());
- }
-
- private boolean isNotDebugInstructionOrJump(String instruction) {
- return !(instruction.startsWith("methodVisitor.visitLocalVariable")
- || instruction.startsWith("methodVisitor.visitLabel")
- || instruction.startsWith("Label")
- || instruction.startsWith("methodVisitor.visitLineNumber")
- || instruction.startsWith("methodVisitor.visitJumpInsn"));
- }
-
- private boolean isNotLabelOrJumpInstruction(String instruction) {
- return !(instruction.startsWith("Label")
- || instruction.startsWith("methodVisitor.visitJumpInsn")
- || instruction.startsWith("methodVisitor.visitLabel"));
- }
-
- public static class Main {
-
- public static void main(String[] args) {
- int i = 0;
- System.out.println(i);
- int j = 0;
- String foo = "foo";
- // Keep the false to have R8 remove it.
- if (false) {
- System.out.println(foo);
- }
- System.out.println(foo);
- System.out.println(j);
- System.out.println(phiTest(args.length > 0 ? args[0] : null));
- System.out.println(exceptionTest(args.length > 0 ? args[0] : null));
- }
-
- @NeverInline
- public static String phiTest(String arg) {
- String result;
- if (arg == null) {
- result = "foo";
- } else {
- result = "bar";
- }
- return result;
- }
-
- @NeverInline
- public static String exceptionTest(String arg) {
- try {
- return arg.toLowerCase();
- } catch (NullPointerException ignored) {
- return "foo";
- }
- }
- }
-}
diff --git a/src/test/java/com/android/tools/r8/debug/ExamplesDebugTest.java b/src/test/java/com/android/tools/r8/debug/ExamplesDebugTest.java
index 76f9758..563fc09 100644
--- a/src/test/java/com/android/tools/r8/debug/ExamplesDebugTest.java
+++ b/src/test/java/com/android/tools/r8/debug/ExamplesDebugTest.java
@@ -60,16 +60,6 @@
}
@Test
- public void testBridgeMethod() throws Exception {
- testDebugging("bridge", "BridgeMethod");
- }
-
- @Test
- public void testCommonSubexpressionElimination() throws Exception {
- testDebugging("cse", "CommonSubexpressionElimination");
- }
-
- @Test
public void testConstants() throws Exception {
testDebugging("constants", "Constants");
}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/PartialDesugaringTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/PartialDesugaringTest.java
index eebc725..1872678 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/PartialDesugaringTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/PartialDesugaringTest.java
@@ -129,9 +129,12 @@
@Test
public void test() throws Exception {
+ InternalOptions options = new InternalOptions();
+ options
+ .getArtProfileOptions()
+ .setAllowReadingEmptyArtProfileProvidersMultipleTimesForTesting(true);
SupportedClasses supportedClasses =
- new SupportedClassesGenerator(
- new InternalOptions(), ToolHelper.getAndroidJar(AndroidApiLevel.U))
+ new SupportedClassesGenerator(options, ToolHelper.getAndroidJar(AndroidApiLevel.U))
.run(librarySpecification.getDesugarJdkLibs(), librarySpecification.getSpecification());
for (AndroidApiLevel api : getRelevantApiLevels()) {
diff --git a/src/test/java/com/android/tools/r8/desugar/sealed/SealedAttributeClasspathTest.java b/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesClasspathTest.java
similarity index 96%
rename from src/test/java/com/android/tools/r8/desugar/sealed/SealedAttributeClasspathTest.java
rename to src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesClasspathTest.java
index 7661218..663c241 100644
--- a/src/test/java/com/android/tools/r8/desugar/sealed/SealedAttributeClasspathTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesClasspathTest.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.desugar.sealed;
-
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestCompilerBuilder;
import com.android.tools.r8.TestParameters;
@@ -17,7 +16,7 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class SealedAttributeClasspathTest extends TestBase {
+public class SealedClassesClasspathTest extends TestBase {
@Parameter(0)
public TestParameters parameters;
diff --git a/src/test/java/com/android/tools/r8/desugar/sealed/SealedAttributeTest.java b/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesEnumJdk17CompiledTest.java
similarity index 80%
copy from src/test/java/com/android/tools/r8/desugar/sealed/SealedAttributeTest.java
copy to src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesEnumJdk17CompiledTest.java
index 2fc66f1..16009e6 100644
--- a/src/test/java/com/android/tools/r8/desugar/sealed/SealedAttributeTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesEnumJdk17CompiledTest.java
@@ -1,4 +1,4 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
@@ -16,7 +16,7 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.TestRuntime.CfVm;
-import com.android.tools.r8.examples.jdk17.Sealed;
+import com.android.tools.r8.examples.jdk17.EnumSealed;
import com.android.tools.r8.utils.StringUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -25,12 +25,12 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class SealedAttributeTest extends TestBase {
+public class SealedClassesEnumJdk17CompiledTest extends TestBase {
@Parameter(0)
public TestParameters parameters;
- static final String EXPECTED = StringUtils.lines("R8 compiler", "D8 compiler");
+ static final String EXPECTED = StringUtils.lines("A", "a B");
@Parameters(name = "{0}")
public static TestParametersCollection data() {
@@ -42,16 +42,16 @@
parameters.assumeJvmTestParameters();
assumeTrue(parameters.asCfRuntime().isNewerThanOrEqual(CfVm.JDK17));
testForJvm(parameters)
- .addRunClasspathFiles(Sealed.jar())
- .run(parameters.getRuntime(), Sealed.Main.typeName())
+ .addRunClasspathFiles(EnumSealed.jar())
+ .run(parameters.getRuntime(), EnumSealed.Main.typeName())
.assertSuccessWithOutput(EXPECTED);
}
@Test
public void testDesugaring() throws Exception {
testForDesugaring(parameters)
- .addProgramFiles(Sealed.jar())
- .run(parameters.getRuntime(), Sealed.Main.typeName())
+ .addProgramFiles(EnumSealed.jar())
+ .run(parameters.getRuntime(), EnumSealed.Main.typeName())
.applyIf(
c ->
DesugarTestConfiguration.isNotJavac(c)
@@ -65,9 +65,9 @@
parameters.assumeR8TestParameters();
R8FullTestBuilder builder =
testForR8(parameters.getBackend())
- .addProgramFiles(Sealed.jar())
+ .addProgramFiles(EnumSealed.jar())
.setMinApi(parameters)
- .addKeepMainRule(Sealed.Main.typeName());
+ .addKeepMainRule(EnumSealed.Main.typeName());
if (parameters.isCfRuntime()) {
assertThrows(
CompilationFailedException.class,
@@ -80,7 +80,7 @@
"Sealed classes are not supported as program classes")))));
} else {
builder
- .run(parameters.getRuntime(), Sealed.Main.typeName())
+ .run(parameters.getRuntime(), EnumSealed.Main.typeName())
.assertSuccessWithOutput(EXPECTED);
}
}
diff --git a/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesIllegalSubclassTest.java b/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesIllegalSubclassTest.java
new file mode 100644
index 0000000..330da7e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesIllegalSubclassTest.java
@@ -0,0 +1,118 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.desugar.sealed;
+
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+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.DesugarTestConfiguration;
+import com.android.tools.r8.R8FullTestBuilder;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestBuilder;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.utils.StringUtils;
+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 SealedClassesIllegalSubclassTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ static final String EXPECTED = StringUtils.lines("Success!");
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
+ }
+
+ private void addTestClasses(TestBuilder<?, ?> builder) throws Exception {
+ builder
+ .addProgramClasses(TestClass.class, Sub1.class, Sub2.class, Sub3.class)
+ .addProgramClassFileData(getTransformedClasses());
+ }
+
+ @Test
+ public void testJvm() throws Exception {
+ parameters.assumeJvmTestParameters();
+ assumeTrue(parameters.asCfRuntime().isNewerThanOrEqual(CfVm.JDK17));
+ testForJvm(parameters)
+ .apply(this::addTestClasses)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertFailureWithErrorThatMatches(containsString("cannot inherit from sealed class"));
+ }
+
+ @Test
+ public void testDesugaring() throws Exception {
+ testForDesugaring(parameters)
+ .apply(this::addTestClasses)
+ .run(parameters.getRuntime(), TestClass.class)
+ .applyIf(
+ DesugarTestConfiguration::isNotJavac,
+ r -> r.assertSuccessWithOutput(EXPECTED),
+ c -> parameters.getRuntime().asCf().isNewerThanOrEqual(CfVm.JDK17),
+ r ->
+ r.assertFailureWithErrorThatMatches(
+ containsString("cannot inherit from sealed class")),
+ r -> r.assertFailureWithErrorThatThrows(UnsupportedClassVersionError.class));
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ parameters.assumeR8TestParameters();
+ R8FullTestBuilder builder =
+ testForR8(parameters.getBackend())
+ .apply(this::addTestClasses)
+ .setMinApi(parameters)
+ .addKeepMainRule(TestClass.class);
+ if (parameters.isCfRuntime()) {
+ // TODO(b/227160052): Support sealed classes for R8 class file output.
+ assertThrows(
+ CompilationFailedException.class,
+ () ->
+ builder.compileWithExpectedDiagnostics(
+ diagnostics ->
+ diagnostics.assertErrorThatMatches(
+ diagnosticMessage(
+ containsString(
+ "Sealed classes are not supported as program classes")))));
+ } else {
+ builder
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Success!");
+ }
+ }
+
+ public byte[] getTransformedClasses() throws Exception {
+ return transformer(C.class).setPermittedSubclasses(C.class, Sub1.class, Sub2.class).transform();
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ new Sub1();
+ new Sub2();
+ new Sub3();
+ System.out.println("Success!");
+ }
+ }
+
+ abstract static class C /* permits Sub1, Sub2 */ {}
+
+ static class Sub1 extends C {}
+
+ static class Sub2 extends C {}
+
+ static class Sub3 extends C {}
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/sealed/SealedAttributeTest.java b/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesJdk17CompiledTest.java
similarity index 97%
rename from src/test/java/com/android/tools/r8/desugar/sealed/SealedAttributeTest.java
rename to src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesJdk17CompiledTest.java
index 2fc66f1..2a6eee8 100644
--- a/src/test/java/com/android/tools/r8/desugar/sealed/SealedAttributeTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesJdk17CompiledTest.java
@@ -25,7 +25,7 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class SealedAttributeTest extends TestBase {
+public class SealedClassesJdk17CompiledTest extends TestBase {
@Parameter(0)
public TestParameters parameters;
diff --git a/src/test/java/com/android/tools/r8/desugar/sealed/SealedAttributeLibraryTest.java b/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesLibraryTest.java
similarity index 96%
rename from src/test/java/com/android/tools/r8/desugar/sealed/SealedAttributeLibraryTest.java
rename to src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesLibraryTest.java
index e8217c9..e94bcec 100644
--- a/src/test/java/com/android/tools/r8/desugar/sealed/SealedAttributeLibraryTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesLibraryTest.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.desugar.sealed;
-
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestCompilerBuilder;
import com.android.tools.r8.TestParameters;
@@ -17,7 +16,7 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class SealedAttributeLibraryTest extends TestBase {
+public class SealedClassesLibraryTest extends TestBase {
@Parameter(0)
public TestParameters parameters;
diff --git a/src/test/java/com/android/tools/r8/desugar/sealed/SealedAttributeTest.java b/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesTest.java
similarity index 66%
copy from src/test/java/com/android/tools/r8/desugar/sealed/SealedAttributeTest.java
copy to src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesTest.java
index 2fc66f1..420c077 100644
--- a/src/test/java/com/android/tools/r8/desugar/sealed/SealedAttributeTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesTest.java
@@ -1,4 +1,4 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
@@ -13,10 +13,10 @@
import com.android.tools.r8.DesugarTestConfiguration;
import com.android.tools.r8.R8FullTestBuilder;
import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestBuilder;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.TestRuntime.CfVm;
-import com.android.tools.r8.examples.jdk17.Sealed;
import com.android.tools.r8.utils.StringUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -25,33 +25,39 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class SealedAttributeTest extends TestBase {
+public class SealedClassesTest extends TestBase {
@Parameter(0)
public TestParameters parameters;
- static final String EXPECTED = StringUtils.lines("R8 compiler", "D8 compiler");
+ static final String EXPECTED = StringUtils.lines("Success!");
@Parameters(name = "{0}")
public static TestParametersCollection data() {
return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
}
+ private void addTestClasses(TestBuilder<?, ?> builder) throws Exception {
+ builder
+ .addProgramClasses(TestClass.class, Sub1.class, Sub2.class)
+ .addProgramClassFileData(getTransformedClasses());
+ }
+
@Test
public void testJvm() throws Exception {
parameters.assumeJvmTestParameters();
assumeTrue(parameters.asCfRuntime().isNewerThanOrEqual(CfVm.JDK17));
testForJvm(parameters)
- .addRunClasspathFiles(Sealed.jar())
- .run(parameters.getRuntime(), Sealed.Main.typeName())
+ .apply(this::addTestClasses)
+ .run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutput(EXPECTED);
}
@Test
public void testDesugaring() throws Exception {
testForDesugaring(parameters)
- .addProgramFiles(Sealed.jar())
- .run(parameters.getRuntime(), Sealed.Main.typeName())
+ .apply(this::addTestClasses)
+ .run(parameters.getRuntime(), TestClass.class)
.applyIf(
c ->
DesugarTestConfiguration.isNotJavac(c)
@@ -65,10 +71,11 @@
parameters.assumeR8TestParameters();
R8FullTestBuilder builder =
testForR8(parameters.getBackend())
- .addProgramFiles(Sealed.jar())
+ .apply(this::addTestClasses)
.setMinApi(parameters)
- .addKeepMainRule(Sealed.Main.typeName());
+ .addKeepMainRule(TestClass.class);
if (parameters.isCfRuntime()) {
+ // TODO(b/227160052): Support sealed classes for R8 class file output.
assertThrows(
CompilationFailedException.class,
() ->
@@ -79,9 +86,26 @@
containsString(
"Sealed classes are not supported as program classes")))));
} else {
- builder
- .run(parameters.getRuntime(), Sealed.Main.typeName())
- .assertSuccessWithOutput(EXPECTED);
+ builder.run(parameters.getRuntime(), TestClass.class).assertSuccessWithOutput(EXPECTED);
}
}
+
+ public byte[] getTransformedClasses() throws Exception {
+ return transformer(C.class).setPermittedSubclasses(C.class, Sub1.class, Sub2.class).transform();
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ new Sub1();
+ new Sub2();
+ System.out.println("Success!");
+ }
+ }
+
+ abstract static class C /* permits Sub1, Sub2 */ {}
+
+ static class Sub1 extends C {}
+
+ static class Sub2 extends C {}
}
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/OverloadingEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/OverloadingEnumUnboxingTest.java
index d9e84f4..5d47744 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/OverloadingEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/OverloadingEnumUnboxingTest.java
@@ -45,7 +45,6 @@
.enableInliningAnnotations()
.addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
.setMinApi(parameters)
- .compile()
.run(parameters.getRuntime(), classToTest)
.assertSuccess()
.inspectStdOut(this::assertLines2By2Correct);
@@ -83,119 +82,143 @@
@NeverInline
private static void constructorTest() {
- new TestClass(42);
- System.out.println("42");
+ constructorOutline();
new TestClass(MyEnum1.A);
- System.out.println("0");
+ System.out.println("1");
new TestClass(MyEnum1.B);
- System.out.println("1");
+ System.out.println("2");
new TestClass(MyEnum2.A);
- System.out.println("0");
+ System.out.println("2");
new TestClass(MyEnum2.B);
- System.out.println("1");
+ System.out.println("3");
new TestClass(MyEnum3.A);
- System.out.println("0");
+ System.out.println("3");
new TestClass(MyEnum3.B);
- System.out.println("1");
+ System.out.println("4");
+ }
+
+ @NeverInline
+ private static void constructorOutline() {
+ // This method is outlined so it is not reprocessed in the second round of IR processing.
+ new TestClass(38);
+ System.out.println("42");
+ new TestClass(40);
+ System.out.println("44");
}
@NeverInline
private static void staticTest() {
- staticMethod(42);
- System.out.println("42");
+ staticOutline();
staticMethod(MyEnum1.A);
- System.out.println("0");
+ System.out.println("1");
staticMethod(MyEnum1.B);
- System.out.println("1");
+ System.out.println("2");
staticMethod(MyEnum2.A);
- System.out.println("0");
+ System.out.println("2");
staticMethod(MyEnum2.B);
- System.out.println("1");
+ System.out.println("3");
staticMethod(MyEnum3.A);
- System.out.println("0");
+ System.out.println("3");
staticMethod(MyEnum3.B);
- System.out.println("1");
+ System.out.println("4");
+ }
+
+ @NeverInline
+ private static void staticOutline() {
+ // This method is outlined so it is not reprocessed in the second round of IR processing.
+ staticMethod(38);
+ System.out.println("42");
+ staticMethod(40);
+ System.out.println("44");
}
@NeverInline
private static void virtualTest() {
TestClass testClass = new TestClass();
- testClass.virtualMethod(42);
- System.out.println("42");
+ virtualOutline(testClass);
testClass.virtualMethod(MyEnum1.A);
- System.out.println("0");
+ System.out.println("1");
testClass.virtualMethod(MyEnum1.B);
- System.out.println("1");
+ System.out.println("2");
testClass.virtualMethod(MyEnum2.A);
- System.out.println("0");
+ System.out.println("2");
testClass.virtualMethod(MyEnum2.B);
- System.out.println("1");
+ System.out.println("3");
testClass.virtualMethod(MyEnum3.A);
- System.out.println("0");
+ System.out.println("3");
testClass.virtualMethod(MyEnum3.B);
- System.out.println("1");
+ System.out.println("4");
+ }
+
+ @NeverInline
+ private static void virtualOutline(TestClass testClass) {
+ // This method is outlined so it is not reprocessed in the second round of IR processing.
+ testClass.virtualMethod(38);
+ System.out.println("42");
+ testClass.virtualMethod(40);
+ System.out.println("44");
}
public TestClass() {}
@NeverInline
public TestClass(MyEnum1 e1) {
- System.out.println(e1.ordinal());
+ System.out.println(e1.ordinal() + 1);
}
@NeverInline
public TestClass(MyEnum2 e2) {
- System.out.println(e2.ordinal());
+ System.out.println(e2.ordinal() + 2);
}
@NeverInline
public TestClass(MyEnum3 e3) {
- System.out.println(e3.ordinal());
+ System.out.println(e3.ordinal() + 3);
}
@NeverInline
public TestClass(int i) {
- System.out.println(i);
+ System.out.println(i + 4);
}
@NeverInline
public void virtualMethod(MyEnum1 e1) {
- System.out.println(e1.ordinal());
+ System.out.println(e1.ordinal() + 1);
}
@NeverInline
public void virtualMethod(MyEnum2 e2) {
- System.out.println(e2.ordinal());
+ System.out.println(e2.ordinal() + 2);
}
@NeverInline
public void virtualMethod(MyEnum3 e3) {
- System.out.println(e3.ordinal());
+ System.out.println(e3.ordinal() + 3);
}
@NeverInline
public void virtualMethod(int i) {
- System.out.println(i);
+ System.out.println(i + 4);
}
@NeverInline
public static void staticMethod(MyEnum1 e1) {
- System.out.println(e1.ordinal());
+ System.out.println(e1.ordinal() + 1);
}
@NeverInline
public static void staticMethod(MyEnum2 e2) {
- System.out.println(e2.ordinal());
+ System.out.println(e2.ordinal() + 2);
}
@NeverInline
public static void staticMethod(MyEnum3 e3) {
- System.out.println(e3.ordinal());
+ System.out.println(e3.ordinal() + 3);
}
@NeverInline
public static void staticMethod(int i) {
- System.out.println(i);
+ System.out.println(i + 4);
}
}
}
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/VirtualMethodOverrideEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/VirtualMethodOverrideEnumUnboxingTest.java
index d14fe97..1719dd9 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/VirtualMethodOverrideEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/VirtualMethodOverrideEnumUnboxingTest.java
@@ -1,16 +1,16 @@
// Copyright (c) 2020, 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.enumunboxing;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import com.android.tools.r8.KeepConstantArguments;
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.NoMethodStaticizing;
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestParameters;
@@ -52,6 +52,7 @@
.enableNeverClassInliningAnnotations()
.enableNoMethodStaticizingAnnotations()
.enableNoVerticalClassMergingAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
.addOptionsModification(options -> enableEnumOptions(options, enumValueOptimization))
.addEnumUnboxingInspector(inspector -> inspector.assertUnboxed(MyEnum.class))
.setMinApi(parameters)
@@ -59,45 +60,97 @@
.inspect(this::inspect)
.run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutputLines(
- "A.m(A : MyEnum, 42 : int)", "B.m(A : MyEnum, 42 : int)", "B.m(42 : int, A : MyEnum)");
+ "Middle.m(A : MyEnum, 42 : int)",
+ "Bottom.m(A : MyEnum, 42 : int)",
+ "Bottom.m(42 : int, A : MyEnum)",
+ "Middle.m(A : MyEnum, 42 : int)",
+ "Middle2.m(A : MyEnum, 42 : int)",
+ "Middle.m(A : MyEnum, 42 : int)",
+ "Bottom.m(A : MyEnum, 42 : int)",
+ "Middle.m(A : MyEnum, 42 : int)",
+ "Middle2.m(A : MyEnum, 42 : int)",
+ "Something");
}
private void inspect(CodeInspector inspector) {
- MethodSubject methodOnA = inspector.clazz(A.class).virtualMethods().get(0);
- MethodSubject methodOnB =
- inspector.clazz(B.class).uniqueMethodWithFinalName(methodOnA.getFinalName());
- assertThat(methodOnB, isPresent());
- // TODO(b/171784168): Should be true.
- assertFalse(methodOnB.streamInstructions().anyMatch(x -> x.asDexInstruction().isInvokeSuper()));
+ MethodSubject methodOnMiddle = inspector.clazz(Middle.class).virtualMethods().get(0);
+ MethodSubject methodOnBottom =
+ inspector.clazz(Bottom.class).uniqueMethodWithFinalName(methodOnMiddle.getFinalName());
+ assertThat(methodOnBottom, isPresent());
+ assertTrue(
+ methodOnBottom
+ .streamInstructions()
+ .anyMatch(
+ i ->
+ i.asDexInstruction().isInvokeSuper()
+ && i.getMethod() == methodOnMiddle.getMethod().getReference()));
}
static class TestClass {
public static void main(String[] args) {
MyEnum value = System.currentTimeMillis() > 0 ? MyEnum.A : MyEnum.B;
- new B().m(value, 42);
- new B().m(42, value);
+ new Bottom().m(value, 42);
+ new Bottom().m(42, value);
+ new Middle().m(value, 42);
+ new Middle2().m(value, 42);
+ fromTop(new Bottom(), value);
+ fromTop(new Middle(), value);
+ fromTop(new Middle2(), value);
+ new Middle().checkNotNullOrPrintSomething(value);
+ new Bottom().checkNotNullOrPrintSomething(value);
+ }
+
+ @NeverInline
+ public static void fromTop(Top top, MyEnum value) {
+ top.m(value, 42);
}
}
@NeverClassInline
@NoVerticalClassMerging
- static class A {
+ abstract static class Top {
@NeverInline
+ abstract void m(MyEnum x, int y);
+ }
+
+ @NeverClassInline
+ @NoVerticalClassMerging
+ @NoHorizontalClassMerging
+ static class Middle2 extends Top {
+ @NeverInline
void m(MyEnum x, int y) {
- System.out.println("A.m(" + x.toString() + " : MyEnum, " + y + " : int)");
+ System.out.println("Middle2.m(" + x.toString() + " : MyEnum, " + y + " : int)");
}
}
@NeverClassInline
- static class B extends A {
+ @NoVerticalClassMerging
+ @NoHorizontalClassMerging
+ static class Middle extends Top {
+
+ @NeverInline
+ void m(MyEnum x, int y) {
+ System.out.println("Middle.m(" + x.toString() + " : MyEnum, " + y + " : int)");
+ }
+
+ @NeverInline
+ void checkNotNullOrPrintSomething(MyEnum e) {
+ if (e == null) {
+ throw new NullPointerException();
+ }
+ }
+ }
+
+ @NeverClassInline
+ static class Bottom extends Middle {
@KeepConstantArguments
@NeverInline
@NoMethodStaticizing
void m(int x, MyEnum y) {
- System.out.println("B.m(" + x + " : int, " + y.toString() + " : MyEnum)");
+ System.out.println("Bottom.m(" + x + " : int, " + y.toString() + " : MyEnum)");
}
@KeepConstantArguments
@@ -105,7 +158,14 @@
@Override
void m(MyEnum x, int y) {
super.m(x, y);
- System.out.println("B.m(" + x.toString() + " : MyEnum, " + y + " : int)");
+ System.out.println("Bottom.m(" + x.toString() + " : MyEnum, " + y + " : int)");
+ }
+
+ @KeepConstantArguments
+ @NeverInline
+ @Override
+ void checkNotNullOrPrintSomething(MyEnum e) {
+ System.out.println("Something");
}
}
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/enummerging/AbstractEnumMergingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/enummerging/AbstractEnumMergingTest.java
new file mode 100644
index 0000000..9335996
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/enumunboxing/enummerging/AbstractEnumMergingTest.java
@@ -0,0 +1,87 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.enumunboxing.enummerging;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.enumunboxing.EnumUnboxingTestBase;
+import java.util.List;
+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 AbstractEnumMergingTest extends EnumUnboxingTestBase {
+
+ private final TestParameters parameters;
+ private final boolean enumValueOptimization;
+ private final EnumKeepRules enumKeepRules;
+
+ @Parameters(name = "{0} valueOpt: {1} keep: {2}")
+ public static List<Object[]> data() {
+ return enumUnboxingTestParameters();
+ }
+
+ public AbstractEnumMergingTest(
+ TestParameters parameters, boolean enumValueOptimization, EnumKeepRules enumKeepRules) {
+ this.parameters = parameters;
+ this.enumValueOptimization = enumValueOptimization;
+ this.enumKeepRules = enumKeepRules;
+ }
+
+ @Test
+ public void testEnumUnboxing() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addKeepRules(enumKeepRules.getKeepRules())
+ .addOptionsModification(opt -> opt.testing.enableEnumWithSubtypesUnboxing = true)
+ .addEnumUnboxingInspector(inspector -> inspector.assertUnboxed(MyEnum.class))
+ .enableInliningAnnotations()
+ .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
+ .setMinApi(parameters)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("336", "74", "96", "44");
+ }
+
+ enum MyEnum {
+ A(8) {
+ @NeverInline
+ @Override
+ public long operate(long another) {
+ return num * another;
+ }
+ },
+ B(32) {
+ @NeverInline
+ @Override
+ public long operate(long another) {
+ return num + another;
+ }
+ };
+ final long num;
+
+ MyEnum(long num) {
+ this.num = num;
+ }
+
+ public abstract long operate(long another);
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ System.out.println(MyEnum.A.operate(42));
+ System.out.println(MyEnum.B.operate(42));
+ System.out.println(indirect(MyEnum.A));
+ System.out.println(indirect(MyEnum.B));
+ }
+
+ @NeverInline
+ public static long indirect(MyEnum e) {
+ return e.operate(12);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/enummerging/BasicEnumMergingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/enummerging/BasicEnumMergingTest.java
index 56f3d9e..0eb13fb 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/enummerging/BasicEnumMergingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/enummerging/BasicEnumMergingTest.java
@@ -48,9 +48,7 @@
.assertNotUnboxed(EnumWithVirtualOverrideAndPrivateField.class))
.enableInliningAnnotations()
.addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
- .addOptionsModification(opt -> opt.testing.enableEnumUnboxingDebugLogs = true)
.setMinApi(parameters)
- .allowDiagnosticInfoMessages()
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines("a", "B", "a", "B", "a", "B", "A 1 1.0 A", "B");
}
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/enummerging/DeterministicEnumMergingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/enummerging/DeterministicEnumMergingTest.java
new file mode 100644
index 0000000..00bb1cf
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/enumunboxing/enummerging/DeterministicEnumMergingTest.java
@@ -0,0 +1,125 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.enumunboxing.enummerging;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.enumunboxing.EnumUnboxingTestBase;
+import java.util.List;
+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 DeterministicEnumMergingTest extends EnumUnboxingTestBase {
+
+ private final TestParameters parameters;
+ private final boolean enumValueOptimization;
+ private final EnumKeepRules enumKeepRules;
+
+ @Parameters(name = "{0} valueOpt: {1} keep: {2}")
+ public static List<Object[]> data() {
+ return enumUnboxingTestParameters();
+ }
+
+ public DeterministicEnumMergingTest(
+ TestParameters parameters, boolean enumValueOptimization, EnumKeepRules enumKeepRules) {
+ this.parameters = parameters;
+ this.enumValueOptimization = enumValueOptimization;
+ this.enumKeepRules = enumKeepRules;
+ }
+
+ @Test
+ public void testEnumUnboxingDeterminism() throws Exception {
+ R8TestCompileResult compile1 =
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addKeepRules(enumKeepRules.getKeepRules())
+ .addOptionsModification(opt -> opt.testing.enableEnumWithSubtypesUnboxing = true)
+ .addEnumUnboxingInspector(inspector -> inspector.assertUnboxed(MyEnum.class))
+ .enableInliningAnnotations()
+ .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
+ .setMinApi(parameters)
+ .compile();
+ R8TestCompileResult compile2 =
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addKeepRules(enumKeepRules.getKeepRules())
+ .addOptionsModification(opt -> opt.testing.enableEnumWithSubtypesUnboxing = true)
+ .addEnumUnboxingInspector(inspector -> inspector.assertUnboxed(MyEnum.class))
+ .enableInliningAnnotations()
+ .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
+ .setMinApi(parameters)
+ .compile();
+ assertIdenticalInspectors(compile1.inspector(), compile2.inspector());
+ compile1
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("336", "74", "22", "10794", "96", "44", "52", "3084");
+ }
+
+ enum MyEnum {
+ // The more cases, the highest the chance to trigger non determinism.
+ // The operate methods have to be moved in the same order, and the dispatch method has to
+ // be generated in the same order too.
+ A(8) {
+ @NeverInline
+ @Override
+ public long operate(long another) {
+ return num * another;
+ }
+ },
+ B(32) {
+ @NeverInline
+ @Override
+ public long operate(long another) {
+ return num + another;
+ }
+ },
+ C(64) {
+ @NeverInline
+ @Override
+ public long operate(long another) {
+ return num - another;
+ }
+ },
+ D(256) {
+
+ @NeverInline
+ @Override
+ public long operate(long another) {
+ return num * another + another;
+ }
+ };
+ final long num;
+
+ MyEnum(long num) {
+ this.num = num;
+ }
+
+ public abstract long operate(long another);
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ System.out.println(MyEnum.A.operate(42));
+ System.out.println(MyEnum.B.operate(42));
+ System.out.println(MyEnum.C.operate(42));
+ System.out.println(MyEnum.D.operate(42));
+ System.out.println(indirect(MyEnum.A));
+ System.out.println(indirect(MyEnum.B));
+ System.out.println(indirect(MyEnum.C));
+ System.out.println(indirect(MyEnum.D));
+ }
+
+ @NeverInline
+ public static long indirect(MyEnum e) {
+ return e.operate(12);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/examples/ExamplesTestBase.java b/src/test/java/com/android/tools/r8/examples/ExamplesTestBase.java
index 093702c..cd9e112 100644
--- a/src/test/java/com/android/tools/r8/examples/ExamplesTestBase.java
+++ b/src/test/java/com/android/tools/r8/examples/ExamplesTestBase.java
@@ -45,6 +45,7 @@
public void runTestR8() throws Exception {
parameters.assumeR8TestParameters();
testForR8(parameters.getBackend())
+ .addOptionsModification(o -> o.testing.roundtripThroughLir = true)
.setMinApi(parameters)
.addProgramClasses(getTestClasses())
.addKeepMainRule(getMainClass())
@@ -52,7 +53,6 @@
.assertSuccessWithOutput(getExpected());
}
- @Test
public void runTestDebugComparator() throws Exception {
Assume.assumeFalse(ToolHelper.isWindows());
Assume.assumeFalse(
diff --git a/src/test/examples/bridge/BridgeMethod.java b/src/test/java/com/android/tools/r8/examples/bridge/BridgeMethod.java
similarity index 70%
rename from src/test/examples/bridge/BridgeMethod.java
rename to src/test/java/com/android/tools/r8/examples/bridge/BridgeMethod.java
index 9e82a29..685f885 100644
--- a/src/test/examples/bridge/BridgeMethod.java
+++ b/src/test/java/com/android/tools/r8/examples/bridge/BridgeMethod.java
@@ -1,10 +1,11 @@
-// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-package bridge;
+package com.android.tools.r8.examples.bridge;
abstract class Super<T> {
public abstract int method(T t0, T t1);
+
public abstract int rangeMethod(T t0, T t1, T t2, T t3, T t4, T t5);
}
@@ -27,10 +28,12 @@
}
public static void main(String[] args) {
+ int i = 0;
Super<Integer> instance = new BridgeMethod();
- instance.method(1, 2);
- instance.method(2, 1);
- instance.rangeMethod(1, 2, 3, 4, 5, 6);
- instance.rangeMethod(2, 1, 3, 4, 5, 6);
+ i += instance.method(1, 2);
+ i += instance.method(2, 1);
+ i += instance.rangeMethod(1, 2, 3, 4, 5, 6);
+ i += instance.rangeMethod(2, 1, 3, 4, 5, 6);
+ System.out.println(i);
}
}
diff --git a/src/test/java/com/android/tools/r8/examples/bridge/BridgeMethodTestRunner.java b/src/test/java/com/android/tools/r8/examples/bridge/BridgeMethodTestRunner.java
new file mode 100644
index 0000000..47e9945
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/examples/bridge/BridgeMethodTestRunner.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.examples.bridge;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.examples.ExamplesTestBase;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class BridgeMethodTestRunner extends ExamplesTestBase {
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().enableApiLevelsForCf().build();
+ }
+
+ public BridgeMethodTestRunner(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Override
+ public Class<?> getMainClass() {
+ return BridgeMethod.class;
+ }
+
+ @Override
+ public List<Class<?>> getTestClasses() {
+ return ImmutableList.of(BridgeMethod.class, Super.class);
+ }
+
+ @Override
+ public String getExpected() {
+ return StringUtils.lines("26");
+ }
+
+ @Test
+ public void testDesugaring() throws Exception {
+ runTestDesugaring();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ runTestR8();
+ }
+
+ @Test
+ public void testDebug() throws Exception {
+ runTestDebugComparator();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/examples/catchhandleroverlap/CatchHandlerOverlap.java b/src/test/java/com/android/tools/r8/examples/catchhandleroverlap/CatchHandlerOverlap.java
new file mode 100644
index 0000000..c66cd26
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/examples/catchhandleroverlap/CatchHandlerOverlap.java
@@ -0,0 +1,50 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.examples.catchhandleroverlap;
+
+public class CatchHandlerOverlap {
+
+ private static void f() throws Exception {
+ throw new Exception("f");
+ }
+
+ private static void g() throws Exception {
+ throw new Exception("g");
+ }
+
+ private static void h(
+ int i1,
+ int i2,
+ int i3,
+ int i4,
+ int i5,
+ int i6,
+ int i7,
+ int i8,
+ int i9,
+ int i10,
+ int i11,
+ int i12,
+ int i13,
+ int i14,
+ int i15,
+ int i16,
+ int i17) {
+ System.out.println(
+ i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9 + i10 + i11 + i12 + i13 + i14 + i15 + i16 + i17);
+ try {
+ f();
+ } catch (Exception e0) {
+ try {
+ g();
+ } catch (Exception e1) {
+ System.out.println(e0.getMessage() + " " + e1.getMessage());
+ }
+ }
+ }
+
+ public static void main(String[] args) {
+ h(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/examples/catchhandleroverlap/CatchHandlerOverlapTestRunner.java b/src/test/java/com/android/tools/r8/examples/catchhandleroverlap/CatchHandlerOverlapTestRunner.java
new file mode 100644
index 0000000..f8d0e85
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/examples/catchhandleroverlap/CatchHandlerOverlapTestRunner.java
@@ -0,0 +1,50 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.examples.catchhandleroverlap;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.examples.ExamplesTestBase;
+import com.android.tools.r8.utils.StringUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class CatchHandlerOverlapTestRunner extends ExamplesTestBase {
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().enableApiLevelsForCf().build();
+ }
+
+ public CatchHandlerOverlapTestRunner(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Override
+ public Class<?> getMainClass() {
+ return CatchHandlerOverlap.class;
+ }
+
+ @Override
+ public String getExpected() {
+ return StringUtils.lines("153", "f g");
+ }
+
+ @Test
+ public void testDesugaring() throws Exception {
+ runTestDesugaring();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ runTestR8();
+ }
+
+ @Test
+ public void testDebug() throws Exception {
+ runTestDebugComparator();
+ }
+}
diff --git a/src/test/examples/cse/CommonSubexpressionElimination.java b/src/test/java/com/android/tools/r8/examples/cse/CommonSubexpressionElimination.java
similarity index 95%
rename from src/test/examples/cse/CommonSubexpressionElimination.java
rename to src/test/java/com/android/tools/r8/examples/cse/CommonSubexpressionElimination.java
index 3c64a1d..4ae0ba7 100644
--- a/src/test/examples/cse/CommonSubexpressionElimination.java
+++ b/src/test/java/com/android/tools/r8/examples/cse/CommonSubexpressionElimination.java
@@ -1,11 +1,11 @@
-// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// This code is not run directly. It needs to be compiled to dex code.
// 'arithmetic.dex' is what is run.
-package cse;
+package com.android.tools.r8.examples.cse;
public class CommonSubexpressionElimination {
@@ -74,7 +74,6 @@
}
}
-
public static void main(String[] args) {
System.out.println(divNoCatch(1, 0, 1));
System.out.println(divNoCatch2(1, 0, 2));
diff --git a/src/test/java/com/android/tools/r8/examples/cse/CommonSubexpressionEliminationTestRunner.java b/src/test/java/com/android/tools/r8/examples/cse/CommonSubexpressionEliminationTestRunner.java
new file mode 100644
index 0000000..1f50713
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/examples/cse/CommonSubexpressionEliminationTestRunner.java
@@ -0,0 +1,50 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.examples.cse;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.examples.ExamplesTestBase;
+import com.android.tools.r8.utils.StringUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class CommonSubexpressionEliminationTestRunner extends ExamplesTestBase {
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().enableApiLevelsForCf().build();
+ }
+
+ public CommonSubexpressionEliminationTestRunner(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Override
+ public Class<?> getMainClass() {
+ return CommonSubexpressionElimination.class;
+ }
+
+ @Override
+ public String getExpected() {
+ return StringUtils.lines("1", "1", "2 2", "2", "3", "3", "4 4", "4", "A", "B");
+ }
+
+ @Test
+ public void testDesugaring() throws Exception {
+ runTestDesugaring();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ runTestR8();
+ }
+
+ @Test
+ public void testDebug() throws Exception {
+ runTestDebugComparator();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/examples/jdk17/EnumSealed.java b/src/test/java/com/android/tools/r8/examples/jdk17/EnumSealed.java
new file mode 100644
index 0000000..c260155
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/examples/jdk17/EnumSealed.java
@@ -0,0 +1,24 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.examples.jdk17;
+
+import com.android.tools.r8.examples.JavaExampleClassProxy;
+import java.nio.file.Path;
+
+public class EnumSealed {
+
+ private static final String EXAMPLE_FILE = "examplesJava17/enum_sealed";
+
+ public static final JavaExampleClassProxy Enum =
+ new JavaExampleClassProxy(EXAMPLE_FILE, "enum_sealed/Enum");
+ public static final JavaExampleClassProxy EnumB =
+ new JavaExampleClassProxy(EXAMPLE_FILE, "enum_sealed/Enum$1");
+ public static final JavaExampleClassProxy Main =
+ new JavaExampleClassProxy(EXAMPLE_FILE, "enum_sealed/Main");
+
+ public static Path jar() {
+ return JavaExampleClassProxy.examplesJar(EXAMPLE_FILE);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java b/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java
index a376a2e..66adde9 100644
--- a/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java
+++ b/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java
@@ -380,7 +380,7 @@
newReturnBlock.iterator().next().asReturn().returnValue().replaceUsers(newReturnValue);
Instruction constInstruction = new ConstNumber(newConstValue, 10);
Instruction addInstruction =
- new Add(NumericType.INT, newReturnValue, oldReturnValue, newConstValue);
+ Add.create(NumericType.INT, newReturnValue, oldReturnValue, newConstValue);
iterator.previous();
iterator.add(constInstruction);
iterator.add(addInstruction);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/ConstClassCanonicalizationMonitorTest.java b/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/ConstClassCanonicalizationMonitorTest.java
index 5c769f0..a1c1662 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/ConstClassCanonicalizationMonitorTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/ConstClassCanonicalizationMonitorTest.java
@@ -42,10 +42,7 @@
.enableInliningAnnotations()
.addKeepAttributeLineNumberTable()
.addKeepAttributeSourceFile()
- .addOptionsModification(
- options -> {
- options.proguardMapConsumer = null;
- })
+ .addOptionsModification(options -> options.mapConsumer = null)
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines("Hello World!")
.inspect(
diff --git a/src/test/java/com/android/tools/r8/ir/regalloc/IdenticalAfterRegisterAllocationTest.java b/src/test/java/com/android/tools/r8/ir/regalloc/IdenticalAfterRegisterAllocationTest.java
index 5db89dc..983dfdb 100644
--- a/src/test/java/com/android/tools/r8/ir/regalloc/IdenticalAfterRegisterAllocationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/regalloc/IdenticalAfterRegisterAllocationTest.java
@@ -110,9 +110,9 @@
Value value2 = new Value(2, TypeElement.getInt(), null);
ConstNumber const2 = new ConstNumber(value2, 2);
Value value3 = new Value(2, TypeElement.getInt(), null);
- Add add0 = new Add(NumericType.INT, value3, value0, value1);
+ Add add0 = Add.create(NumericType.INT, value3, value0, value1);
add0.setPosition(Position.none());
- Add add1 = new Add(NumericType.INT, value3, value0, value2);
+ Add add1 = Add.create(NumericType.INT, value3, value0, value2);
add1.setPosition(Position.none());
value0.computeNeedsRegister();
assertTrue(value0.needsRegister());
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDelegatedPropertyTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDelegatedPropertyTest.java
index 44a9963..8cfa77f 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDelegatedPropertyTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDelegatedPropertyTest.java
@@ -122,30 +122,20 @@
.addProgramFiles(libJars.getForConfiguration(kotlinc, targetVersion))
.compile()
.writeToZip();
- Path outputPath = temp.newFolder().toPath();
ProcessResult compileResult =
kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion)
.addClasspathFiles(outputJar)
.addSourceFiles(
getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_APP), "main"))
- .setOutputPath(outputPath)
+ .setOutputPath(temp.newFolder().toPath())
.compileRaw();
- if (kotlinParameters.isNewerThan(KOTLINC_1_8_0)) {
- testForJvm(parameters)
- .addRunClasspathFiles(
- kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinReflectJar(), outputJar)
- .addClasspath(outputPath)
- .run(parameters.getRuntime(), PKG_APP + ".MainKt")
- .assertSuccessWithOutputLines(
- "foobar", "property oldName (Kotlin reflection is not available)");
- } else {
- Assert.assertEquals(1, compileResult.exitCode);
- assertThat(
- compileResult.stderr,
- containsString(
- "unsupported [reference to the synthetic extension property for a Java get/set"
- + " method]"));
- }
+ Assert.assertEquals(1, compileResult.exitCode);
+ assertThat(
+ compileResult.stderr,
+ containsString(
+ kotlinParameters.isNewerThan(KOTLINC_1_8_0)
+ ? "references to synthetic java properties"
+ : "reference to the synthetic extension property"));
}
private void inspectMetadata(CodeInspector inspector) {
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTypeResultTest.java b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTypeResultTest.java
index 4d76e2c..72a17ac 100644
--- a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTypeResultTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTypeResultTest.java
@@ -4,20 +4,54 @@
package com.android.tools.r8.retrace.api;
+import static com.android.tools.r8.naming.retrace.StackTrace.isSame;
import static junit.framework.TestCase.assertEquals;
+import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestDiagnosticMessagesImpl;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ThrowingFunction;
+import com.android.tools.r8.naming.retrace.StackTrace;
+import com.android.tools.r8.naming.retrace.StackTrace.StackTraceLine;
+import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.references.TypeReference;
+import com.android.tools.r8.retrace.MappingPartitionMetadata;
+import com.android.tools.r8.retrace.PartitionMappingSupplier;
+import com.android.tools.r8.retrace.PartitionMappingSupplierAsync;
+import com.android.tools.r8.retrace.ProguardMapPartitioner;
import com.android.tools.r8.retrace.ProguardMapProducer;
+import com.android.tools.r8.retrace.Retrace;
+import com.android.tools.r8.retrace.RetraceAsync;
+import com.android.tools.r8.retrace.RetraceAsyncResult;
+import com.android.tools.r8.retrace.RetraceStackTraceContext;
+import com.android.tools.r8.retrace.RetraceStackTraceElementProxy;
+import com.android.tools.r8.retrace.RetraceStackTraceResult;
import com.android.tools.r8.retrace.RetraceTypeElement;
+import com.android.tools.r8.retrace.RetracedMethodReference;
+import com.android.tools.r8.retrace.RetracedMethodReference.KnownRetracedMethodReference;
import com.android.tools.r8.retrace.Retracer;
+import com.android.tools.r8.retrace.StackTraceElementProxy;
+import com.android.tools.r8.retrace.StackTraceLineParser;
+import com.android.tools.r8.retrace.api.RetraceApiTypeResultTest.RetracePartitionStackTraceTest.IdentityStackTraceLineParser;
+import com.android.tools.r8.utils.BooleanBox;
+import com.android.tools.r8.utils.Box;
+import com.android.tools.r8.utils.StringUtils;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
+import java.util.Map;
+import java.util.Set;
import java.util.stream.Collectors;
+import org.junit.Assert;
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 RetraceApiTypeResultTest extends RetraceApiTestBase {
@@ -64,4 +98,391 @@
assertEquals(intArr, collect.get(0).getType().getTypeReference());
}
}
+
+ @RunWith(Parameterized.class)
+ public static class RetracePartitionStackTracePromiseTest extends TestBase {
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ public RetracePartitionStackTracePromiseTest(TestParameters parameters) {
+ parameters.assertNoneRuntime();
+ }
+
+ private final String MAPPING =
+ StringUtils.unixLines(
+ "com.Foo -> a:",
+ " 1:1:void m1():42:42 -> a",
+ "com.Bar -> b:",
+ " 2:2:void m2():43:43 -> b",
+ "com.Baz -> c:",
+ " 3:3:void m3():44:44 -> c");
+
+ public static class Promise<T> {
+
+ private final T value;
+
+ private Promise(T value) {
+ this.value = value;
+ }
+
+ public <R> Promise<R> then(ThrowingFunction<T, R, InterruptedException> f)
+ throws InterruptedException {
+ return new Promise<>(f.apply(value));
+ }
+
+ public <R> Promise<R> thenApply(ThrowingFunction<T, Promise<R>, InterruptedException> f)
+ throws InterruptedException {
+ return f.apply(value);
+ }
+ }
+
+ public static class PromiseRunner<T> {
+
+ public T run(Promise<T> promise) {
+ // Here we are cheating since we know the value is always available.
+ return promise.value;
+ }
+ }
+
+ public static class Storage {
+
+ private final byte[] metadata;
+ private final Map<String, byte[]> partitions;
+
+ public Storage(byte[] metadata, Map<String, byte[]> partitions) {
+ this.metadata = metadata;
+ this.partitions = partitions;
+ }
+
+ public Promise<byte[]> getMetadata() {
+ return new Promise<>(metadata);
+ }
+
+ public Promise<byte[]> getPartition(String key) {
+ return new Promise<>(partitions.getOrDefault(key, new byte[0]));
+ }
+ }
+
+ private Storage buildStorage(TestDiagnosticMessagesImpl diagnosticMessages) throws Exception {
+ Map<String, byte[]> partitions = new HashMap<>();
+ MappingPartitionMetadata metadataPromise =
+ ProguardMapPartitioner.builder(diagnosticMessages)
+ .setProguardMapProducer(ProguardMapProducer.fromString(MAPPING))
+ .setPartitionConsumer(
+ partition -> partitions.put(partition.getKey(), partition.getPayload()))
+ .build()
+ .run();
+ return new Storage(metadataPromise.getBytes(), partitions);
+ }
+
+ private static <T, R> Promise<Map<T, R>> transpose(Map<T, Promise<R>> promiseMap)
+ throws InterruptedException {
+ Map<T, R> resolvedMap = new HashMap<>();
+ Box<InterruptedException> interruptedException = new Box<>();
+ promiseMap.forEach(
+ (key, promise) -> {
+ try {
+ promise.then(resolvedValue -> resolvedMap.put(key, resolvedValue));
+ } catch (InterruptedException e) {
+ interruptedException.set(e);
+ }
+ });
+ if (interruptedException.isSet()) {
+ throw interruptedException.get();
+ }
+ return new Promise<>(resolvedMap);
+ }
+
+ @Test
+ public void testRetrace() throws Exception {
+ TestDiagnosticMessagesImpl diagnosticMessages = new TestDiagnosticMessagesImpl();
+ Storage storage = buildStorage(diagnosticMessages);
+
+ List<StackTraceLine> minifiedStackTrace = new ArrayList<>();
+ minifiedStackTrace.add(StackTraceLine.parse("at a.a(SourceFile:1)"));
+ minifiedStackTrace.add(StackTraceLine.parse("at b.b(SourceFile:2)"));
+ minifiedStackTrace.add(StackTraceLine.parse("at c.c(SourceFile:3)"));
+
+ StackTrace retracedStacktrace =
+ new PromiseRunner<StackTrace>()
+ .run(
+ storage
+ .getMetadata()
+ .thenApply(
+ metadata -> {
+ Map<String, Promise<byte[]>> partitionRequests = new HashMap<>();
+ RetraceAsyncResult<RetraceStackTraceResult<StackTraceLine>>
+ asyncResult =
+ RetraceAsync
+ .<StackTraceLine,
+ RetracePartitionStackTraceTest.StackTraceLineProxy>
+ builder()
+ .setStackTraceLineParser(new IdentityStackTraceLineParser())
+ .setDiagnosticsHandler(diagnosticMessages)
+ .setMappingSupplier(
+ PartitionMappingSupplierAsync.builder()
+ .setMetadata(metadata)
+ .setRegisterMappingPartitionCallback(
+ key ->
+ partitionRequests.put(
+ key, storage.getPartition(key)))
+ .build())
+ .build()
+ .retraceStackTrace(
+ minifiedStackTrace, RetraceStackTraceContext.empty());
+ return getThen(partitionRequests, asyncResult);
+ }));
+ StackTrace expectedStackTrace =
+ StackTrace.builder()
+ .add(
+ StackTraceLine.builder()
+ .setClassName("com.Foo")
+ .setMethodName("m1")
+ .setFileName("Foo.java")
+ .setLineNumber(42)
+ .build())
+ .add(
+ StackTraceLine.builder()
+ .setClassName("com.Bar")
+ .setMethodName("m2")
+ .setFileName("Bar.java")
+ .setLineNumber(43)
+ .build())
+ .add(
+ StackTraceLine.builder()
+ .setClassName("com.Baz")
+ .setMethodName("m3")
+ .setFileName("Baz.java")
+ .setLineNumber(44)
+ .build())
+ .build();
+ assertThat(retracedStacktrace, isSame(expectedStackTrace));
+ }
+
+ // This method needs to be outlined due to a bug in javac 8.
+ private Promise<StackTrace> getThen(
+ Map<String, Promise<byte[]>> partitionRequests,
+ RetraceAsyncResult<RetraceStackTraceResult<StackTraceLine>> asyncResult)
+ throws InterruptedException {
+ return transpose(partitionRequests)
+ .then(
+ resolvedPartitions -> {
+ StackTrace.Builder retraceStackTraceBuilder = StackTrace.builder();
+ asyncResult
+ .getResult(resolvedPartitions::get)
+ .forEach(
+ retraced -> {
+ Assert.assertEquals(1, retraced.size());
+ retraced.get(0).forEach(retraceStackTraceBuilder::add);
+ });
+ return retraceStackTraceBuilder.build();
+ });
+ }
+ }
+
+ @RunWith(Parameterized.class)
+ public static class RetracePartitionStackTraceTest extends TestBase {
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ public RetracePartitionStackTraceTest(TestParameters parameters) {
+ parameters.assertNoneRuntime();
+ }
+
+ private final String MAPPING =
+ StringUtils.unixLines(
+ "com.Foo -> a:",
+ " 1:1:void m1():42:42 -> a",
+ "com.Bar -> b:",
+ " 2:2:void m2():43:43 -> b",
+ "com.Baz -> c:",
+ " 3:3:void m3():44:44 -> c");
+
+ @Test
+ public void testRetrace() throws Exception {
+ TestDiagnosticMessagesImpl diagnosticMessages = new TestDiagnosticMessagesImpl();
+ Map<String, byte[]> partitions = new HashMap<>();
+ MappingPartitionMetadata metadata =
+ ProguardMapPartitioner.builder(diagnosticMessages)
+ .setProguardMapProducer(ProguardMapProducer.fromString(MAPPING))
+ .setPartitionConsumer(
+ partition -> partitions.put(partition.getKey(), partition.getPayload()))
+ .build()
+ .run();
+
+ Set<String> requestedKeys = new HashSet<>();
+ BooleanBox setPartitionCallBack = new BooleanBox();
+ PartitionMappingSupplier mappingSupplier =
+ PartitionMappingSupplier.builder()
+ .setMetadata(metadata.getBytes())
+ .setRegisterMappingPartitionCallback(requestedKeys::add)
+ .setPrepareMappingPartitionsCallback(setPartitionCallBack::set)
+ .setMappingPartitionFromKeySupplier(partitions::get)
+ .build();
+
+ Retrace<StackTraceLine, StackTraceLineProxy> retrace =
+ Retrace.<StackTraceLine, StackTraceLineProxy>builder()
+ .setMappingSupplier(mappingSupplier)
+ .setStackTraceLineParser(StackTraceLineProxy::new)
+ .setDiagnosticsHandler(diagnosticMessages)
+ .build();
+
+ List<StackTraceLine> minifiedStackTrace = new ArrayList<>();
+ minifiedStackTrace.add(StackTraceLine.parse("at a.a(SourceFile:1)"));
+ minifiedStackTrace.add(StackTraceLine.parse("at b.b(SourceFile:2)"));
+ minifiedStackTrace.add(StackTraceLine.parse("at c.c(SourceFile:3)"));
+ StackTrace.Builder retraceStackTraceBuilder = StackTrace.builder();
+ retrace
+ .retraceStackTrace(minifiedStackTrace, RetraceStackTraceContext.empty())
+ .forEach(
+ stackTraceLines -> {
+ Assert.assertEquals(1, stackTraceLines.size());
+ stackTraceLines.get(0).forEach(retraceStackTraceBuilder::add);
+ });
+ Assert.assertEquals(partitions.keySet(), requestedKeys);
+ StackTrace expectedStackTrace =
+ StackTrace.builder()
+ .add(
+ StackTraceLine.builder()
+ .setClassName("com.Foo")
+ .setMethodName("m1")
+ .setFileName("Foo.java")
+ .setLineNumber(42)
+ .build())
+ .add(
+ StackTraceLine.builder()
+ .setClassName("com.Bar")
+ .setMethodName("m2")
+ .setFileName("Bar.java")
+ .setLineNumber(43)
+ .build())
+ .add(
+ StackTraceLine.builder()
+ .setClassName("com.Baz")
+ .setMethodName("m3")
+ .setFileName("Baz.java")
+ .setLineNumber(44)
+ .build())
+ .build();
+ assertThat(expectedStackTrace, isSame(retraceStackTraceBuilder.build()));
+ }
+
+ public static class IdentityStackTraceLineParser
+ implements StackTraceLineParser<StackTraceLine, StackTraceLineProxy> {
+
+ @Override
+ public StackTraceLineProxy parse(StackTraceLine stackTraceLine) {
+ return new StackTraceLineProxy(stackTraceLine);
+ }
+ }
+
+ public static class StackTraceLineProxy
+ extends StackTraceElementProxy<StackTraceLine, StackTraceLineProxy> {
+
+ private final StackTraceLine stackTraceLine;
+
+ public StackTraceLineProxy(StackTraceLine stackTraceLine) {
+ this.stackTraceLine = stackTraceLine;
+ }
+
+ @Override
+ public boolean hasClassName() {
+ return true;
+ }
+
+ @Override
+ public boolean hasMethodName() {
+ return true;
+ }
+
+ @Override
+ public boolean hasSourceFile() {
+ return true;
+ }
+
+ @Override
+ public boolean hasLineNumber() {
+ return true;
+ }
+
+ @Override
+ public boolean hasFieldName() {
+ return false;
+ }
+
+ @Override
+ public boolean hasFieldOrReturnType() {
+ return false;
+ }
+
+ @Override
+ public boolean hasMethodArguments() {
+ return false;
+ }
+
+ @Override
+ public ClassReference getClassReference() {
+ return Reference.classFromTypeName(stackTraceLine.className);
+ }
+
+ @Override
+ public String getMethodName() {
+ return stackTraceLine.methodName;
+ }
+
+ @Override
+ public String getSourceFile() {
+ return stackTraceLine.fileName;
+ }
+
+ @Override
+ public int getLineNumber() {
+ return stackTraceLine.lineNumber;
+ }
+
+ @Override
+ public String getFieldName() {
+ return null;
+ }
+
+ @Override
+ public String getFieldOrReturnType() {
+ return null;
+ }
+
+ @Override
+ public String getMethodArguments() {
+ return null;
+ }
+
+ @Override
+ public StackTraceLine toRetracedItem(
+ RetraceStackTraceElementProxy<StackTraceLine, StackTraceLineProxy> retracedProxy,
+ boolean verbose) {
+ RetracedMethodReference retracedMethod = retracedProxy.getRetracedMethod();
+ if (retracedMethod == null) {
+ return new StackTraceLine(
+ stackTraceLine.toString(),
+ stackTraceLine.className,
+ stackTraceLine.methodName,
+ stackTraceLine.fileName,
+ stackTraceLine.lineNumber);
+ } else {
+ KnownRetracedMethodReference knownRetracedMethodReference = retracedMethod.asKnown();
+ return new StackTraceLine(
+ stackTraceLine.toString(),
+ knownRetracedMethodReference.getMethodReference().getHolderClass().getTypeName(),
+ knownRetracedMethodReference.getMethodName(),
+ retracedProxy.getSourceFile(),
+ knownRetracedMethodReference.getOriginalPositionOrDefault(0));
+ }
+ }
+ }
+ }
}
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetracePartitionStackTraceTest.java b/src/test/java/com/android/tools/r8/retrace/api/RetracePartitionStackTraceTest.java
deleted file mode 100644
index 671d075..0000000
--- a/src/test/java/com/android/tools/r8/retrace/api/RetracePartitionStackTraceTest.java
+++ /dev/null
@@ -1,234 +0,0 @@
-// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.retrace.api;
-
-import static com.android.tools.r8.naming.retrace.StackTrace.isSame;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
-
-import com.android.tools.r8.TestBase;
-import com.android.tools.r8.TestDiagnosticMessagesImpl;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.naming.retrace.StackTrace;
-import com.android.tools.r8.naming.retrace.StackTrace.StackTraceLine;
-import com.android.tools.r8.references.ClassReference;
-import com.android.tools.r8.references.Reference;
-import com.android.tools.r8.retrace.MappingPartitionMetadata;
-import com.android.tools.r8.retrace.PartitionMappingSupplier;
-import com.android.tools.r8.retrace.ProguardMapPartitioner;
-import com.android.tools.r8.retrace.ProguardMapProducer;
-import com.android.tools.r8.retrace.Retrace;
-import com.android.tools.r8.retrace.RetraceStackTraceContext;
-import com.android.tools.r8.retrace.RetraceStackTraceElementProxy;
-import com.android.tools.r8.retrace.RetracedMethodReference.KnownRetracedMethodReference;
-import com.android.tools.r8.retrace.StackTraceElementProxy;
-import com.android.tools.r8.retrace.StackTraceLineParser;
-import com.android.tools.r8.utils.BooleanBox;
-import com.android.tools.r8.utils.StringUtils;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-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 RetracePartitionStackTraceTest extends TestBase {
-
- @Parameters(name = "{0}")
- public static TestParametersCollection data() {
- return getTestParameters().withNoneRuntime().build();
- }
-
- public RetracePartitionStackTraceTest(TestParameters parameters) {
- parameters.assertNoneRuntime();
- }
-
- private final String MAPPING =
- StringUtils.unixLines(
- "com.Foo -> a:",
- " 1:1:void m1():42:42 -> a",
- "com.Bar -> b:",
- " 2:2:void m2():43:43 -> b",
- "com.Baz -> c:",
- " 3:3:void m3():44:44 -> c");
-
- @Test
- public void testRetrace() throws Exception {
- TestDiagnosticMessagesImpl diagnosticMessages = new TestDiagnosticMessagesImpl();
- Map<String, byte[]> partitions = new HashMap<>();
- MappingPartitionMetadata metadata =
- ProguardMapPartitioner.builder(diagnosticMessages)
- .setProguardMapProducer(ProguardMapProducer.fromString(MAPPING))
- .setPartitionConsumer(
- partition -> partitions.put(partition.getKey(), partition.getPayload()))
- .build()
- .run();
-
- Set<String> requestedKeys = new HashSet<>();
- BooleanBox setPartitionCallBack = new BooleanBox();
- PartitionMappingSupplier mappingSupplier =
- PartitionMappingSupplier.builder()
- .setMetadata(metadata.getBytes())
- .setRegisterMappingPartitionCallback(requestedKeys::add)
- .setPrepareMappingPartitionsCallback(setPartitionCallBack::set)
- .setMappingPartitionFromKeySupplier(partitions::get)
- .build();
-
- Retrace<StackTraceLine, StackTraceLineProxy> retrace =
- Retrace.<StackTraceLine, StackTraceLineProxy>builder()
- .setMappingSupplier(mappingSupplier)
- .setStackTraceLineParser(StackTraceLineProxy::new)
- .setDiagnosticsHandler(diagnosticMessages)
- .build();
-
- List<StackTraceLine> minifiedStackTrace = new ArrayList<>();
- minifiedStackTrace.add(StackTraceLine.parse("at a.a(SourceFile:1)"));
- minifiedStackTrace.add(StackTraceLine.parse("at b.b(SourceFile:2)"));
- minifiedStackTrace.add(StackTraceLine.parse("at c.c(SourceFile:3)"));
- StackTrace.Builder retraceStackTraceBuilder = StackTrace.builder();
- retrace
- .retraceStackTrace(minifiedStackTrace, RetraceStackTraceContext.empty())
- .forEach(
- stackTraceLines -> {
- assertEquals(1, stackTraceLines.size());
- stackTraceLines.get(0).forEach(retraceStackTraceBuilder::add);
- });
- assertEquals(partitions.keySet(), requestedKeys);
- StackTrace expectedStackTrace =
- StackTrace.builder()
- .add(
- StackTraceLine.builder()
- .setClassName("com.Foo")
- .setMethodName("m1")
- .setFileName("Foo.java")
- .setLineNumber(42)
- .build())
- .add(
- StackTraceLine.builder()
- .setClassName("com.Bar")
- .setMethodName("m2")
- .setFileName("Bar.java")
- .setLineNumber(43)
- .build())
- .add(
- StackTraceLine.builder()
- .setClassName("com.Baz")
- .setMethodName("m3")
- .setFileName("Baz.java")
- .setLineNumber(44)
- .build())
- .build();
- assertThat(expectedStackTrace, isSame(retraceStackTraceBuilder.build()));
- }
-
- public static class IdentityStackTraceLineParser
- implements StackTraceLineParser<StackTraceLine, StackTraceLineProxy> {
-
- @Override
- public StackTraceLineProxy parse(StackTraceLine stackTraceLine) {
- return new StackTraceLineProxy(stackTraceLine);
- }
- }
-
- public static class StackTraceLineProxy
- extends StackTraceElementProxy<StackTraceLine, StackTraceLineProxy> {
-
- private final StackTraceLine stackTraceLine;
-
- public StackTraceLineProxy(StackTraceLine stackTraceLine) {
- this.stackTraceLine = stackTraceLine;
- }
-
- @Override
- public boolean hasClassName() {
- return true;
- }
-
- @Override
- public boolean hasMethodName() {
- return true;
- }
-
- @Override
- public boolean hasSourceFile() {
- return true;
- }
-
- @Override
- public boolean hasLineNumber() {
- return true;
- }
-
- @Override
- public boolean hasFieldName() {
- return false;
- }
-
- @Override
- public boolean hasFieldOrReturnType() {
- return false;
- }
-
- @Override
- public boolean hasMethodArguments() {
- return false;
- }
-
- @Override
- public ClassReference getClassReference() {
- return Reference.classFromTypeName(stackTraceLine.className);
- }
-
- @Override
- public String getMethodName() {
- return stackTraceLine.methodName;
- }
-
- @Override
- public String getSourceFile() {
- return stackTraceLine.fileName;
- }
-
- @Override
- public int getLineNumber() {
- return stackTraceLine.lineNumber;
- }
-
- @Override
- public String getFieldName() {
- return null;
- }
-
- @Override
- public String getFieldOrReturnType() {
- return null;
- }
-
- @Override
- public String getMethodArguments() {
- return null;
- }
-
- @Override
- public StackTraceLine toRetracedItem(
- RetraceStackTraceElementProxy<StackTraceLine, StackTraceLineProxy> retracedProxy,
- boolean verbose) {
- KnownRetracedMethodReference knownRetracedMethodReference =
- retracedProxy.getRetracedMethod().asKnown();
- return new StackTraceLine(
- stackTraceLine.toString(),
- knownRetracedMethodReference.getMethodReference().getHolderClass().getTypeName(),
- knownRetracedMethodReference.getMethodName(),
- retracedProxy.getSourceFile(),
- knownRetracedMethodReference.getOriginalPositionOrDefault(0));
- }
- }
-}
diff --git a/src/test/java/com/android/tools/r8/retrace/partition/R8ZipContainerMappingFileTest.java b/src/test/java/com/android/tools/r8/retrace/partition/R8ZipContainerMappingFileTest.java
index 211121c..7c64527 100644
--- a/src/test/java/com/android/tools/r8/retrace/partition/R8ZipContainerMappingFileTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/partition/R8ZipContainerMappingFileTest.java
@@ -9,14 +9,15 @@
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.DiagnosticsHandler;
-import com.android.tools.r8.ProguardMapConsumer;
+import com.android.tools.r8.PartitionMapConsumer;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestDiagnosticMessagesImpl;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.naming.ProguardMapPartitionConsumer;
import com.android.tools.r8.naming.retrace.StackTrace;
import com.android.tools.r8.naming.retrace.StackTrace.StackTraceLine;
+import com.android.tools.r8.retrace.MappingPartition;
+import com.android.tools.r8.retrace.MappingPartitionMetadata;
import com.android.tools.r8.retrace.PartitionMappingSupplier;
import com.android.tools.r8.retrace.partition.testclasses.R8ZipContainerMappingFileTestClasses;
import com.android.tools.r8.retrace.partition.testclasses.R8ZipContainerMappingFileTestClasses.Main;
@@ -79,8 +80,6 @@
public void testR8() throws Exception {
Path pgMapFile = temp.newFile("mapping.zip").toPath();
DiagnosticsHandler diagnosticsHandler = new TestDiagnosticMessagesImpl();
- ProguardMapConsumer partitionZipConsumer =
- createPartitionZipConsumer(pgMapFile, diagnosticsHandler);
StackTrace originalStackTrace =
testForR8(parameters.getBackend())
.addInnerClasses(R8ZipContainerMappingFileTestClasses.class)
@@ -88,7 +87,7 @@
.addKeepMainRule(Main.class)
.addKeepAttributeSourceFile()
.addKeepAttributeLineNumberTable()
- .addOptionsModification(options -> options.proguardMapConsumer = partitionZipConsumer)
+ .setPartitionMapConsumer(createPartitionZipConsumer(pgMapFile))
.run(parameters.getRuntime(), Main.class)
.assertFailureWithErrorThatThrows(RuntimeException.class)
.getOriginalStackTrace();
@@ -99,36 +98,37 @@
isSame(EXPECTED));
}
- private ProguardMapConsumer createPartitionZipConsumer(
- Path pgMapFile, DiagnosticsHandler diagnosticsHandler) throws IOException {
+ private PartitionMapConsumer createPartitionZipConsumer(Path pgMapFile) throws IOException {
ZipBuilder zipBuilder = ZipBuilder.builder(pgMapFile);
- return ProguardMapPartitionConsumer.builder()
- .setMappingPartitionConsumer(
- mappingPartition -> {
- try {
- zipBuilder.addBytes(mappingPartition.getKey(), mappingPartition.getPayload());
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- })
- .setMetadataConsumer(
- metadata -> {
- try {
- zipBuilder.addBytes("METADATA", metadata.getBytes());
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- })
- .setFinishedConsumer(
- () -> {
- try {
- zipBuilder.build();
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- })
- .setDiagnosticsHandler(diagnosticsHandler)
- .build();
+ return new PartitionMapConsumer() {
+ @Override
+ public void acceptMappingPartition(MappingPartition mappingPartition) {
+ try {
+ zipBuilder.addBytes(mappingPartition.getKey(), mappingPartition.getPayload());
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public void acceptMappingPartitionMetadata(
+ MappingPartitionMetadata mappingPartitionMetadata) {
+ try {
+ zipBuilder.addBytes("METADATA", mappingPartitionMetadata.getBytes());
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public void finished(DiagnosticsHandler handler) {
+ try {
+ zipBuilder.build();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ };
}
private PartitionMappingSupplier createMappingSupplierFromPartitionZip(Path pgMapFile)
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 81c7400..21a12a2 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
@@ -34,6 +34,7 @@
import com.android.tools.r8.naming.signature.GenericSignatureAction;
import com.android.tools.r8.naming.signature.GenericSignatureParser;
import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.profile.art.ArtProfileOptions;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.FieldReference;
import com.android.tools.r8.references.MethodReference;
@@ -123,6 +124,7 @@
dexItemFactory = options.itemFactory;
AndroidApp input = AndroidApp.builder().addProgramFiles(files).build();
application = new ApplicationReader(input, options, timing).read();
+ configureOptions();
}
public CodeInspector(AndroidApp app) throws IOException {
@@ -184,6 +186,12 @@
obfuscatedToOriginalMapping = nameMapping.original;
originalToObfuscatedMapping = nameMapping.inverse;
}
+ configureOptions();
+ }
+
+ private void configureOptions() {
+ ArtProfileOptions artProfileOptions = application.options.getArtProfileOptions();
+ artProfileOptions.setAllowReadingEmptyArtProfileProvidersMultipleTimesForTesting(true);
}
public DexApplication getApplication() {
diff --git a/third_party/retrace/binary_compatibility.tar.gz.sha1 b/third_party/retrace/binary_compatibility.tar.gz.sha1
index ed13628..5678440 100644
--- a/third_party/retrace/binary_compatibility.tar.gz.sha1
+++ b/third_party/retrace/binary_compatibility.tar.gz.sha1
@@ -1 +1 @@
-f9e5287651f7663c7ebd2f14a58be99eb238bd8a
\ No newline at end of file
+5c0b406a52a08af4318a104660091c61d6bb8446
\ No newline at end of file
diff --git a/tools/trigger.py b/tools/trigger.py
index d716a55..0fd8b64 100755
--- a/tools/trigger.py
+++ b/tools/trigger.py
@@ -25,6 +25,7 @@
DESUGAR_JDK11_BOT = 'lib_desugar-archive-jdk11'
DESUGAR_JDK11_LEGACY_BOT = 'lib_desugar-archive-jdk11-legacy'
DESUGAR_JDK8_BOT = 'lib_desugar-archive-jdk8'
+SMALI_BOT = 'smali'
def ParseOptions():
result = optparse.OptionParser()
@@ -32,6 +33,7 @@
help='Run on the release branch builders.',
default=False, action='store_true')
result.add_option('--cl',
+ metavar=('<url>'),
help='Run the specified cl on the bots. This should be '
'the full url, e.g., '
'https://r8-review.googlesource.com/c/r8/+/37420/1')
@@ -44,6 +46,9 @@
result.add_option('--desugar-jdk8',
help='Run the jdk8 library desugar and archiving bot.',
default=False, action='store_true')
+ result.add_option('--smali',
+ metavar=('<version>'),
+ help='Build smali version <version>.')
result.add_option('--builder', help='Trigger specific builder')
return result.parse_args()
@@ -72,6 +77,7 @@
print('Desugar jdk11 builder:\n ' + DESUGAR_JDK11_BOT)
print('Desugar jdk11 legacy builder:\n ' + DESUGAR_JDK11_LEGACY_BOT)
print('Desugar jdk8 builder:\n ' + DESUGAR_JDK8_BOT)
+ print('Smali builder:\n ' + SMALI_BOT)
print('Main builders:\n ' + '\n '.join(main_builders))
print('Release builders:\n ' + '\n '.join(release_builders))
return (main_builders, release_builders)
@@ -88,6 +94,20 @@
cmd = ['bb', 'add', 'r8/ci/%s' % builder , '-commit', commit_url]
subprocess.check_call(cmd)
+def trigger_smali_builder(version):
+ utils.check_basic_semver_version(
+ version,
+ 'use semantic version of the smali version to built (pre-releases are not supported)',
+ allowPrerelease = False)
+ cmd = [
+ 'bb',
+ 'add',
+ 'r8/ci/%s' % SMALI_BOT,
+ '-p',
+ '\'test_options=["--version", "%s"]\'' % version
+ ]
+ subprocess.check_call(cmd)
+
def trigger_cl(builders, cl_url):
for builder in builders:
cmd = ['bb', 'add', 'r8/ci/%s' % builder , '-cl', cl_url]
@@ -96,7 +116,8 @@
def Main():
(options, args) = ParseOptions()
desugar = options.desugar_jdk11 or options.desugar_jdk11_legacy or options.desugar_jdk8
- if len(args) != 1 and not options.cl and not desugar:
+ requires_commit = not options.cl and not desugar and not options.smali
+ if len(args) != 1 and requires_commit:
print('Takes exactly one argument, the commit to run')
return 1
@@ -108,7 +129,19 @@
print('You can\'t run cls on the desugar bot')
return 1
- commit = None if (options.cl or desugar) else args[0]
+ if options.cl and options.smali:
+ print('You can\'t run cls on the smali bot')
+ return 1
+
+ if options.smali:
+ if not options.release:
+ print('Only release versions of smali can be built')
+ return 1
+
+ trigger_smali_builder(options.smali)
+ return
+
+ commit = None if not requires_commit else args[0]
(main_builders, release_builders) = get_builders()
builders = release_builders if options.release else main_builders
if options.builder: