Merge commit '5c7d3ce0754f28f499a6073a83d2abde5cf7bc02' into dev-release
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 41ea658..d96d546 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -650,7 +650,7 @@
appView.protoShrinker().enumLiteProtoShrinker.verifyDeadEnumLiteMapsAreDead();
}
- IRConverter converter = new IRConverter(appView, timing);
+ IRConverter converter = new IRConverter(appView);
// If proto shrinking is enabled, we need to reprocess every dynamicMethod(). This ensures
// that proto fields that have been removed by the second round of tree shaking are also
@@ -1017,7 +1017,7 @@
static void processWhyAreYouKeepingAndCheckDiscarded(
RootSet rootSet,
- Supplier<Iterable<DexProgramClass>> classes,
+ Supplier<Collection<DexProgramClass>> classes,
WhyAreYouKeepingConsumer whyAreYouKeepingConsumer,
AppView<? extends AppInfoWithClassHierarchy> appView,
Enqueuer enqueuer,
diff --git a/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java b/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java
index 261497d..8bbfcfd 100644
--- a/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java
+++ b/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java
@@ -24,10 +24,12 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.synthesis.CommittedItems;
import com.android.tools.r8.synthesis.SyntheticItems;
+import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.WorkList;
import com.google.common.collect.Sets;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@@ -58,7 +60,16 @@
public void run(ExecutorService executorService) throws ExecutionException {
if (appView.options().isGeneratingDex()
&& appView.options().apiModelingOptions().enableStubbingOfClasses) {
- ThreadUtils.processItems(appView.appInfo().classes(), this::processClass, executorService);
+ Collection<DexProgramClass> classes =
+ ListUtils.filter(
+ appView.appInfo().classes(), DexProgramClass::originatesFromClassResource);
+ // Finding super types is really fast so no need to pay the overhead of threading if the
+ // number of classes is low.
+ if (classes.size() > 2) {
+ ThreadUtils.processItems(classes, this::processClass, executorService);
+ } else {
+ classes.forEach(this::processClass);
+ }
}
if (!libraryClassesToMock.isEmpty()) {
libraryClassesToMock.forEach(
@@ -98,6 +109,7 @@
}
public void processClass(DexProgramClass clazz) {
+ assert clazz.originatesFromClassResource();
if (isAlreadyOutlined(clazz)) {
return;
}
@@ -124,7 +136,7 @@
}
private void findReferencedLibraryClasses(DexType type, DexProgramClass context) {
- if (!type.isClassType()) {
+ if (!type.isClassType() || isJavaType(type)) {
return;
}
WorkList<DexType> workList = WorkList.newIdentityWorkList(type, seenTypes);
@@ -147,17 +159,18 @@
}
}
+ private boolean isJavaType(DexType type) {
+ return type == appView.dexItemFactory().objectType
+ || type.getDescriptor().startsWith(appView.dexItemFactory().javaDescriptorPrefix);
+ }
+
private void mockMissingLibraryClass(
DexLibraryClass libraryClass,
ThrowExceptionCode throwExceptionCode,
ApiReferenceStubberEventConsumer eventConsumer) {
DexItemFactory factory = appView.dexItemFactory();
// Do not stub the anything starting with java (including the object type).
- if (libraryClass.getType() == appView.dexItemFactory().objectType
- || libraryClass
- .getType()
- .getDescriptor()
- .startsWith(appView.dexItemFactory().javaDescriptorPrefix)) {
+ if (isJavaType(libraryClass.getType())) {
return;
}
// Check if desugared library will bridge the type.
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstDynamic.java b/src/main/java/com/android/tools/r8/cf/code/CfConstDynamic.java
index 6c379bb..9b9b2b1 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstDynamic.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstDynamic.java
@@ -46,17 +46,21 @@
private final ConstantDynamicReference reference;
public CfConstDynamic(
+ int symbolicReferenceId,
DexString name,
DexType type,
DexMethodHandle bootstrapMethod,
List<DexValue> bootstrapMethodArguments) {
+ assert symbolicReferenceId >= 0;
assert name != null;
assert type != null;
assert bootstrapMethod != null;
assert bootstrapMethodArguments != null;
assert bootstrapMethodArguments.isEmpty();
- reference = new ConstantDynamicReference(name, type, bootstrapMethod, bootstrapMethodArguments);
+ reference =
+ new ConstantDynamicReference(
+ symbolicReferenceId, name, type, bootstrapMethod, bootstrapMethodArguments);
}
@Override
@@ -86,7 +90,10 @@
}
public static CfConstDynamic fromAsmConstantDynamic(
- ConstantDynamic insn, JarApplicationReader application, DexType clazz) {
+ int symbolicReferenceId,
+ ConstantDynamic insn,
+ JarApplicationReader application,
+ DexType clazz) {
String constantName = insn.getName();
String constantDescriptor = insn.getDescriptor();
DexMethodHandle bootstrapMethodHandle =
@@ -99,6 +106,7 @@
bootstrapMethodArguments.add(dexValue);
}
return new CfConstDynamic(
+ symbolicReferenceId,
application.getString(constantName),
application.getTypeFromDescriptor(constantDescriptor),
bootstrapMethodHandle,
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/instrumentation/StartupInstrumentation.java b/src/main/java/com/android/tools/r8/experimental/startup/instrumentation/StartupInstrumentation.java
index 72908ca..6e9c218 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/instrumentation/StartupInstrumentation.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/instrumentation/StartupInstrumentation.java
@@ -57,7 +57,7 @@
private StartupInstrumentation(AppView<AppInfo> appView) {
this.appView = appView;
- this.converter = new IRConverter(appView, Timing.empty());
+ this.converter = new IRConverter(appView);
this.dexItemFactory = appView.dexItemFactory();
this.options = appView.options();
this.references = new StartupInstrumentationReferences(dexItemFactory);
diff --git a/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java b/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java
index 03a4ce6..53ebcee 100644
--- a/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java
+++ b/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java
@@ -174,7 +174,7 @@
}
private void writeIR(ProgramMethod method) {
- IRConverter converter = new IRConverter(appInfo, timing);
+ IRConverter converter = new IRConverter(appInfo);
MethodProcessorEventConsumer eventConsumer = MethodProcessorEventConsumer.empty();
OneTimeMethodProcessor methodProcessor =
OneTimeMethodProcessor.create(
diff --git a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
index 45fed00..153f85f 100644
--- a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
@@ -79,6 +79,8 @@
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
+import it.unimi.dsi.fastutil.objects.Reference2IntArrayMap;
+import it.unimi.dsi.fastutil.objects.Reference2IntMap;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -86,6 +88,7 @@
import java.util.List;
import java.util.Map;
import java.util.function.BiFunction;
+import java.util.function.Supplier;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ConstantDynamic;
@@ -334,6 +337,7 @@
private boolean usrJsrInliner;
private final Origin origin;
private final DebugParsingOptions debugParsingOptions;
+ private Reference2IntMap<ConstantDynamic> constantDynamicSymbolicReferences;
ClassCodeVisitor(
DexClass clazz,
@@ -351,6 +355,13 @@
this.debugParsingOptions = debugParsingOptions;
}
+ private Reference2IntMap<ConstantDynamic> getConstantDynamicSymbolicReferences() {
+ if (constantDynamicSymbolicReferences == null) {
+ constantDynamicSymbolicReferences = new Reference2IntArrayMap<>();
+ }
+ return constantDynamicSymbolicReferences;
+ }
+
@Override
public MethodVisitor visitMethod(
int access, String name, String desc, String signature, String[] exceptions) {
@@ -360,7 +371,13 @@
if (code != null) {
DexMethod method = application.getMethod(clazz.type, name, desc);
MethodCodeVisitor methodVisitor =
- new MethodCodeVisitor(application, method, code, origin, debugParsingOptions);
+ new MethodCodeVisitor(
+ application,
+ method,
+ code,
+ origin,
+ debugParsingOptions,
+ this::getConstantDynamicSymbolicReferences);
if (!usrJsrInliner) {
return methodVisitor;
}
@@ -391,13 +408,16 @@
private final Origin origin;
private int minLine = Integer.MAX_VALUE;
private int maxLine = -1;
+ private final Supplier<Reference2IntMap<ConstantDynamic>>
+ constantDynamicSymbolicReferencesSupplier;
MethodCodeVisitor(
JarApplicationReader application,
DexMethod method,
LazyCfCode code,
Origin origin,
- DebugParsingOptions debugParsingOptions) {
+ DebugParsingOptions debugParsingOptions,
+ Supplier<Reference2IntMap<ConstantDynamic>> constantDynamicSymbolicReferencesSupplier) {
super(InternalOptions.ASM_VERSION);
this.debugParsingOptions = debugParsingOptions;
assert code != null;
@@ -406,6 +426,7 @@
this.code = code;
this.method = method;
this.origin = origin;
+ this.constantDynamicSymbolicReferencesSupplier = constantDynamicSymbolicReferencesSupplier;
}
private void addInstruction(CfInstruction instruction) {
@@ -1004,9 +1025,20 @@
new CfConstMethodHandle(
DexMethodHandle.fromAsmHandle((Handle) cst, application, method.holder)));
} else if (cst instanceof ConstantDynamic) {
+ // Each symbolic reference to a dynamically-computed constant has a unique ConstantDynamic
+ // instance from ASM, even when they are equal (i.e. all their components are equal). See
+ // ConstantDynamicMultipleConstantsWithDifferentSymbolicReferenceUsingSameBSMAndArgumentsTest
+ // for an example.
+ Reference2IntMap<ConstantDynamic> constantDynamicSymbolicReferences =
+ constantDynamicSymbolicReferencesSupplier.get();
+ int symbolicReferenceId = constantDynamicSymbolicReferences.getOrDefault(cst, -1);
+ if (symbolicReferenceId == -1) {
+ symbolicReferenceId = constantDynamicSymbolicReferences.size();
+ constantDynamicSymbolicReferences.put((ConstantDynamic) cst, symbolicReferenceId);
+ }
addInstruction(
CfConstDynamic.fromAsmConstantDynamic(
- (ConstantDynamic) cst, application, method.holder));
+ symbolicReferenceId, (ConstantDynamic) cst, application, method.holder));
} else {
throw new CompilationError("Unsupported constant: " + cst.toString());
}
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 1872754..93572c6 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
@@ -59,7 +59,7 @@
public void convertClassInitializers(ExecutorService executorService) throws ExecutionException {
if (!classInitializers.isEmpty()) {
- IRConverter converter = new IRConverter(createAppViewForConversion(), Timing.empty());
+ IRConverter converter = new IRConverter(createAppViewForConversion());
ThreadUtils.processItems(
classInitializers, method -> processMethod(method, converter), executorService);
}
@@ -68,7 +68,7 @@
public void convertInstanceInitializers(ExecutorService executorService)
throws ExecutionException {
if (!instanceInitializers.isEmpty()) {
- IRConverter converter = new IRConverter(createAppViewForConversion(), Timing.empty());
+ IRConverter converter = new IRConverter(createAppViewForConversion());
ThreadUtils.processItems(
instanceInitializers,
clazz -> processInstanceInitializers(clazz, converter),
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteBuilderShrinker.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteBuilderShrinker.java
index b5eec8c..2ff901b 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteBuilderShrinker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteBuilderShrinker.java
@@ -195,7 +195,7 @@
}
timing.begin("Remove dead builder references");
AppInfoWithLiveness appInfo = appView.appInfo();
- IRConverter converter = new IRConverter(appView, Timing.empty());
+ IRConverter converter = new IRConverter(appView);
ThreadUtils.processMap(
builders,
(builder, dynamicMethod) -> {
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 c2917b8..4291c1c 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
@@ -96,7 +96,6 @@
public final AppView<?> appView;
- protected final Timing timing;
public final Outliner outliner;
private final ClassInitializerDefaultsOptimization classInitializerDefaultsOptimization;
protected final CfInstructionDesugaringCollection instructionDesugaring;
@@ -148,11 +147,9 @@
* The argument `appView` is used to determine if whole program optimizations are allowed or not
* (i.e., whether we are running R8). See {@link AppView#enableWholeProgramOptimizations()}.
*/
- public IRConverter(AppView<?> appView, Timing timing) {
+ public IRConverter(AppView<?> appView) {
assert appView.options() != null;
assert appView.options().programConsumer != null;
- assert timing != null;
- this.timing = timing;
this.appView = appView;
this.options = appView.options();
this.codeRewriter = new CodeRewriter(appView);
@@ -290,8 +287,8 @@
: null;
}
- public IRConverter(AppInfo appInfo, Timing timing) {
- this(AppView.createForD8(appInfo), timing);
+ public IRConverter(AppInfo appInfo) {
+ this(AppView.createForD8(appInfo));
}
public Inliner getInliner() {
@@ -428,10 +425,6 @@
}
}
- String logCode(InternalOptions options, DexEncodedMethod method) {
- return options.useSmaliSyntax ? method.toSmaliString(null) : method.codeToString();
- }
-
// TODO(b/140766440): Make this receive a list of CodeOptimizations to conduct.
public Timing processDesugaredMethod(
ProgramMethod method,
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/OneTimeMethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/OneTimeMethodProcessor.java
index c243d46..86a4feb 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/OneTimeMethodProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/OneTimeMethodProcessor.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.ThreadUtils.WorkLoad;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
@@ -79,11 +80,11 @@
}
@FunctionalInterface
- public interface MethodAction<E extends Exception> {
- void accept(ProgramMethod method, MethodProcessingContext methodProcessingContext) throws E;
+ public interface MethodAction {
+ void accept(ProgramMethod method, MethodProcessingContext methodProcessingContext);
}
- public <E extends Exception> void forEachWaveWithExtension(MethodAction<E> consumer) throws E {
+ public void forEachWaveWithExtension(MethodAction consumer) {
while (!wave.isEmpty()) {
for (ProgramMethod method : wave) {
consumer.accept(method, processorContext.createMethodProcessingContext(method));
@@ -92,13 +93,15 @@
}
}
- public <E extends Exception> void forEachWaveWithExtension(
- MethodAction<E> consumer, ExecutorService executorService) throws ExecutionException {
+ public void forEachWaveWithExtension(MethodAction consumer, ExecutorService executorService)
+ throws ExecutionException {
while (!wave.isEmpty()) {
ThreadUtils.processItems(
wave,
- method -> consumer.accept(method, processorContext.createMethodProcessingContext(method)),
- executorService);
+ (method, ignored) ->
+ consumer.accept(method, processorContext.createMethodProcessingContext(method)),
+ executorService,
+ WorkLoad.HEAVY);
prepareForWaveExtensionProcessing();
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryD8L8IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryD8L8IRConverter.java
index b2959f2..cf8e52d 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryD8L8IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryD8L8IRConverter.java
@@ -45,8 +45,11 @@
public class PrimaryD8L8IRConverter extends IRConverter {
+ private final Timing timing;
+
public PrimaryD8L8IRConverter(AppView<AppInfo> appView, Timing timing) {
- super(appView, timing);
+ super(appView);
+ this.timing = timing;
}
public void convert(AppView<AppInfo> appView, ExecutorService executorService)
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java
index 1ad9a82..0ad28d4 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java
@@ -26,8 +26,11 @@
public class PrimaryR8IRConverter extends IRConverter {
+ private final Timing timing;
+
public PrimaryR8IRConverter(AppView<? extends AppInfoWithClassHierarchy> appView, Timing timing) {
- super(appView, timing);
+ super(appView);
+ this.timing = timing;
}
public void optimize(AppView<AppInfoWithLiveness> appView, ExecutorService executorService)
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicReference.java b/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicReference.java
index ed08c4d..478e4cf 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicReference.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicReference.java
@@ -15,24 +15,24 @@
import java.util.Objects;
public class ConstantDynamicReference implements StructuralItem<ConstantDynamicReference> {
+ private final int symbolicReferenceId;
private final DexString name;
private final DexType type;
private final DexMethodHandle bootstrapMethod;
private final List<DexValue> bootstrapMethodArguments;
private static void specify(StructuralSpecification<ConstantDynamicReference, ?> spec) {
- spec.withItem(ConstantDynamicReference::getName)
- .withItem(ConstantDynamicReference::getType)
- .withItem(ConstantDynamicReference::getBootstrapMethod)
- .withItemCollection(ConstantDynamicReference::getBootstrapMethodArguments);
+ spec.withInt(c -> c.symbolicReferenceId);
}
public ConstantDynamicReference(
+ int symbolicReferenceId,
DexString name,
DexType type,
DexMethodHandle bootstrapMethod,
List<DexValue> bootstrapMethodArguments) {
assert bootstrapMethodArguments.isEmpty();
+ this.symbolicReferenceId = symbolicReferenceId;
this.name = name;
this.type = type;
this.bootstrapMethod = bootstrapMethod;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodProcessorFacade.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodProcessorFacade.java
index c92c9ce..02f581d 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodProcessorFacade.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodProcessorFacade.java
@@ -11,8 +11,8 @@
import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaringEventConsumer;
import com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper.InterfaceMethodDesugaringMode;
import com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter.Flavor;
+import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.ThreadUtils;
-import com.google.common.collect.Iterables;
import java.util.Collection;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
@@ -51,7 +51,7 @@
ExecutorService executorService)
throws ExecutionException {
ThreadUtils.processItems(
- Iterables.filter(programClasses, (DexProgramClass clazz) -> shouldProcess(clazz, flavour)),
+ ListUtils.filter(programClasses, clazz -> shouldProcess(clazz, flavour)),
clazz -> classProcessor.process(clazz, eventConsumer),
executorService);
classProcessor.finalizeProcessing(eventConsumer, executorService);
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 d8a62a0..0c1c56f 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
@@ -45,7 +45,7 @@
private RecordFieldValuesRewriter(AppView<AppInfoWithLiveness> appView) {
this.appView = appView;
- irConverter = new IRConverter(appView, Timing.empty());
+ irConverter = new IRConverter(appView);
}
// Called after final tree shaking, prune and minify field names and field values.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedback.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedback.java
index 6e7bf18..d033ca6 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedback.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedback.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.ir.conversion.MethodOptimizationFeedback;
import com.android.tools.r8.shaking.AppInfoWithLivenessModifier;
import com.android.tools.r8.utils.ThreadUtils;
+import java.util.Collection;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.function.Consumer;
@@ -55,7 +56,7 @@
}
public void fixupOptimizationInfos(
- Iterable<DexProgramClass> classes,
+ Collection<DexProgramClass> classes,
ExecutorService executorService,
OptimizationInfoFixer fixer)
throws ExecutionException {
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java
index 7bae38e..a9d515a 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin;
+import static com.android.tools.r8.kotlin.KotlinMetadataUtils.getInvalidKotlinInfo;
import static com.android.tools.r8.kotlin.KotlinMetadataUtils.getNoKotlinInfo;
import static com.android.tools.r8.kotlin.KotlinSyntheticClassInfo.getFlavour;
@@ -18,9 +19,11 @@
import com.android.tools.r8.graph.DexValue;
import com.android.tools.r8.graph.DexValue.DexValueArray;
import com.android.tools.r8.kotlin.KotlinSyntheticClassInfo.Flavour;
+import com.android.tools.r8.utils.StringDiagnostic;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.function.Consumer;
+import java.util.function.Supplier;
import kotlin.Metadata;
import kotlinx.metadata.InconsistentKotlinMetadataException;
import kotlinx.metadata.jvm.KotlinClassMetadata;
@@ -34,16 +37,58 @@
private static final int SYNTHETIC_CLASS_KIND = 3;
public static KotlinClassLevelInfo getKotlinInfo(
- DexClass clazz, AppView<?> appView, Consumer<DexEncodedMethod> keepByteCode)
- throws KotlinMetadataException {
+ AppView<?> appView,
+ DexClass clazz,
+ Consumer<DexEncodedMethod> keepByteCode,
+ Supplier<Boolean> reportUnknownMetadata) {
DexAnnotation meta =
clazz.annotations().getFirstMatching(appView.dexItemFactory().kotlinMetadataType);
- return meta != null ? getKotlinInfo(clazz, appView, keepByteCode, meta) : getNoKotlinInfo();
+ if (meta == null) {
+ return getNoKotlinInfo();
+ }
+ return getKotlinInfoFromAnnotation(appView, clazz, meta, keepByteCode, reportUnknownMetadata);
}
- public static KotlinClassLevelInfo getKotlinInfo(
- DexClass clazz,
+ public static KotlinClassLevelInfo getKotlinInfoFromAnnotation(
AppView<?> appView,
+ DexClass clazz,
+ DexAnnotation meta,
+ Consumer<DexEncodedMethod> keepByteCode,
+ Supplier<Boolean> reportUnknownMetadata) {
+ try {
+ return getKotlinInfo(appView, clazz, keepByteCode, meta);
+ } catch (KotlinMetadataException e) {
+ if (reportUnknownMetadata.get()) {
+ appView.reporter().warning(KotlinMetadataDiagnostic.unknownMetadataVersion());
+ }
+ appView
+ .reporter()
+ .info(
+ new StringDiagnostic(
+ "Class "
+ + clazz.type.toSourceString()
+ + " has malformed kotlin.Metadata: "
+ + e.getMessage()));
+ return getInvalidKotlinInfo();
+ } catch (Throwable e) {
+ if (reportUnknownMetadata.get()) {
+ appView.reporter().warning(KotlinMetadataDiagnostic.unknownMetadataVersion());
+ }
+ appView
+ .reporter()
+ .info(
+ new StringDiagnostic(
+ "Unexpected error while reading "
+ + clazz.type.toSourceString()
+ + "'s kotlin.Metadata: "
+ + e.getMessage()));
+ return getNoKotlinInfo();
+ }
+ }
+
+ private static KotlinClassLevelInfo getKotlinInfo(
+ AppView<?> appView,
+ DexClass clazz,
Consumer<DexEncodedMethod> keepByteCode,
DexAnnotation annotation)
throws KotlinMetadataException {
@@ -55,8 +100,8 @@
return createKotlinInfo(kotlin, clazz, kMetadata, appView, keepByteCode);
}
- public static boolean isLambda(AppView<?> appView, DexClass clazz)
- throws KotlinMetadataException {
+ public static boolean isLambda(
+ AppView<?> appView, DexClass clazz, Supplier<Boolean> reportUnknownMetadata) {
DexItemFactory dexItemFactory = appView.dexItemFactory();
Kotlin kotlin = dexItemFactory.kotlin;
Flavour flavour = getFlavour(clazz, kotlin);
@@ -69,16 +114,31 @@
return false;
}
Map<DexString, DexAnnotationElement> elementMap = toElementMap(metadataAnnotation.annotation);
- if (getKind(kotlin, elementMap) == SYNTHETIC_CLASS_KIND) {
- KotlinClassMetadata kMetadata = toKotlinClassMetadata(kotlin, elementMap);
- if (kMetadata instanceof SyntheticClass) {
- return ((SyntheticClass) kMetadata).isLambda();
+ try {
+ if (getKind(kotlin, elementMap) == SYNTHETIC_CLASS_KIND) {
+ KotlinClassMetadata kMetadata = toKotlinClassMetadata(kotlin, elementMap);
+ if (kMetadata instanceof SyntheticClass) {
+ return ((SyntheticClass) kMetadata).isLambda();
+ }
}
+ assert toKotlinClassMetadata(kotlin, elementMap) instanceof SyntheticClass
+ == (getKind(kotlin, elementMap) == SYNTHETIC_CLASS_KIND)
+ : "Synthetic class kinds should agree";
+ return false;
+ } catch (KotlinMetadataException exception) {
+ if (reportUnknownMetadata.get()) {
+ appView.reporter().warning(KotlinMetadataDiagnostic.unknownMetadataVersion());
+ }
+ appView
+ .reporter()
+ .info(
+ new StringDiagnostic(
+ "Class "
+ + clazz.type.toSourceString()
+ + " has malformed kotlin.Metadata: "
+ + exception.getMessage()));
+ return false;
}
- assert toKotlinClassMetadata(kotlin, elementMap) instanceof SyntheticClass
- == (getKind(kotlin, elementMap) == SYNTHETIC_CLASS_KIND)
- : "Synthetic class kinds should agree";
- return false;
}
public static boolean hasKotlinClassMetadataAnnotation(
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java
index 6b5d589..dd7f94a 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java
@@ -5,7 +5,6 @@
package com.android.tools.r8.kotlin;
import static com.android.tools.r8.kotlin.KotlinClassMetadataReader.hasKotlinClassMetadataAnnotation;
-import static com.android.tools.r8.kotlin.KotlinMetadataUtils.getInvalidKotlinInfo;
import static com.android.tools.r8.kotlin.KotlinMetadataUtils.getNoKotlinInfo;
import com.android.tools.r8.errors.Unreachable;
@@ -27,9 +26,9 @@
import com.android.tools.r8.shaking.Enqueuer;
import com.android.tools.r8.shaking.Enqueuer.EnqueuerDefinitionSupplier;
import com.android.tools.r8.shaking.KeepClassInfo;
-import com.android.tools.r8.utils.StringDiagnostic;
import com.google.common.collect.Sets;
import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
public class KotlinMetadataEnqueuerExtension extends EnqueuerAnalysis {
@@ -38,7 +37,7 @@
private final AppView<?> appView;
private final EnqueuerDefinitionSupplier enqueuerDefinitionSupplier;
private final Set<DexType> prunedTypes;
- private boolean reportedUnknownMetadataVersion;
+ private final AtomicBoolean reportedUnknownMetadataVersion = new AtomicBoolean(false);
public KotlinMetadataEnqueuerExtension(
AppView<?> appView,
@@ -69,52 +68,30 @@
enqueuer.forAllLiveClasses(
clazz -> {
assert clazz.getKotlinInfo().isNoKotlinInformation();
- try {
- if (enqueuer
- .getKeepInfo(clazz)
- .isKotlinMetadataRemovalAllowed(appView.options(), keepKotlinMetadata)) {
- if (KotlinClassMetadataReader.isLambda(appView, clazz)
- && clazz.hasClassInitializer()) {
- feedback.classInitializerMayBePostponed(clazz.getClassInitializer());
- }
- clazz.clearKotlinInfo();
- clazz.removeAnnotations(
- annotation ->
- annotation.getAnnotationType()
- == appView.dexItemFactory().kotlinMetadataType);
- } else {
- clazz.setKotlinInfo(
- KotlinClassMetadataReader.getKotlinInfo(
- clazz,
- appView,
- method -> keepByteCodeFunctions.add(method.getReference())));
- if (clazz.getEnclosingMethodAttribute() != null
- && clazz.getEnclosingMethodAttribute().getEnclosingMethod() != null) {
- localOrAnonymousClasses.add(clazz);
- }
+ if (enqueuer
+ .getKeepInfo(clazz)
+ .isKotlinMetadataRemovalAllowed(appView.options(), keepKotlinMetadata)) {
+ if (KotlinClassMetadataReader.isLambda(
+ appView, clazz, () -> reportedUnknownMetadataVersion.getAndSet(true))
+ && clazz.hasClassInitializer()) {
+ feedback.classInitializerMayBePostponed(clazz.getClassInitializer());
}
- } catch (KotlinMetadataException e) {
- appView
- .reporter()
- .info(
- new StringDiagnostic(
- "Class "
- + clazz.type.toSourceString()
- + " has malformed kotlin.Metadata: "
- + e.getMessage()));
- clazz.setKotlinInfo(getInvalidKotlinInfo());
- reportUnknownMetadataVersion();
- } catch (Throwable e) {
- appView
- .reporter()
- .info(
- new StringDiagnostic(
- "Unexpected error while reading "
- + clazz.type.toSourceString()
- + "'s kotlin.Metadata: "
- + e.getMessage()));
- clazz.setKotlinInfo(getNoKotlinInfo());
- reportUnknownMetadataVersion();
+ clazz.clearKotlinInfo();
+ clazz.removeAnnotations(
+ annotation ->
+ annotation.getAnnotationType()
+ == appView.dexItemFactory().kotlinMetadataType);
+ } else {
+ clazz.setKotlinInfo(
+ KotlinClassMetadataReader.getKotlinInfo(
+ appView,
+ clazz,
+ method -> keepByteCodeFunctions.add(method.getReference()),
+ () -> reportedUnknownMetadataVersion.getAndSet(true)));
+ if (clazz.getEnclosingMethodAttribute() != null
+ && clazz.getEnclosingMethodAttribute().getEnclosingMethod() != null) {
+ localOrAnonymousClasses.add(clazz);
+ }
}
});
for (DexProgramClass localOrAnonymousClass : localOrAnonymousClasses) {
@@ -167,13 +144,6 @@
});
}
- private void reportUnknownMetadataVersion() {
- if (!reportedUnknownMetadataVersion) {
- reportedUnknownMetadataVersion = true;
- appView.reporter().warning(KotlinMetadataDiagnostic.unknownMetadataVersion());
- }
- }
-
public class KotlinMetadataDefinitionSupplier implements DexDefinitionSupplier {
private final ProgramDefinition context;
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java
index e57fc63..858a57c 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java
@@ -20,6 +20,7 @@
import com.android.tools.r8.graph.DexValue.DexValueArray;
import com.android.tools.r8.graph.DexValue.DexValueInt;
import com.android.tools.r8.graph.DexValue.DexValueString;
+import com.android.tools.r8.utils.BooleanBox;
import com.android.tools.r8.utils.ConsumerUtils;
import com.android.tools.r8.utils.Pair;
import com.android.tools.r8.utils.ThreadUtils;
@@ -130,6 +131,7 @@
return;
}
final WriteMetadataFieldInfo writeMetadataFieldInfo = WriteMetadataFieldInfo.rewriteAll();
+ BooleanBox reportedUnknownMetadataVersion = new BooleanBox();
ThreadUtils.processItems(
appView.appInfo().classes(),
clazz -> {
@@ -138,8 +140,12 @@
return;
}
KotlinClassLevelInfo kotlinInfo =
- KotlinClassMetadataReader.getKotlinInfo(
- clazz, appView, ConsumerUtils.emptyConsumer(), metadata);
+ KotlinClassMetadataReader.getKotlinInfoFromAnnotation(
+ appView,
+ clazz,
+ metadata,
+ ConsumerUtils.emptyConsumer(),
+ reportedUnknownMetadataVersion::getAndSet);
if (kotlinInfo == getNoKotlinInfo()) {
return;
}
diff --git a/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureRewriter.java b/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureRewriter.java
index e4b384f..7b5a3ef 100644
--- a/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureRewriter.java
+++ b/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureRewriter.java
@@ -12,8 +12,9 @@
import com.android.tools.r8.graph.GenericSignatureContextBuilder;
import com.android.tools.r8.graph.GenericSignaturePartialTypeArgumentApplier;
import com.android.tools.r8.graph.GenericSignatureTypeRewriter;
-import com.android.tools.r8.utils.IterableUtils;
+import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.ThreadUtils;
+import java.util.Collection;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.function.BiPredicate;
@@ -35,7 +36,8 @@
this.contextBuilder = contextBuilder;
}
- public void runForD8(Iterable<? extends DexProgramClass> classes, ExecutorService executorService)
+ public void runForD8(
+ Collection<? extends DexProgramClass> classes, ExecutorService executorService)
throws ExecutionException {
if (appView.getNamingLens().isIdentityLens()) {
return;
@@ -43,7 +45,7 @@
run(classes, executorService);
}
- public void run(Iterable<? extends DexProgramClass> classes, ExecutorService executorService)
+ public void run(Collection<? extends DexProgramClass> classes, ExecutorService executorService)
throws ExecutionException {
// Rewrite signature annotations for applications that are minified or if we have liveness
// information, since we could have pruned types.
@@ -66,7 +68,7 @@
ThreadUtils.processItems(
// Final merging of classes can introduce pruned types that still exists in classes, we
// therefore prune them from work here.
- IterableUtils.filter(classes, clazz -> !wasPruned.test(clazz.getType())),
+ ListUtils.filter(classes, clazz -> !wasPruned.test(clazz.getType())),
clazz -> {
GenericSignaturePartialTypeArgumentApplier classArgumentApplier =
contextBuilder != null
diff --git a/src/main/java/com/android/tools/r8/optimize/RedundantBridgeRemover.java b/src/main/java/com/android/tools/r8/optimize/RedundantBridgeRemover.java
index 815a807..763c40f 100644
--- a/src/main/java/com/android/tools/r8/optimize/RedundantBridgeRemover.java
+++ b/src/main/java/com/android/tools/r8/optimize/RedundantBridgeRemover.java
@@ -3,8 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.optimize;
-import static com.android.tools.r8.utils.ThreadUtils.processItems;
-
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -21,6 +19,7 @@
import com.android.tools.r8.optimize.redundantbridgeremoval.RedundantBridgeRemovalLens;
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.collections.ProgramMethodSet;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@@ -134,7 +133,7 @@
RedundantBridgeRemovalLens.Builder lensBuilder, ExecutorService executorService)
throws ExecutionException {
Map<DexProgramClass, ProgramMethodSet> bridgesToRemove = new ConcurrentHashMap<>();
- processItems(
+ ThreadUtils.processItems(
appView.appInfo().classes(),
clazz -> {
ProgramMethodSet bridgesToRemoveForClass = ProgramMethodSet.create();
diff --git a/src/main/java/com/android/tools/r8/shaking/DiscardedChecker.java b/src/main/java/com/android/tools/r8/shaking/DiscardedChecker.java
index dfc170e..cbc4940 100644
--- a/src/main/java/com/android/tools/r8/shaking/DiscardedChecker.java
+++ b/src/main/java/com/android/tools/r8/shaking/DiscardedChecker.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.ThreadUtils;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
@@ -49,7 +50,7 @@
}
public List<ProgramDefinition> run(
- Iterable<DexProgramClass> classes, ExecutorService executorService)
+ Collection<DexProgramClass> classes, ExecutorService executorService)
throws ExecutionException {
assert failed.isEmpty();
diff --git a/src/main/java/com/android/tools/r8/shaking/EnqueuerDeferredTracingImpl.java b/src/main/java/com/android/tools/r8/shaking/EnqueuerDeferredTracingImpl.java
index 41c48cb..650aa8b 100644
--- a/src/main/java/com/android/tools/r8/shaking/EnqueuerDeferredTracingImpl.java
+++ b/src/main/java/com/android/tools/r8/shaking/EnqueuerDeferredTracingImpl.java
@@ -32,6 +32,7 @@
import com.android.tools.r8.shaking.EnqueuerWorklist.EnqueuerAction;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.ThreadUtils.WorkLoad;
import com.android.tools.r8.utils.Timing;
import com.android.tools.r8.utils.collections.ProgramFieldMap;
import com.android.tools.r8.utils.collections.ProgramFieldSet;
@@ -237,8 +238,9 @@
new ConcurrentHashMap<>();
ThreadUtils.processItems(
methodsToProcess,
- method -> rewriteMethod(method, initializedClassesWithContexts, prunedFields),
- executorService);
+ (method, ignored) -> rewriteMethod(method, initializedClassesWithContexts, prunedFields),
+ executorService,
+ WorkLoad.HEAVY);
// Register new InitClass instructions.
initializedClassesWithContexts.forEach(
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
index 95105f8..301d7b4 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
@@ -65,7 +65,7 @@
// TODO(b/214901256): Sharing of synthetic classes may lead to duplicate method errors.
public final SyntheticKind NON_FIXED_INIT_TYPE_ARGUMENT =
generator.forNonSharableInstanceClass("$IA");
- public final SyntheticKind CONST_DYNAMIC = generator.forInstanceClass("$Condy");
+ public final SyntheticKind CONST_DYNAMIC = generator.forNonSharableInstanceClass("$Condy");
// Method synthetics.
public final SyntheticKind ENUM_UNBOXING_CHECK_NOT_ZERO_METHOD =
diff --git a/src/main/java/com/android/tools/r8/utils/BooleanBox.java b/src/main/java/com/android/tools/r8/utils/BooleanBox.java
index 5ad9494..554176e 100644
--- a/src/main/java/com/android/tools/r8/utils/BooleanBox.java
+++ b/src/main/java/com/android/tools/r8/utils/BooleanBox.java
@@ -59,4 +59,10 @@
public boolean isAssigned() {
return assigned;
}
+
+ public Boolean getAndSet() {
+ boolean current = get();
+ set();
+ return current;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/utils/ThreadUtils.java b/src/main/java/com/android/tools/r8/utils/ThreadUtils.java
index b0a6710..45c36b6 100644
--- a/src/main/java/com/android/tools/r8/utils/ThreadUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ThreadUtils.java
@@ -5,6 +5,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.utils.ListUtils.ReferenceAndIntConsumer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
@@ -16,9 +17,37 @@
import java.util.concurrent.Executors;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.Future;
+import java.util.function.Consumer;
public class ThreadUtils {
+ public enum WorkLoad {
+ // The threshold for HEAVY is basically just a fan-out when we have two items to process.
+ HEAVY(2),
+ // The threshold for LIGHT has been found by running TiviIncremental benchmark in different
+ // configurations. For partitioning inputs in buckets of 3 and use threading on 4 or more was
+ // slightly better than threading on 3:
+ // Buckets of 3 with threshold 4:
+ // TiviIncrementalLibrary(RunTimeRaw): 28076 ms
+ // TiviIncrementalMerge(RunTimeRaw): 1429 ms
+ // TiviIncrementalProgram(RunTimeRaw): 26374 ms
+ // Buckets of 3 with threshold 3:
+ // TiviIncrementalLibrary(RunTimeRaw): 30347 ms
+ // TiviIncrementalMerge(RunTimeRaw): 1558 ms
+ // TiviIncrementalProgram(RunTimeRaw): 26638 ms
+ LIGHT(4);
+
+ private final int threshold;
+
+ WorkLoad(int threshold) {
+ this.threshold = threshold;
+ }
+
+ public int getThreshold() {
+ return threshold;
+ }
+ }
+
public static final int NOT_SPECIFIED = -1;
public static <T> Future<T> processAsynchronously(
@@ -71,18 +100,26 @@
return awaitFuturesWithResults(futures);
}
- public static <T, E extends Exception> void processItems(
- Iterable<T> items, ThrowingConsumer<T, E> consumer, ExecutorService executorService)
+ public static <T> void processItems(
+ Collection<T> items, Consumer<T> consumer, ExecutorService executorService)
throws ExecutionException {
- processItems(items, (item, i) -> consumer.accept(item), executorService);
+ processItems(items, (item, i) -> consumer.accept(item), executorService, WorkLoad.LIGHT);
}
- public static <T, E extends Exception> void processItems(
- Iterable<T> items,
- ThrowingReferenceIntConsumer<T, E> consumer,
- ExecutorService executorService)
+ public static <T> void processItems(
+ Collection<T> items,
+ ReferenceAndIntConsumer<T> consumer,
+ ExecutorService executorService,
+ WorkLoad workLoad)
throws ExecutionException {
- processItems(items::forEach, consumer, executorService);
+ if (items.size() >= workLoad.getThreshold()) {
+ processItems(items::forEach, consumer::accept, executorService);
+ } else {
+ int counter = 0;
+ for (T item : items) {
+ consumer.accept(item, counter++);
+ }
+ }
}
public static <T, E extends Exception> void processItems(
diff --git a/src/test/java/com/android/tools/r8/desugar/constantdynamic/ConstantDynamicMultipleConstantsUsingSameSymbolicReferenceTest.java b/src/test/java/com/android/tools/r8/desugar/constantdynamic/ConstantDynamicMultipleConstantsUsingSameSymbolicReferenceTest.java
new file mode 100644
index 0000000..5ba0396
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/constantdynamic/ConstantDynamicMultipleConstantsUsingSameSymbolicReferenceTest.java
@@ -0,0 +1,141 @@
+// 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.constantdynamic;
+
+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.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.cf.CfVersion;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import java.io.IOException;
+import java.lang.invoke.MethodHandles;
+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 ConstantDynamicMultipleConstantsUsingSameSymbolicReferenceTest extends TestBase {
+
+ @Parameter() public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
+ }
+
+ private static final Class<?> MAIN_CLASS = Main.class;
+ private static final String EXPECTED_OUTPUT = StringUtils.lines("true");
+
+ @Test
+ public void testReference() throws Exception {
+ parameters.assumeJvmTestParameters();
+ assumeTrue(parameters.getRuntime().asCf().isNewerThanOrEqual(CfVm.JDK11));
+ testForJvm(parameters)
+ .addProgramClassFileData(getTransformedClasses())
+ .addProgramClasses(Main.class)
+ .run(parameters.getRuntime(), MAIN_CLASS)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ }
+
+ @Test
+ public void testDesugaring() throws Exception {
+ testForDesugaring(parameters)
+ .addProgramClassFileData(getTransformedClasses())
+ .addProgramClasses(Main.class)
+ .run(parameters.getRuntime(), MAIN_CLASS)
+ .applyIf(
+ // When not desugaring the CF code requires JDK 11.
+ DesugarTestConfiguration::isNotDesugared,
+ r -> {
+ if (parameters.isCfRuntime()
+ && parameters.getRuntime().asCf().isNewerThanOrEqual(CfVm.JDK11)) {
+ r.assertSuccessWithOutput(EXPECTED_OUTPUT);
+ } else {
+ r.assertFailureWithErrorThatThrows(UnsupportedClassVersionError.class);
+ }
+ })
+ .applyIf(
+ DesugarTestConfiguration::isDesugared, r -> r.assertSuccessWithOutput(EXPECTED_OUTPUT));
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ parameters.assumeR8TestParameters();
+ testForR8(parameters.getBackend())
+ .addProgramClassFileData(getTransformedClasses())
+ .addProgramClasses(Main.class)
+ .setMinApi(parameters)
+ .addKeepMainRule(MAIN_CLASS)
+ // TODO(b/198142613): There should not be a warnings on class references which are
+ // desugared away.
+ .applyIf(
+ parameters.getApiLevel().isLessThan(AndroidApiLevel.O),
+ b -> b.addDontWarn("java.lang.invoke.MethodHandles$Lookup"))
+ // TODO(b/198142625): Support CONSTANT_Dynamic output for class files.
+ .applyIf(
+ parameters.isCfRuntime(),
+ r -> {
+ assertThrows(
+ CompilationFailedException.class,
+ () ->
+ r.compileWithExpectedDiagnostics(
+ diagnostics -> {
+ diagnostics.assertOnlyErrors();
+ diagnostics.assertErrorsMatch(
+ diagnosticMessage(
+ containsString(
+ "Unsupported dynamic constant (not desugaring)")));
+ }));
+ },
+ r ->
+ r.run(parameters.getRuntime(), MAIN_CLASS)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT));
+ }
+
+ // When ASM writes two dynamic constants with the same BSM and arguments they are canonicialized
+ // into the same symbolic reference.
+ private byte[] getTransformedClasses() throws IOException {
+ return transformer(A.class)
+ .setVersion(CfVersion.V11)
+ .transformConstStringToConstantDynamic(
+ "condy1", A.class, "myConstant", false, "constantName", Object.class)
+ .transformConstStringToConstantDynamic(
+ "condy2", A.class, "myConstant", false, "constantName", Object.class)
+ .transform();
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ // This prints "true" due to the ASM canonicalization of the ConstantDynamic when writing.
+ System.out.println(A.getConstant1() == A.getConstant2());
+ }
+ }
+
+ public static class A {
+
+ public static Object getConstant1() {
+ return "condy1"; // Will be transformed to Constant_DYNAMIC.
+ }
+
+ public static Object getConstant2() {
+ return "condy2"; // Will be transformed to Constant_DYNAMIC.
+ }
+
+ private static Object myConstant(MethodHandles.Lookup lookup, String name, Class<?> type) {
+ return new Object();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/constantdynamic/ConstantDynamicMultipleConstantsWithDifferentSymbolicReferenceUsingSameBSMAndArgumentsTest.java b/src/test/java/com/android/tools/r8/desugar/constantdynamic/ConstantDynamicMultipleConstantsWithDifferentSymbolicReferenceUsingSameBSMAndArgumentsTest.java
new file mode 100644
index 0000000..3bf967d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/constantdynamic/ConstantDynamicMultipleConstantsWithDifferentSymbolicReferenceUsingSameBSMAndArgumentsTest.java
@@ -0,0 +1,592 @@
+// 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.constantdynamic;
+
+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.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.cf.CfVersion;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import java.io.IOException;
+import java.lang.invoke.MethodHandles;
+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 ConstantDynamicMultipleConstantsWithDifferentSymbolicReferenceUsingSameBSMAndArgumentsTest
+ extends TestBase {
+
+ @Parameter() public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
+ }
+
+ private static final String MAIN_CLASS = "A";
+ private static final String EXPECTED_OUTPUT = StringUtils.lines("false");
+
+ @Test
+ public void testReference() throws Exception {
+ parameters.assumeJvmTestParameters();
+ assumeTrue(parameters.getRuntime().asCf().isNewerThanOrEqual(CfVm.JDK11));
+ testForJvm(parameters)
+ .addProgramClassFileData(classFileData)
+ .disassemble()
+ .run(parameters.getRuntime(), MAIN_CLASS)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ }
+
+ @Test
+ public void testDesugaring() throws Exception {
+ testForDesugaring(parameters)
+ .addProgramClassFileData(classFileData)
+ .run(parameters.getRuntime(), MAIN_CLASS)
+ .applyIf(
+ // When not desugaring the CF code requires JDK 11.
+ DesugarTestConfiguration::isNotDesugared,
+ r -> {
+ if (parameters.isCfRuntime()
+ && parameters.getRuntime().asCf().isNewerThanOrEqual(CfVm.JDK11)) {
+ r.assertSuccessWithOutput(EXPECTED_OUTPUT);
+ } else {
+ r.assertFailureWithErrorThatThrows(UnsupportedClassVersionError.class);
+ }
+ })
+ .applyIf(
+ DesugarTestConfiguration::isDesugared, r -> r.assertSuccessWithOutput(EXPECTED_OUTPUT));
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ parameters.assumeR8TestParameters();
+ testForR8(parameters.getBackend())
+ .addProgramClassFileData(classFileData)
+ .setMinApi(parameters)
+ .addKeepMainRule(MAIN_CLASS)
+ // TODO(b/198142613): There should not be a warnings on class references which are
+ // desugared away.
+ .applyIf(
+ parameters.getApiLevel().isLessThan(AndroidApiLevel.O),
+ b -> b.addDontWarn("java.lang.invoke.MethodHandles$Lookup"))
+ // TODO(b/198142625): Support CONSTANT_Dynamic output for class files.
+ .applyIf(
+ parameters.isCfRuntime(),
+ r -> {
+ assertThrows(
+ CompilationFailedException.class,
+ () ->
+ r.compileWithExpectedDiagnostics(
+ diagnostics -> {
+ diagnostics.assertOnlyErrors();
+ diagnostics.assertErrorsMatch(
+ diagnosticMessage(
+ containsString(
+ "Unsupported dynamic constant (not desugaring)")));
+ }));
+ },
+ r ->
+ r.run(parameters.getRuntime(), MAIN_CLASS)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT));
+ }
+
+ /*
+ This test was supposed to use the following test classes to have two dynamic constants
+ using exactly the same bootstrap method and arguments as set up in getTransformedClasses
+ below. However, ASM will canonicalize both dynamic constants and bootstrap methods, so instead
+ a class file directly from bytes is used.
+ */
+
+ private byte[] getTransformedClasses() throws IOException {
+ return transformer(A.class)
+ .setVersion(CfVersion.V11)
+ .transformConstStringToConstantDynamic(
+ "condy1", A.class, "myConstant", false, "constantName", Object.class)
+ .transformConstStringToConstantDynamic(
+ "condy2", A.class, "myConstant", false, "constantName", Object.class)
+ .transform();
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ System.out.println(A.getConstant1() == A.getConstant2());
+ }
+ }
+
+ public static class A {
+
+ public static Object getConstant1() {
+ return "condy1"; // Will be transformed to Constant_DYNAMIC.
+ }
+
+ public static Object getConstant2() {
+ return "condy2"; // Will be transformed to Constant_DYNAMIC.
+ }
+
+ private static Object myConstant(MethodHandles.Lookup lookup, String name, Class<?> type) {
+ return new Object();
+ }
+ }
+
+/*
+
+Class file bytes for the following class file:
+
+Classfile A.class
+ Last modified Mar 17, 2023; size 1364 bytes
+ SHA-256 checksum 4379e359d727521479cee1aa5b6d711090b2777553454d9b667fe502a1b0daa9
+ Compiled from "A.java"
+public class A
+ minor version: 0
+ major version: 55
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #2 // A
+ super_class: #4 // java/lang/Object
+ interfaces: 0, fields: 0, methods: 5, attributes: 3
+Constant pool:
+ #1 = Utf8 A
+ #2 = Class #1 // A
+ #3 = Utf8 java/lang/Object
+ #4 = Class #3 // java/lang/Object
+ #5 = Utf8 A.java
+ #6 = Utf8 java/lang/invoke/MethodHandles$Lookup
+ #7 = Class #6 // java/lang/invoke/MethodHandles$Lookup
+ #8 = Utf8 java/lang/invoke/MethodHandles
+ #9 = Class #8 // java/lang/invoke/MethodHandles
+ #10 = Utf8 Lookup
+ #11 = Utf8 <init>
+ #12 = Utf8 ()V
+ #13 = NameAndType #11:#12 // "<init>":()V
+ #14 = Methodref #4.#13 // java/lang/Object."<init>":()V
+ #15 = Utf8 this
+ #16 = Utf8 LA;
+ #17 = Utf8 getConstant1
+ #18 = Utf8 ()Ljava/lang/Object;
+ #19 = Utf8 myConstant
+ #20 = Utf8 (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Object;
+ #21 = NameAndType #19:#20 // myConstant:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Object;
+ #22 = Methodref #2.#21 // A.myConstant:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Object;
+ #23 = MethodHandle 6:#22 // REF_invokeStatic A.myConstant:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Object;
+ #24 = Utf8 constantName
+ #25 = Utf8 Ljava/lang/Object;
+ #26 = NameAndType #24:#25 // constantName:Ljava/lang/Object;
+ #27 = Dynamic #0:#26 // #0:constantName:Ljava/lang/Object;
+ #28 = Utf8 getConstant2
+ #29 = Dynamic #1:#26 // #1:constantName:Ljava/lang/Object;
+ #30 = Utf8 (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class<*>;)Ljava/lang/Object;
+ #31 = Utf8 lookup
+ #32 = Utf8 Ljava/lang/invoke/MethodHandles$Lookup;
+ #33 = Utf8 name
+ #34 = Utf8 Ljava/lang/String;
+ #35 = Utf8 type
+ #36 = Utf8 Ljava/lang/Class<*>;
+ #37 = Utf8 Ljava/lang/Class;
+ #38 = Utf8 main
+ #39 = Utf8 ([Ljava/lang/String;)V
+ #40 = Utf8 java/lang/System
+ #41 = Class #40 // java/lang/System
+ #42 = Utf8 out
+ #43 = Utf8 Ljava/io/PrintStream;
+ #44 = NameAndType #42:#43 // out:Ljava/io/PrintStream;
+ #45 = Fieldref #41.#44 // java/lang/System.out:Ljava/io/PrintStream;
+ #46 = NameAndType #17:#18 // getConstant1:()Ljava/lang/Object;
+ #47 = Methodref #2.#46 // A.getConstant1:()Ljava/lang/Object;
+ #48 = NameAndType #28:#18 // getConstant2:()Ljava/lang/Object;
+ #49 = Methodref #2.#48 // A.getConstant2:()Ljava/lang/Object;
+ #50 = Utf8 java/io/PrintStream
+ #51 = Class #50 // java/io/PrintStream
+ #52 = Utf8 [Ljava/lang/String;
+ #53 = Class #52 // "[Ljava/lang/String;"
+ #54 = Utf8 println
+ #55 = Utf8 (Z)V
+ #56 = NameAndType #54:#55 // println:(Z)V
+ #57 = Methodref #51.#56 // java/io/PrintStream.println:(Z)V
+ #58 = Utf8 Code
+ #59 = Utf8 LineNumberTable
+ #60 = Utf8 LocalVariableTable
+ #61 = Utf8 LocalVariableTypeTable
+ #62 = Utf8 Signature
+ #63 = Utf8 StackMapTable
+ #64 = Utf8 InnerClasses
+ #65 = Utf8 SourceFile
+ #66 = Utf8 BootstrapMethods
+{
+ public A();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ 0: aload_0
+ 1: invokespecial #14 // Method java/lang/Object."<init>":()V
+ 4: return
+ LineNumberTable:
+ line 132: 0
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this LA;
+
+ public static java.lang.Object getConstant1();
+ descriptor: ()Ljava/lang/Object;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=1, locals=0, args_size=0
+ 0: ldc #27 // Dynamic #0:constantName:Ljava/lang/Object;
+ 2: areturn
+ LineNumberTable:
+ line 135: 0
+
+ public static java.lang.Object getConstant2();
+ descriptor: ()Ljava/lang/Object;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=1, locals=0, args_size=0
+ 0: ldc #29 // Dynamic #1:constantName:Ljava/lang/Object;
+ 2: areturn
+ LineNumberTable:
+ line 139: 0
+
+ private static java.lang.Object myConstant(java.lang.invoke.MethodHandles$Lookup, java.lang.String, java.lang.Class<?>);
+ descriptor: (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Object;
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=3, args_size=3
+ 0: new #4 // class java/lang/Object
+ 3: dup
+ 4: invokespecial #14 // Method java/lang/Object."<init>":()V
+ 7: areturn
+ LineNumberTable:
+ line 143: 0
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 8 0 lookup Ljava/lang/invoke/MethodHandles$Lookup;
+ 0 8 1 name Ljava/lang/String;
+ 0 8 2 type Ljava/lang/Class;
+ LocalVariableTypeTable:
+ Start Length Slot Name Signature
+ 0 8 2 type Ljava/lang/Class<*>;
+ Signature: #30 // (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class<*>;)Ljava/lang/Object;
+
+ public static void main(java.lang.String[]);
+ descriptor: ([Ljava/lang/String;)V
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=1, args_size=1
+ 0: getstatic #45 // Field java/lang/System.out:Ljava/io/PrintStream;
+ 3: invokestatic #47 // Method getConstant1:()Ljava/lang/Object;
+ 6: invokestatic #49 // Method getConstant2:()Ljava/lang/Object;
+ 9: if_acmpne 16
+ 12: iconst_1
+ 13: goto 17
+ 16: iconst_0
+ 17: invokevirtual #57 // Method java/io/PrintStream.println:(Z)V
+ 20: return
+ StackMapTable: number_of_entries = 2
+ frame_type = 80 // same_locals_1_stack_item
+ stack = [ class java/io/PrintStream ]
+ frame_type = 255 // full_frame
+ offset_delta = 0
+ locals = [ class "[Ljava/lang/String;" ]
+ stack = [ class java/io/PrintStream, int ]
+ LineNumberTable:
+ line 12: 0
+ line 13: 20
+}
+InnerClasses:
+public static final #10= #7 of #9; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
+ SourceFile: "A.java"
+ BootstrapMethods:
+ 0: #23 REF_invokeStatic A.myConstant:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Object;
+ Method arguments:
+ 1: #23 REF_invokeStatic A.myConstant:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Object;
+ Method arguments:
+*/
+
+ private byte[] classFileData = {
+ -54, -2, -70, -66, 0, 0, 0, 55, 0, 67, 1, 0, 1, 65, 7, 0, 1,
+ 1, 0, 16, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 79, 98, 106,
+ 101, 99, 116, 7, 0, 3, 1, 0, 6, 65, 46, 106, 97, 118, 97, 1,
+ 0, 37, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 105, 110, 118, 111,
+ 107, 101, 47, 77, 101, 116, 104, 111, 100, 72, 97, 110, 100, 108, 101, 115,
+ 36, 76, 111, 111, 107, 117, 112, 7, 0, 6, 1, 0, 30, 106, 97, 118,
+ 97, 47, 108, 97, 110, 103, 47, 105, 110, 118, 111, 107, 101, 47, 77, 101,
+ 116, 104, 111, 100, 72, 97, 110, 100, 108, 101, 115, 7, 0, 8, 1, 0,
+ 6, 76, 111, 111, 107, 117, 112, 1, 0, 6, 60, 105, 110, 105, 116, 62,
+ 1, 0, 3, 40, 41, 86, 12, 0, 11, 0, 12, 10, 0, 4, 0, 13,
+ 1, 0, 4, 116, 104, 105, 115, 1, 0, 3, 76, 65, 59, 1, 0, 12,
+ 103, 101, 116, 67, 111, 110, 115, 116, 97, 110, 116, 49, 1, 0, 20, 40,
+ 41, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 79, 98, 106, 101,
+ 99, 116, 59, 1, 0, 10, 109, 121, 67, 111, 110, 115, 116, 97, 110, 116,
+ 1, 0, 94, 40, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 105,
+ 110, 118, 111, 107, 101, 47, 77, 101, 116, 104, 111, 100, 72, 97, 110, 100,
+ 108, 101, 115, 36, 76, 111, 111, 107, 117, 112, 59, 76, 106, 97, 118, 97,
+ 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 76, 106, 97,
+ 118, 97, 47, 108, 97, 110, 103, 47, 67, 108, 97, 115, 115, 59, 41, 76,
+ 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 79, 98, 106, 101, 99, 116,
+ 59, 12, 0, 19, 0, 20, 10, 0, 2, 0, 21, 15, 6, 0, 22, 1,
+ 0, 12, 99, 111, 110, 115, 116, 97, 110, 116, 78, 97, 109, 101, 1, 0,
+ 18, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 79, 98, 106, 101,
+ 99, 116, 59, 12, 0, 24, 0, 25, 17, 0, 0, 0, 26, 1, 0, 12,
+ 103, 101, 116, 67, 111, 110, 115, 116, 97, 110, 116, 50, 17, 0, 1, 0,
+ 26, 1, 0, 97, 40, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47,
+ 105, 110, 118, 111, 107, 101, 47, 77, 101, 116, 104, 111, 100, 72, 97, 110,
+ 100, 108, 101, 115, 36, 76, 111, 111, 107, 117, 112, 59, 76, 106, 97, 118,
+ 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 76, 106,
+ 97, 118, 97, 47, 108, 97, 110, 103, 47, 67, 108, 97, 115, 115, 60, 42,
+ 62, 59, 41, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 79, 98,
+ 106, 101, 99, 116, 59, 1, 0, 6, 108, 111, 111, 107, 117, 112, 1, 0,
+ 39, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 105, 110, 118, 111,
+ 107, 101, 47, 77, 101, 116, 104, 111, 100, 72, 97, 110, 100, 108, 101, 115,
+ 36, 76, 111, 111, 107, 117, 112, 59, 1, 0, 4, 110, 97, 109, 101, 1,
+ 0, 18, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114,
+ 105, 110, 103, 59, 1, 0, 4, 116, 121, 112, 101, 1, 0, 20, 76, 106,
+ 97, 118, 97, 47, 108, 97, 110, 103, 47, 67, 108, 97, 115, 115, 60, 42,
+ 62, 59, 1, 0, 17, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47,
+ 67, 108, 97, 115, 115, 59, 1, 0, 4, 109, 97, 105, 110, 1, 0, 22,
+ 40, 91, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114,
+ 105, 110, 103, 59, 41, 86, 1, 0, 16, 106, 97, 118, 97, 47, 108, 97,
+ 110, 103, 47, 83, 121, 115, 116, 101, 109, 7, 0, 40, 1, 0, 3, 111,
+ 117, 116, 1, 0, 21, 76, 106, 97, 118, 97, 47, 105, 111, 47, 80, 114,
+ 105, 110, 116, 83, 116, 114, 101, 97, 109, 59, 12, 0, 42, 0, 43, 9,
+ 0, 41, 0, 44, 12, 0, 17, 0, 18, 10, 0, 2, 0, 46, 12, 0,
+ 28, 0, 18, 10, 0, 2, 0, 48, 1, 0, 19, 106, 97, 118, 97, 47,
+ 105, 111, 47, 80, 114, 105, 110, 116, 83, 116, 114, 101, 97, 109, 7, 0,
+ 50, 1, 0, 19, 91, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47,
+ 83, 116, 114, 105, 110, 103, 59, 7, 0, 52, 1, 0, 7, 112, 114, 105,
+ 110, 116, 108, 110, 1, 0, 4, 40, 90, 41, 86, 12, 0, 54, 0, 55,
+ 10, 0, 51, 0, 56, 1, 0, 4, 67, 111, 100, 101, 1, 0, 15, 76,
+ 105, 110, 101, 78, 117, 109, 98, 101, 114, 84, 97, 98, 108, 101, 1, 0,
+ 18, 76, 111, 99, 97, 108, 86, 97, 114, 105, 97, 98, 108, 101, 84, 97,
+ 98, 108, 101, 1, 0, 22, 76, 111, 99, 97, 108, 86, 97, 114, 105, 97,
+ 98, 108, 101, 84, 121, 112, 101, 84, 97, 98, 108, 101, 1, 0, 9, 83,
+ 105, 103, 110, 97, 116, 117, 114, 101, 1, 0, 13, 83, 116, 97, 99, 107,
+ 77, 97, 112, 84, 97, 98, 108, 101, 1, 0, 12, 73, 110, 110, 101, 114,
+ 67, 108, 97, 115, 115, 101, 115, 1, 0, 10, 83, 111, 117, 114, 99, 101,
+ 70, 105, 108, 101, 1, 0, 16, 66, 111, 111, 116, 115, 116, 114, 97, 112,
+ 77, 101, 116, 104, 111, 100, 115, 0, 33, 0, 2, 0, 4, 0, 0, 0,
+ 0, 0, 5, 0, 1, 0, 11, 0, 12, 0, 1, 0, 58, 0, 0, 0,
+ 47, 0, 1, 0, 1, 0, 0, 0, 5, 42, -73, 0, 14, -79, 0, 0,
+ 0, 2, 0, 59, 0, 0, 0, 6, 0, 1, 0, 0, 0, -124, 0, 60,
+ 0, 0, 0, 12, 0, 1, 0, 0, 0, 5, 0, 15, 0, 16, 0, 0,
+ 0, 9, 0, 17, 0, 18, 0, 1, 0, 58, 0, 0, 0, 27, 0, 1,
+ 0, 0, 0, 0, 0, 3, 18, 27, -80, 0, 0, 0, 1, 0, 59, 0,
+ 0, 0, 6, 0, 1, 0, 0, 0, -121, 0, 9, 0, 28, 0, 18, 0,
+ 1, 0, 58, 0, 0, 0, 27, 0, 1, 0, 0, 0, 0, 0, 3, 18,
+ 29, -80, 0, 0, 0, 1, 0, 59, 0, 0, 0, 6, 0, 1, 0, 0,
+ 0, -117, 0, 10, 0, 19, 0, 20, 0, 2, 0, 58, 0, 0, 0, 88,
+ 0, 2, 0, 3, 0, 0, 0, 8, -69, 0, 4, 89, -73, 0, 14, -80,
+ 0, 0, 0, 3, 0, 59, 0, 0, 0, 6, 0, 1, 0, 0, 0, -113,
+ 0, 60, 0, 0, 0, 32, 0, 3, 0, 0, 0, 8, 0, 31, 0, 32,
+ 0, 0, 0, 0, 0, 8, 0, 33, 0, 34, 0, 1, 0, 0, 0, 8,
+ 0, 35, 0, 37, 0, 2, 0, 61, 0, 0, 0, 12, 0, 1, 0, 0,
+ 0, 8, 0, 35, 0, 36, 0, 2, 0, 62, 0, 0, 0, 2, 0, 30,
+ 0, 9, 0, 38, 0, 39, 0, 1, 0, 58, 0, 0, 0, 75, 0, 3,
+ 0, 1, 0, 0, 0, 21, -78, 0, 45, -72, 0, 47, -72, 0, 49, -90,
+ 0, 7, 4, -89, 0, 4, 3, -74, 0, 57, -79, 0, 0, 0, 2, 0,
+ 63, 0, 0, 0, 20, 0, 2, 80, 7, 0, 51, -1, 0, 0, 0, 1,
+ 7, 0, 53, 0, 2, 7, 0, 51, 1, 0, 59, 0, 0, 0, 10, 0,
+ 2, 0, 0, 0, 12, 0, 20, 0, 13, 0, 3, 0, 64, 0, 0, 0,
+ 10, 0, 1, 0, 7, 0, 9, 0, 10, 0, 25, 0, 65, 0, 0, 0,
+ 2, 0, 5, 0, 66, 0, 0, 0, 10, 0, 2, 0, 23, 0, 0, 0,
+ 23, 0, 0
+ };
+
+ /*
+
+ The class file bytes above was generated from the following ASM visitor code using ASM with the
+ patch below applied (was applied on 443339a964352dcec4dd3915de8f13188920d3ac).
+
+ The thing to note is that the two calls
+
+ methodVisitor.visitLdcInsn(new ConstantDynamic(...));
+
+ have the exact same arguments, which ASM will canonicalize making it impossible to
+ write the test using standard ASM visitor.
+
+ import java.nio.file.Files;
+ import java.nio.file.Paths;
+
+ import org.objectweb.asm.AnnotationVisitor;
+ import org.objectweb.asm.Attribute;
+ import org.objectweb.asm.ClassReader;
+ import org.objectweb.asm.ClassWriter;
+ import org.objectweb.asm.ConstantDynamic;
+ import org.objectweb.asm.FieldVisitor;
+ import org.objectweb.asm.Handle;
+ import org.objectweb.asm.Label;
+ import org.objectweb.asm.MethodVisitor;
+ import org.objectweb.asm.Opcodes;
+ import org.objectweb.asm.RecordComponentVisitor;
+ import org.objectweb.asm.Type;
+ import org.objectweb.asm.TypePath;
+
+ public class DC implements Opcodes {
+
+ public static byte[] dump () throws Exception {
+
+ ClassWriter classWriter = new ClassWriter(0);
+ FieldVisitor fieldVisitor;
+ RecordComponentVisitor recordComponentVisitor;
+ MethodVisitor methodVisitor;
+ AnnotationVisitor annotationVisitor0;
+
+ classWriter.visit(V11, ACC_PUBLIC | ACC_SUPER, "A", null, "java/lang/Object", null);
+
+ classWriter.visitSource("A.java", null);
+
+ classWriter.visitInnerClass("java/lang/invoke/MethodHandles$Lookup", "java/lang/invoke/MethodHandles", "Lookup", ACC_PUBLIC | ACC_FINAL | ACC_STATIC);
+
+ {
+ methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
+ methodVisitor.visitCode();
+ Label label0 = new Label();
+ methodVisitor.visitLabel(label0);
+ methodVisitor.visitLineNumber(132, label0);
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
+ methodVisitor.visitInsn(RETURN);
+ Label label1 = new Label();
+ methodVisitor.visitLabel(label1);
+ methodVisitor.visitLocalVariable("this", "LA;", null, label0, label1, 0);
+ methodVisitor.visitMaxs(1, 1);
+ methodVisitor.visitEnd();
+ }
+ {
+ methodVisitor = classWriter.visitMethod(ACC_PUBLIC | ACC_STATIC, "getConstant1", "()Ljava/lang/Object;", null, null);
+ methodVisitor.visitCode();
+ Label label0 = new Label();
+ methodVisitor.visitLabel(label0);
+ methodVisitor.visitLineNumber(135, label0);
+ methodVisitor.visitLdcInsn(new ConstantDynamic("constantName", "Ljava/lang/Object;", new Handle(Opcodes.H_INVOKESTATIC, "A", "myConstant", "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Object;", false), new Object[] {}));
+ methodVisitor.visitInsn(ARETURN);
+ methodVisitor.visitMaxs(1, 0);
+ methodVisitor.visitEnd();
+ }
+ {
+ methodVisitor = classWriter.visitMethod(ACC_PUBLIC | ACC_STATIC, "getConstant2", "()Ljava/lang/Object;", null, null);
+ methodVisitor.visitCode();
+ Label label0 = new Label();
+ methodVisitor.visitLabel(label0);
+ methodVisitor.visitLineNumber(139, label0);
+ methodVisitor.visitLdcInsn(new ConstantDynamic("constantName", "Ljava/lang/Object;", new Handle(Opcodes.H_INVOKESTATIC, "A", "myConstant", "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Object;", false), new Object[] {}));
+ methodVisitor.visitInsn(ARETURN);
+ methodVisitor.visitMaxs(1, 0);
+ methodVisitor.visitEnd();
+ }
+ {
+ methodVisitor = classWriter.visitMethod(ACC_PRIVATE | ACC_STATIC, "myConstant", "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Object;", "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class<*>;)Ljava/lang/Object;", null);
+ methodVisitor.visitCode();
+ Label label0 = new Label();
+ methodVisitor.visitLabel(label0);
+ methodVisitor.visitLineNumber(143, label0);
+ methodVisitor.visitTypeInsn(NEW, "java/lang/Object");
+ methodVisitor.visitInsn(DUP);
+ methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
+ methodVisitor.visitInsn(ARETURN);
+ Label label1 = new Label();
+ methodVisitor.visitLabel(label1);
+ methodVisitor.visitLocalVariable("lookup", "Ljava/lang/invoke/MethodHandles$Lookup;", null, label0, label1, 0);
+ methodVisitor.visitLocalVariable("name", "Ljava/lang/String;", null, label0, label1, 1);
+ methodVisitor.visitLocalVariable("type", "Ljava/lang/Class;", "Ljava/lang/Class<*>;", label0, label1, 2);
+ methodVisitor.visitMaxs(2, 3);
+ methodVisitor.visitEnd();
+ }
+ {
+ methodVisitor = classWriter.visitMethod(ACC_PUBLIC | ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
+ methodVisitor.visitCode();
+ Label label0 = new Label();
+ methodVisitor.visitLabel(label0);
+ methodVisitor.visitLineNumber(12, label0);
+ methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
+ methodVisitor.visitMethodInsn(INVOKESTATIC, "A", "getConstant1", "()Ljava/lang/Object;", false);
+ methodVisitor.visitMethodInsn(INVOKESTATIC, "A", "getConstant2", "()Ljava/lang/Object;", false);
+ Label label1 = new Label();
+ methodVisitor.visitJumpInsn(IF_ACMPNE, label1);
+ methodVisitor.visitInsn(ICONST_1);
+ Label label2 = new Label();
+ methodVisitor.visitJumpInsn(GOTO, label2);
+ methodVisitor.visitLabel(label1);
+ methodVisitor.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[] {"java/io/PrintStream"});
+ methodVisitor.visitInsn(ICONST_0);
+ methodVisitor.visitLabel(label2);
+ methodVisitor.visitFrame(Opcodes.F_FULL, 1, new Object[] {"[Ljava/lang/String;"}, 2, new Object[] {"java/io/PrintStream", Opcodes.INTEGER});
+ methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Z)V", false);
+ Label label3 = new Label();
+ methodVisitor.visitLabel(label3);
+ methodVisitor.visitLineNumber(13, label3);
+ methodVisitor.visitInsn(RETURN);
+ methodVisitor.visitMaxs(3, 1);
+ methodVisitor.visitEnd();
+ }
+ classWriter.visitEnd();
+
+ return classWriter.toByteArray();
+ }
+
+ public static void main(String[] args) throws Exception {
+ Files.write(Paths.get("A.class"), DC.dump());
+ }
+ }
+
+ diff --git a/asm/src/main/java/org/objectweb/asm/SymbolTable.java b/asm/src/main/java/org/objectweb/asm/SymbolTable.java
+index a2f26f18..999620c5 100644
+--- a/asm/src/main/java/org/objectweb/asm/SymbolTable.java
++++ b/asm/src/main/java/org/objectweb/asm/SymbolTable.java
+@@ -922,17 +922,6 @@ final class SymbolTable {
+ private Symbol addConstantDynamicOrInvokeDynamicReference(
+ final int tag, final String name, final String descriptor, final int bootstrapMethodIndex) {
+ int hashCode = hash(tag, name, descriptor, bootstrapMethodIndex);
+- Entry entry = get(hashCode);
+- while (entry != null) {
+- if (entry.tag == tag
+- && entry.hashCode == hashCode
+- && entry.data == bootstrapMethodIndex
+- && entry.name.equals(name)
+- && entry.value.equals(descriptor)) {
+- return entry;
+- }
+- entry = entry.next;
+- }
+ constantPool.put122(tag, bootstrapMethodIndex, addConstantNameAndType(name, descriptor));
+ return put(
+ new Entry(
+@@ -1094,24 +1083,6 @@ final class SymbolTable {
+ private Symbol addBootstrapMethod(final int offset, final int length, final int hashCode) {
+ final byte[] bootstrapMethodsData = bootstrapMethods.data;
+- Entry entry = get(hashCode);
+- while (entry != null) {
+- if (entry.tag == Symbol.BOOTSTRAP_METHOD_TAG && entry.hashCode == hashCode) {
+- int otherOffset = (int) entry.data;
+- boolean isSameBootstrapMethod = true;
+- for (int i = 0; i < length; ++i) {
+- if (bootstrapMethodsData[offset + i] != bootstrapMethodsData[otherOffset + i]) {
+- isSameBootstrapMethod = false;
+- break;
+- }
+- }
+- if (isSameBootstrapMethod) {
+- bootstrapMethods.length = offset; // Revert to old position.
+- return entry;
+- }
+- }
+- entry = entry.next;
+- }
+ return put(new Entry(bootstrapMethodCount++, Symbol.BOOTSTRAP_METHOD_TAG, offset, hashCode));
+ }
+
+*/
+
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/constantdynamic/ConstantDynamicRegress272346803Test.java b/src/test/java/com/android/tools/r8/desugar/constantdynamic/ConstantDynamicRegress272346803Test.java
new file mode 100644
index 0000000..bec0148
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/constantdynamic/ConstantDynamicRegress272346803Test.java
@@ -0,0 +1,159 @@
+// 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.constantdynamic;
+
+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.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.cf.CfVersion;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import java.io.IOException;
+import java.lang.invoke.MethodHandles;
+import java.util.Arrays;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ConstantDynamicRegress272346803Test extends TestBase {
+
+ @Parameter() public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
+ }
+
+ private static final Class<?> MAIN_CLASS = Main.class;
+ private static final String EXPECTED_OUTPUT = StringUtils.lines("A", "B");
+
+ @Test
+ public void testReference() throws Exception {
+ parameters.assumeJvmTestParameters();
+ assumeTrue(parameters.getRuntime().asCf().isNewerThanOrEqual(CfVm.JDK11));
+ testForJvm(parameters)
+ .addProgramClasses(Main.class)
+ .addProgramClassFileData(getTransformedClasses())
+ .run(parameters.getRuntime(), MAIN_CLASS)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ }
+
+ @Test
+ public void testDesugaring() throws Exception {
+ testForDesugaring(parameters)
+ .addProgramClasses(Main.class)
+ .addProgramClassFileData(getTransformedClasses())
+ .run(parameters.getRuntime(), MAIN_CLASS)
+ .applyIf(
+ // When not desugaring the CF code requires JDK 11.
+ DesugarTestConfiguration::isNotDesugared,
+ r -> {
+ if (parameters.isCfRuntime()
+ && parameters.getRuntime().asCf().isNewerThanOrEqual(CfVm.JDK11)) {
+ r.assertSuccessWithOutput(EXPECTED_OUTPUT);
+ } else {
+ r.assertFailureWithErrorThatThrows(UnsupportedClassVersionError.class);
+ }
+ })
+ .applyIf(
+ DesugarTestConfiguration::isDesugared, r -> r.assertSuccessWithOutput(EXPECTED_OUTPUT));
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ parameters.assumeR8TestParameters();
+ testForR8(parameters.getBackend())
+ .addProgramClasses(Main.class)
+ .addProgramClassFileData(getTransformedClasses())
+ .setMinApi(parameters)
+ .addKeepMainRule(MAIN_CLASS)
+ // Access modification is required for inlining the get method from the desugared constant
+ // dynamic class.
+ .allowAccessModification()
+ // TODO(b/198142613): There should not be a warnings on class references which are
+ // desugared away.
+ .applyIf(
+ parameters.getApiLevel().isLessThan(AndroidApiLevel.O),
+ b -> b.addDontWarn("java.lang.invoke.MethodHandles$Lookup"))
+ // TODO(b/198142625): Support CONSTANT_Dynamic output for class files.
+ .applyIf(
+ parameters.isCfRuntime(),
+ r -> {
+ assertThrows(
+ CompilationFailedException.class,
+ () ->
+ r.compileWithExpectedDiagnostics(
+ diagnostics -> {
+ diagnostics.assertOnlyErrors();
+ diagnostics.assertErrorsMatch(
+ diagnosticMessage(
+ containsString(
+ "Unsupported dynamic constant (not desugaring)")));
+ }));
+ },
+ r ->
+ r.run(parameters.getRuntime(), MAIN_CLASS)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT));
+ }
+
+ private List<byte[]> getTransformedClasses() throws IOException {
+ return Arrays.asList(
+ transformer(A.class)
+ .setVersion(CfVersion.V11)
+ .transformConstStringToConstantDynamic(
+ "condy1", A.class, "myConstant", false, "constantName", String.class)
+ .transform(),
+ transformer(B.class)
+ .setVersion(CfVersion.V11)
+ .transformConstStringToConstantDynamic(
+ "condy1", B.class, "myConstant", false, "constantName", String.class)
+ .transform());
+ }
+
+ // When R8 optimize this code the getter for the two constant dynamics will be inlined into
+ // Main.main. This leaves the synthetic constant dynamic classes with just two static fields.
+ // The synthetic sharing cannot share these two synthetics as that would leave only one constant.
+ // See b/272346803 for details.
+ public static class Main {
+
+ public static void main(String[] args) {
+ System.out.println(A.getConstant());
+ System.out.println(B.getConstant());
+ }
+ }
+
+ public static class A {
+
+ public static String getConstant() {
+ return "condy1"; // Will be transformed to Constant_DYNAMIC.
+ }
+
+ private static Object myConstant(MethodHandles.Lookup lookup, String name, Class<?> type) {
+ return "A";
+ }
+ }
+
+ public static class B {
+
+ public static String getConstant() {
+ return "condy1"; // Will be transformed to Constant_DYNAMIC.
+ }
+
+ private static Object myConstant(MethodHandles.Lookup lookup, String name, Class<?> type) {
+ return "B";
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/IrInjectionTestBase.java b/src/test/java/com/android/tools/r8/ir/IrInjectionTestBase.java
index d11c68e..0d23c77 100644
--- a/src/test/java/com/android/tools/r8/ir/IrInjectionTestBase.java
+++ b/src/test/java/com/android/tools/r8/ir/IrInjectionTestBase.java
@@ -101,7 +101,7 @@
public String run() throws IOException {
Timing timing = Timing.empty();
- IRConverter converter = new IRConverter(appView, timing);
+ IRConverter converter = new IRConverter(appView);
converter.replaceCodeForTesting(code);
AndroidApp app = writeDex();
return runOnArtRaw(app, DEFAULT_MAIN_CLASS_NAME).stdout;
diff --git a/tools/compiledump.py b/tools/compiledump.py
index c313cb9..39315c9 100755
--- a/tools/compiledump.py
+++ b/tools/compiledump.py
@@ -415,6 +415,8 @@
f.write(line)
def clean_config_line(line, minify, optimize, shrink):
+ if line.lstrip().startswith('#'):
+ return False
if ('-injars' in line or '-libraryjars' in line or
'-print' in line or '-applymapping' in line):
return True